Răsfoiți Sursa

Merge branch 'master' of github.com:darkrenaissance/darkfi

narodnik 4 ani în urmă
părinte
comite
0f1169f620

+ 3 - 6
README.md

@@ -10,19 +10,18 @@
 connect_url = "127.0.0.1:3333"
 publisher_url = "127.0.0.1:4444"
 log_path = "/tmp/gatewayd.log"	
-
 ```
 
 **cashierd.toml**
 
 ```
 accept_url = "127.0.0.1:7777"
-rpc_url = "http://127.0.0.1:8000"
+rpc_url = "127.0.0.1:8000"
 gateway_url = "127.0.0.1:3333"
+gateway_subscriber_url = "127.0.0.1:4444"
 log_path = "/tmp/cashierd.log"
 password = "TEST_PASSWORD"
 client_password = "TEST_PASSWORD"
-
 ```
 
 **darkfid.toml**
@@ -34,15 +33,13 @@ cashier_url = "127.0.0.1:7777"
 rpc_url = "127.0.0.1:8000"
 log_path = "/tmp/darkfid_service_daemon.log"
 password = "TEST_PASSWORD"
-
 ```
 
 **drk.toml**
 
 ```
-rpc_url = "http://127.0.0.1:8000"
+rpc_url = "127.0.0.1:8000"
 log_path = "/tmp/drk_cli.log"
-
 ```
 
 3. Configure the password field on all TOML files.

+ 4 - 0
example/config/cashierd.toml

@@ -1,6 +1,10 @@
 accept_url = "127.0.0.1:7777"
 rpc_url = "127.0.0.1:8000"
 gateway_url = "127.0.0.1:3333"
+gateway_subscriber_url = "127.0.0.1:4444"
 log_path = "/tmp/cashierd.log"
 password = "TEST_PASSWORD"
 client_password = "TEST_PASSWORD"
+
+
+

+ 3 - 3
sql/cashier.sql

@@ -1,12 +1,12 @@
 CREATE TABLE IF NOT EXISTS deposit_keypairs(
     d_key_public INTEGER PRIMARY KEY NOT NULL,
-   	coin_key_private BLOB NOT NULL,
-    coin_key_public BLOB NOT NULL,
+   	token_key_private BLOB NOT NULL,
+    token_key_public BLOB NOT NULL,
 	asset_id BLOB NOT NULL
 );
 
 CREATE TABLE IF NOT EXISTS withdraw_keypairs(
-    coin_key_id BLOB PRIMARY KEY NOT NULL,
+    token_key_id BLOB PRIMARY KEY NOT NULL,
 	d_key_private BLOB NOT NULL,
     d_key_public BLOB NOT NULL,
 	asset_id BLOB NOT NULL,

+ 344 - 90
src/bin/cashierd.rs

@@ -1,134 +1,388 @@
-use async_std::sync::Arc;
-use std::net::SocketAddr;
+use drk::{
+    blockchain::Rocks,
+    cli::{CashierdConfig, Config},
+    client::Client,
+    rpc::{
+        jsonrpc::{error as jsonerr, response as jsonresp},
+        jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
+    },
+    serial::{deserialize, serialize},
+    service::{bridge, bridge::Bridge},
+    util::join_config_path,
+    wallet::{CashierDb, WalletDb},
+    Error, Result,
+};
 
-use std::path::PathBuf;
-
-use blake2b_simd::Params;
-use drk::cli::{CashierdCli, CashierdConfig, Config};
-use drk::serial::{deserialize, serialize};
-use drk::service::CashierService;
-use drk::util::join_config_path;
-use drk::wallet::{CashierDb, WalletDb};
-use drk::{Error, Result};
-use serde::{Deserialize, Serialize};
+use clap::clap_app;
+use log::*;
+use serde::Serialize;
+use serde_json::{json, Value};
+use simplelog::{
+    CombinedLogger, Config as SimLogConfig, ConfigBuilder, LevelFilter, TermLogger, TerminalMode,
+    WriteLogger,
+};
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::net::TcpListener;
 
 use async_executor::Executor;
 use easy_parallel::Parallel;
 
-// TODO: this will be replaced by a vector of assets that can be updated at runtime
-#[derive(Deserialize, Serialize, Debug)]
-pub struct Asset {
-    pub name: String,
-    pub id: Vec<u8>,
+use async_std::sync::{Arc, Mutex};
+use ff::PrimeField;
+use std::path::PathBuf;
+
+#[derive(Debug, Clone, Serialize)]
+struct Features {
+    networks: Vec<String>,
 }
 
-impl Asset {
-    pub fn new(name: String) -> Self {
-        let id = Self::id_hash(&name);
-        Self { name, id }
-    }
-    pub fn id_hash(name: &String) -> Vec<u8> {
-        let mut hasher = Params::new().hash_length(64).to_state();
-        hasher.update(name.as_bytes());
-        let result = hasher.finalize();
-        let hash = jubjub::Fr::from_bytes_wide(result.as_array());
-        let id = serialize(&hash);
-        id
+impl Features {
+    fn new() -> Self {
+        let mut networks = Vec::new();
+        networks.push("solana".to_string());
+        networks.push("bitcoin".to_string());
+        Self { networks }
     }
 }
 
-async fn start(executor: Arc<Executor<'_>>, config: Arc<CashierdConfig>) -> Result<()> {
-    let ex = executor.clone();
-    let accept_addr: SocketAddr = config.accept_url.parse()?;
+#[derive(Clone)]
+struct Cashierd {
+    verbose: bool,
+    config: CashierdConfig,
+    client_wallet: Arc<WalletDb>,
+    cashier_wallet: Arc<CashierDb>,
+    features: Features,
+    client: Arc<Mutex<Client>>,
+}
 
-    let gateway_addr: SocketAddr = config.gateway_url.parse()?;
+impl Cashierd {
+    fn new(verbose: bool, config_path: PathBuf) -> Result<Self> {
+        let mint_params_path = join_config_path(&PathBuf::from("cashier_mint.params"))?;
+        let spend_params_path = join_config_path(&PathBuf::from("cashier_spend.params"))?;
 
-    let database_path = join_config_path(&PathBuf::from("cashier_client_database.db"))?;
+        let config: CashierdConfig = Config::<CashierdConfig>::load(config_path)?;
 
-    let cashierdb = join_config_path(&PathBuf::from("cashier.db"))?;
-    let client_wallet = join_config_path(&PathBuf::from("cashier_client_walletdb.db"))?;
+        let cashier_wallet_path = join_config_path(&PathBuf::from("cashier_wallet.db"))?;
 
-    let wallet = CashierDb::new(
-        &cashierdb.clone(),
-        config.password.clone(),
-    )?;
+        let client_wallet_path = join_config_path(&PathBuf::from("cashier_client_wallet.db"))?;
 
-    let client_wallet = WalletDb::new(
-        &client_wallet.clone(),
-        config.client_password.clone(),
-    )?;
+        let cashier_wallet = CashierDb::new(
+            &cashier_wallet_path,
+            config.password.clone(),
+        )?;
+        let client_wallet = WalletDb::new(
+            &client_wallet_path.clone(),
+            config.password.clone(),
+        )?;
 
-    let mint_params_path = join_config_path(&PathBuf::from("cashier_mint.params"))?;
-    let spend_params_path = join_config_path(&PathBuf::from("cashier_spend.params"))?;
 
-    let mut cashier = CashierService::new(
-        accept_addr,
-        wallet.clone(),
-        client_wallet.clone(),
-        database_path,
-        (gateway_addr, "127.0.0.1:4444".parse()?),
-        (mint_params_path, spend_params_path),
-    )
-    .await?;
+        let database_path = join_config_path(&PathBuf::from("cashier_database.db"))?;
 
-    // TODO: make this a vector of accepted assets
-    let asset = Asset::new("btc".to_string());
-    // TODO: this should be done by the user
-    let asset_id = deserialize(&asset.id)?;
+        let rocks = Rocks::new(&database_path)?;
 
-    // TODO: pass vector of assets into cashier.start()
-    cashier.start(ex.clone(), asset_id).await?;
+        let client = Client::new(
+            rocks,
+            (
+                config.gateway_url.parse()?,
+                config.gateway_subscriber_url.parse()?,
+            ),
+            (mint_params_path, spend_params_path),
+            client_wallet.clone(),
+        )?;
 
-    Ok(())
-}
+        let client = Arc::new(Mutex::new(client));
 
-fn main() -> Result<()> {
-    let ex = Arc::new(Executor::new());
-    let (signal, shutdown) = async_channel::unbounded::<()>();
+        let features = Features::new();
+
+        Ok(Self {
+            verbose,
+            config: config.clone(),
+            cashier_wallet,
+            client_wallet,
+            features,
+            client: client.clone(),
+        })
+    }
+
+    async fn start(&self, executor: Arc<Executor<'_>>) -> Result<()> {
+        //// TODO: pass vector of assets
 
-    let path = join_config_path(&PathBuf::from("cashierd.toml")).unwrap();
+        self.cashier_wallet.init_db()?;
 
-    let config: CashierdConfig = Config::<CashierdConfig>::load(path)?;
+        let bridge = Bridge::new();
 
-    let config = Arc::new(config);
+        self.client.lock().await.start().await?;
 
-    let options = CashierdCli::load()?;
+        let (notify, recv_coin) = async_channel::unbounded::<(jubjub::SubgroupPoint, u64)>();
 
-    {
-        use simplelog::*;
-        let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+        let cashier_client_subscriber_task =
+            executor.spawn(Client::connect_to_subscriber_from_cashier(
+                self.client.clone(),
+                executor.clone(),
+                self.cashier_wallet.clone(),
+                notify.clone(),
+            ));
 
-        let debug_level = if options.verbose {
-            LevelFilter::Debug
-        } else {
-            LevelFilter::Off
+        let cashier_wallet = self.cashier_wallet.clone();
+
+        let ex = executor.clone();
+        let listen_for_receiving_coins_task = executor.spawn(async move {
+            loop {
+                Self::listen_for_receiving_coins(
+                    ex.clone(),
+                    bridge.clone(),
+                    cashier_wallet.clone(),
+                    recv_coin.clone(),
+                )
+                .await
+                .expect(" listen for receiving coins");
+            }
+        });
+
+        let rpc_url = self.config.rpc_url.clone();
+        run_rpc_server(self.clone(), rpc_url).await?;
+
+        listen_for_receiving_coins_task.cancel().await;
+        cashier_client_subscriber_task.cancel().await;
+        Ok(())
+    }
+
+    async fn listen_for_receiving_coins(
+        ex: Arc<Executor<'_>>,
+        bridge: Arc<Bridge>,
+        cashier_wallet: Arc<CashierDb>,
+        recv_coin: async_channel::Receiver<(jubjub::SubgroupPoint, u64)>,
+    ) -> Result<()> {
+        let bridge_subscribtion = bridge.subscribe(ex.clone()).await;
+
+        // received drk coin
+        let (drk_pub_key, amount) = recv_coin.recv().await?;
+
+        debug!(target: "CASHIER DAEMON", "Receive coin with following address and amount: {}, {}"
+            , drk_pub_key, amount);
+
+        // get public key, and asset_id of the token
+        let token = cashier_wallet.get_withdraw_token_public_key_by_dkey_public(&drk_pub_key)?;
+
+        // send a request to bridge to send equivalent amount of
+        // received drk coin to token publickey
+        if let Some((addr, asset_id)) = token {
+            bridge_subscribtion
+                .sender
+                .send(bridge::BridgeRequests {
+                    asset_id,
+                    payload: bridge::BridgeRequestsPayload::SendRequest(addr.clone(), amount),
+                })
+                .await?;
+
+            // receive a response
+            let res = bridge_subscribtion.receiver.recv().await?;
+
+            if res.error == 0 {
+                match res.payload {
+                    bridge::BridgeResponsePayload::SendResponse => {
+                        // TODO Send the received coins to the main address
+                        cashier_wallet.confirm_withdraw_key_record(&addr, &serialize(&1))?;
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    async fn handle_request(self, req: JsonRequest) -> 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());
+
+        // TODO: "features"
+        match req.method.as_str() {
+            Some("deposit") => return self.deposit(req.id, req.params).await,
+            Some("withdraw") => return self.withdraw(req.id, req.params).await,
+            Some("features") => return self.features(req.id, req.params).await,
+            Some(_) => {}
+            None => {}
         };
 
-        let log_path = config.log_path.clone();
-        CombinedLogger::init(vec![
-            TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
-            WriteLogger::new(
-                LevelFilter::Debug,
-                Config::default(),
-                std::fs::File::create(log_path).unwrap(),
-            ),
-        ])
-        .unwrap();
+        return JsonResult::Err(jsonerr(MethodNotFound, None, req.id));
+    }
+
+    async fn deposit(self, id: Value, params: Value) -> JsonResult {
+        debug!(target: "CASHIER", "RECEIVED DEPOSIT REQUEST");
+
+        if params.as_array().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        }
+
+        let args = params.as_array().unwrap();
+
+        let _ntwk = &args[0];
+        let tkn = &args[1];
+        let pk = &args[2];
+
+        debug!(target: "CASHIER", "PROCESSING INPUT");
+
+        if tkn.as_str().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        }
+        let tkn_str = tkn.as_str().unwrap();
+
+        let _tkn_fr = jubjub::Fr::from_str(tkn_str);
+        // TODO: debug this
+        //if tkn_fr.is_none() {
+        //    return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        //};
+        //let token = tkn_fr.unwrap();
+
+        if pk.as_str().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        }
+        let pk_str = pk.as_str().unwrap();
+
+        let pk_58 = bs58::decode(pk_str).into_vec().unwrap();
+
+        let pubkey: jubjub::SubgroupPoint = deserialize(&pk_58).unwrap();
+
+        //// TODO: Sanity check.
+        let _check = self
+            .cashier_wallet
+            .get_deposit_token_keys_by_dkey_public(&pubkey, &serialize(&1));
+
+        // TODO: implement bridge communication
+        // this just returns the user public key
+        let pubkey = bs58::encode(serialize(&pubkey)).into_string();
+        debug!(target: "CASHIER", "ATTEMPING REPLY");
+        JsonResult::Resp(jsonresp(json!(pubkey), json!(id)))
+    }
+
+    async fn withdraw(self, id: Value, params: Value) -> JsonResult {
+        debug!(target: "CASHIER", "RECEIVED DEPOSIT REQUEST");
+
+        let args = params.as_array().unwrap();
+
+        let _network = &args[0];
+        let _token = &args[1];
+        let _address = &args[2];
+        let _amount = &args[3];
+
+        // 2. Cashier checks if they support the network, and if so,
+        //    return adeposit address.
+
+        JsonResult::Err(jsonerr(InvalidParams, None, id))
+    }
+
+    // TODO: implement this
+    async fn features(self, id: Value, _params: Value) -> JsonResult {
+        JsonResult::Resp(jsonresp(json!(self.features), id))
     }
+}
+
+async fn run_rpc_server(cashierd: Cashierd, rpc_url: String) -> Result<()> {
+    let listener = TcpListener::bind(rpc_url.clone()).await?;
+    debug!(target: "RPC SERVER", "Listening on {}", rpc_url);
+    loop {
+        debug!(target: "RPC SERVER", "waiting for client");
+
+        let (mut socket, _) = listener.accept().await?;
+
+        debug!(target: "RPC SERVER", "accepted client");
+
+        let cashierd = cashierd.clone();
+        tokio::spawn(async move {
+            let mut buf = [0; 2048];
 
+            loop {
+                let n = match socket.read(&mut buf).await {
+                    Ok(n) if n == 0 => {
+                        debug!(target: "RPC SERVER", "closed connection");
+                        return;
+                    }
+                    Ok(n) => n,
+                    Err(e) => {
+                        debug!(target: "RPC SERVER", "failed to read from socket; err = {:?}", e);
+                        return;
+                    }
+                };
+
+                let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
+                    Ok(r) => r,
+                    Err(e) => {
+                        debug!(target: "RPC SERVER", "received invalid json; err = {:?}", e);
+                        return;
+                    }
+                };
+
+                let reply = cashierd.clone().handle_request(r).await;
+                let j = serde_json::to_string(&reply).unwrap();
+
+                debug!(target: "RPC", "<-- {:#?}", j);
+
+                // Write the data back
+                if let Err(e) = socket.write_all(j.as_bytes()).await {
+                    debug!(target: "RPC SERVER", "failed to write to socket; err = {:?}", e);
+                    return;
+                }
+            }
+        });
+    }
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    let args = clap_app!(cashierd =>
+        (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
+        (@arg verbose: -v --verbose "Increase verbosity")
+    )
+    .get_matches();
+
+    let config_path: PathBuf;
+
+    if args.is_present("CONFIG") {
+        config_path = PathBuf::from(args.value_of("CONFIG").unwrap());
+    } else {
+        config_path = join_config_path(&PathBuf::from("cashierd.toml"))?;
+    }
+
+    let cashierd = Cashierd::new(args.clone().is_present("verbose"), config_path)?;
+
+    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+    let debug_level = if args.is_present("verbose") {
+        LevelFilter::Debug
+    } else {
+        LevelFilter::Off
+    };
+
+    let log_path = cashierd.clone().config.log_path;
+    CombinedLogger::init(vec![
+        TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
+        WriteLogger::new(
+            LevelFilter::Debug,
+            SimLogConfig::default(),
+            std::fs::File::create(log_path).unwrap(),
+        ),
+    ])
+    .unwrap();
+
+    let ex = Arc::new(Executor::new());
     let ex2 = ex.clone();
+    let (signal, shutdown) = async_channel::unbounded::<()>();
 
-    let (_, result) = Parallel::new()
+    let cashierd2 = cashierd.clone();
+    let (_, _result) = Parallel::new()
         // Run four executor threads.
         .each(0..3, |_| 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).await?;
+                cashierd2.start(ex2).await?;
                 drop(signal);
                 Ok::<(), Error>(())
             })
         });
 
-    result
+    Ok(())
 }

+ 0 - 305
src/bin/cashierd2.rs

@@ -1,305 +0,0 @@
-use async_std::sync::Arc;
-use log::*;
-use std::path::PathBuf;
-
-use clap::clap_app;
-use serde::Serialize;
-use serde_json::{json, Value};
-use simplelog::{
-    CombinedLogger, Config as SimLogConfig, ConfigBuilder, LevelFilter, TermLogger, TerminalMode,
-    WriteLogger,
-};
-use std::net::SocketAddr;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-use tokio::net::TcpListener;
-
-use async_executor::Executor;
-use easy_parallel::Parallel;
-
-use drk::{
-    cli::{CashierdConfig, Config},
-    rpc::{
-        jsonrpc::{error as jsonerr, response as jsonresp},
-        jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
-    },
-    serial::{deserialize, serialize},
-    service::{bridge, CashierService},
-    util::join_config_path,
-    wallet::{CashierDb, WalletDb},
-    Error, Result,
-};
-
-use ff::PrimeField;
-
-#[derive(Debug, Clone, Serialize)]
-struct Features {
-    networks: Vec<String>,
-}
-
-impl Features {
-    fn new() -> Self {
-        let mut networks = Vec::new();
-        networks.push("solana".to_string());
-        networks.push("bitcoin".to_string());
-        Self { networks }
-    }
-}
-
-#[derive(Clone)]
-struct Cashierd {
-    verbose: bool,
-    config: CashierdConfig,
-    client_wallet: Arc<WalletDb>,
-    cashier_wallet: Arc<CashierDb>,
-    features: Features,
-    // clientdb:
-    // mint_params:
-    // spend_params:
-}
-
-impl Cashierd {
-    fn new(verbose: bool, config_path: PathBuf) -> Result<Self> {
-        let config: CashierdConfig = Config::<CashierdConfig>::load(config_path)?;
-        let cashier_wallet = CashierDb::new(
-            &PathBuf::from(config.cashierdb_path.clone()),
-            config.password.clone(),
-        )?;
-        let client_wallet = WalletDb::new(
-            &PathBuf::from(config.cashierdb_path.clone()),
-            config.password.clone(),
-        )?;
-        let features = Features::new();
-
-        Ok(Self {
-            verbose,
-            config,
-            cashier_wallet,
-            client_wallet,
-            features,
-        })
-    }
-
-    async fn start(self, executor: Arc<Executor<'_>>, config: CashierdConfig) -> Result<()> {
-        let ex = executor.clone();
-        let accept_addr: SocketAddr = config.accept_url.parse()?;
-
-        let gateway_addr: SocketAddr = config.gateway_url.parse()?;
-
-        let database_path = PathBuf::from(config.cashierdb_path);
-
-        let mint_params_path = join_config_path(&PathBuf::from("cashier_mint.params"))?;
-        let spend_params_path = join_config_path(&PathBuf::from("cashier_spend.params"))?;
-
-        let mut cashier = CashierService::new(
-            accept_addr,
-            self.cashier_wallet.clone(),
-            self.client_wallet.clone(),
-            database_path,
-            (gateway_addr, "127.0.0.1:4444".parse()?),
-            (mint_params_path, spend_params_path),
-        )
-        .await?;
-
-        //// TODO: make this a vector of accepted assets
-        //let asset = Asset::new("btc".to_string());
-        //// TODO: this should be done by the user
-        //let asset_id = deserialize(&asset.id)?;
-
-        //// TODO: pass vector of assets into cashier.start()
-        //cashier.start(ex.clone(), asset_id).await?;
-
-        Ok(())
-    }
-
-    async fn handle_request(self, req: JsonRequest) -> 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());
-
-        // TODO: "features"
-        match req.method.as_str() {
-            Some("deposit") => return self.deposit(req.id, req.params).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));
-    }
-
-    async fn deposit(self, id: Value, params: Value) -> JsonResult {
-        debug!(target: "CASHIER", "RECEIVED DEPOSIT REQUEST");
-
-        if params.as_array().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        }
-
-        let args = params.as_array().unwrap();
-
-        let _ntwk = &args[0];
-        let tkn = &args[1];
-        let pk = &args[2];
-
-        debug!(target: "CASHIER", "PROCESSING INPUT");
-
-        if tkn.as_str().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        }
-        let tkn_str = tkn.as_str().unwrap();
-
-        let _tkn_fr = jubjub::Fr::from_str(tkn_str);
-        // TODO: debug this
-        //if tkn_fr.is_none() {
-        //    return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        //};
-        //let token = tkn_fr.unwrap();
-
-        if pk.as_str().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        }
-        let pk_str = pk.as_str().unwrap();
-
-        let pk_58 = bs58::decode(pk_str).into_vec().unwrap();
-
-        let pubkey: jubjub::SubgroupPoint = deserialize(&pk_58).unwrap();
-
-        //// TODO: Sanity check.
-        let _check = self
-            .cashier_wallet
-            .get_deposit_coin_keys_by_dkey_public(&pubkey, &serialize(&1));
-
-        // TODO: implement bridge communication
-        // this just returns the user public key
-        let pubkey = bs58::encode(serialize(&pubkey)).into_string();
-        debug!(target: "CASHIER", "ATTEMPING REPLY");
-        JsonResult::Resp(jsonresp(json!(pubkey), json!(id)))
-    }
-
-    async fn withdraw(self, id: Value, params: Value) -> JsonResult {
-        debug!(target: "CASHIER", "RECEIVED DEPOSIT REQUEST");
-
-        let args = params.as_array().unwrap();
-
-        let network = &args[0];
-        let token = &args[1];
-        let address = &args[2];
-        let amount = &args[3];
-
-        // 2. Cashier checks if they support the network, and if so,
-        //    return adeposit address.
-
-        JsonResult::Err(jsonerr(InvalidParams, None, id))
-    }
-
-    // TODO: implement this
-    async fn features(self, id: Value, _params: Value) -> JsonResult {
-        JsonResult::Resp(jsonresp(json!(self.features), id))
-    }
-}
-
-#[tokio::main]
-async fn main() -> Result<()> {
-    let args = clap_app!(cashierd =>
-        (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
-        (@arg verbose: -v --verbose "Increase verbosity")
-    )
-    .get_matches();
-
-    let config_path: PathBuf;
-
-    if args.is_present("CONFIG") {
-        config_path = PathBuf::from(args.value_of("CONFIG").unwrap());
-    } else {
-        config_path = join_config_path(&PathBuf::from("cashierd.toml"))?;
-    }
-
-    let cashierd = Cashierd::new(args.clone().is_present("verbose"), config_path)?;
-
-    let listener = TcpListener::bind(cashierd.clone().config.rpc_url).await?;
-    debug!(target: "RPC SERVER", "Listening on {}", cashierd.clone().config.rpc_url);
-
-    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
-    let debug_level = if args.is_present("verbose") {
-        LevelFilter::Debug
-    } else {
-        LevelFilter::Off
-    };
-
-    let log_path = cashierd.clone().config.log_path;
-    CombinedLogger::init(vec![
-        TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
-        WriteLogger::new(
-            LevelFilter::Debug,
-            SimLogConfig::default(),
-            std::fs::File::create(log_path).unwrap(),
-        ),
-    ])
-    .unwrap();
-
-    let ex = Arc::new(Executor::new());
-    let ex2 = ex.clone();
-    let (signal, shutdown) = async_channel::unbounded::<()>();
-
-    let cashierd2 = cashierd.clone();
-    let cashierd3 = cashierd.clone();
-    let (_, _result) = Parallel::new()
-        // Run four executor threads.
-        .each(0..3, |_| smol::future::block_on(ex.run(shutdown.recv())))
-        // Run the main future on the current thread.
-        .finish(|| {
-            smol::future::block_on(async move {
-                cashierd2.start(ex2, cashierd3.clone().config).await?;
-                drop(signal);
-                Ok::<(), Error>(())
-            })
-        });
-
-    loop {
-        debug!(target: "RPC SERVER", "waiting for client");
-
-        let (mut socket, _) = listener.accept().await?;
-
-        debug!(target: "RPC SERVER", "accepted client");
-
-        let cashierd = cashierd.clone();
-        tokio::spawn(async move {
-            let mut buf = [0; 2048];
-
-            loop {
-                let n = match socket.read(&mut buf).await {
-                    Ok(n) if n == 0 => {
-                        debug!(target: "RPC SERVER", "closed connection");
-                        return;
-                    }
-                    Ok(n) => n,
-                    Err(e) => {
-                        debug!(target: "RPC SERVER", "failed to read from socket; err = {:?}", e);
-                        return;
-                    }
-                };
-
-                let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
-                    Ok(r) => r,
-                    Err(e) => {
-                        debug!(target: "RPC SERVER", "received invalid json; err = {:?}", e);
-                        return;
-                    }
-                };
-
-                let reply = cashierd.clone().handle_request(r).await;
-                let j = serde_json::to_string(&reply).unwrap();
-
-                debug!(target: "RPC", "<-- {:#?}", j);
-
-                // Write the data back
-                if let Err(e) = socket.write_all(j.as_bytes()).await {
-                    debug!(target: "RPC SERVER", "failed to write to socket; err = {:?}", e);
-                    return;
-                }
-            }
-        });
-    }
-}

+ 134 - 0
src/bin/cashierd_old

@@ -0,0 +1,134 @@
+use async_std::sync::Arc;
+use std::net::SocketAddr;
+
+use std::path::PathBuf;
+
+use blake2b_simd::Params;
+use drk::cli::{CashierdCli, CashierdConfig, Config};
+use drk::serial::{deserialize, serialize};
+use drk::service::CashierService;
+use drk::util::join_config_path;
+use drk::wallet::{CashierDb, WalletDb};
+use drk::{Error, Result};
+use serde::{Deserialize, Serialize};
+
+use async_executor::Executor;
+use easy_parallel::Parallel;
+
+// TODO: this will be replaced by a vector of assets that can be updated at runtime
+#[derive(Deserialize, Serialize, Debug)]
+pub struct Asset {
+    pub name: String,
+    pub id: Vec<u8>,
+}
+
+impl Asset {
+    pub fn new(name: String) -> Self {
+        let id = Self::id_hash(&name);
+        Self { name, id }
+    }
+    pub fn id_hash(name: &String) -> Vec<u8> {
+        let mut hasher = Params::new().hash_length(64).to_state();
+        hasher.update(name.as_bytes());
+        let result = hasher.finalize();
+        let hash = jubjub::Fr::from_bytes_wide(result.as_array());
+        let id = serialize(&hash);
+        id
+    }
+}
+
+async fn start(executor: Arc<Executor<'_>>, config: Arc<CashierdConfig>) -> Result<()> {
+    let ex = executor.clone();
+    let accept_addr: SocketAddr = config.accept_url.parse()?;
+
+    let gateway_addr: SocketAddr = config.gateway_url.parse()?;
+
+    let database_path = join_config_path(&PathBuf::from("cashier_client_database.db"))?;
+
+    let cashierdb = join_config_path(&PathBuf::from("cashier.db"))?;
+    let client_wallet = join_config_path(&PathBuf::from("cashier_client_walletdb.db"))?;
+
+    let wallet = CashierDb::new(
+        &cashierdb.clone(),
+        config.password.clone(),
+    )?;
+
+    let client_wallet = WalletDb::new(
+        &client_wallet.clone(),
+        config.client_password.clone(),
+    )?;
+
+    let mint_params_path = join_config_path(&PathBuf::from("cashier_mint.params"))?;
+    let spend_params_path = join_config_path(&PathBuf::from("cashier_spend.params"))?;
+
+    let mut cashier = CashierService::new(
+        accept_addr,
+        wallet.clone(),
+        client_wallet.clone(),
+        database_path,
+        (gateway_addr, "127.0.0.1:4444".parse()?),
+        (mint_params_path, spend_params_path),
+    )
+    .await?;
+
+    // TODO: make this a vector of accepted assets
+    let asset = Asset::new("btc".to_string());
+    // TODO: this should be done by the user
+    let asset_id = deserialize(&asset.id)?;
+
+    // TODO: pass vector of assets into cashier.start()
+    cashier.start(ex.clone(), asset_id).await?;
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let ex = Arc::new(Executor::new());
+    let (signal, shutdown) = async_channel::unbounded::<()>();
+
+    let path = join_config_path(&PathBuf::from("cashierd.toml")).unwrap();
+
+    let config: CashierdConfig = Config::<CashierdConfig>::load(path)?;
+
+    let config = Arc::new(config);
+
+    let options = CashierdCli::load()?;
+
+    {
+        use simplelog::*;
+        let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+
+        let debug_level = if options.verbose {
+            LevelFilter::Debug
+        } else {
+            LevelFilter::Off
+        };
+
+        let log_path = config.log_path.clone();
+        CombinedLogger::init(vec![
+            TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
+            WriteLogger::new(
+                LevelFilter::Debug,
+                Config::default(),
+                std::fs::File::create(log_path).unwrap(),
+            ),
+        ])
+        .unwrap();
+    }
+
+    let ex2 = ex.clone();
+
+    let (_, result) = Parallel::new()
+        // Run four executor threads.
+        .each(0..3, |_| 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).await?;
+                drop(signal);
+                Ok::<(), Error>(())
+            })
+        });
+
+    result
+}

+ 314 - 85
src/bin/darkfid.rs

@@ -1,115 +1,344 @@
-use drk::blockchain::Rocks;
-use drk::cli::{Config, DarkfidCli, DarkfidConfig};
-use drk::util::join_config_path;
-use drk::wallet::WalletDb;
-use drk::Result;
-
-use drk::client::Client;
+use log::*;
+use std::fs;
+use std::path::PathBuf;
 
-use async_executor::Executor;
-use easy_parallel::Parallel;
+use clap::clap_app;
+use serde_json::{json, Value};
+use simplelog::{
+    CombinedLogger, Config as SimLogConfig, ConfigBuilder, LevelFilter, TermLogger, TerminalMode,
+    WriteLogger,
+};
 
 use async_std::sync::Arc;
-use std::net::SocketAddr;
-use std::path::PathBuf;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::net::TcpListener;
+
+use drk::{
+    cli::{Config, DarkfidConfig},
+    rpc::{
+        jsonrpc::{error as jsonerr, request as jsonreq, response as jsonresp, send_request},
+        jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
+    },
+    serial::serialize,
+    util::join_config_path,
+    wallet::WalletDb,
+    Error,
+};
 
-async fn start(executor: Arc<Executor<'_>>, config: Arc<DarkfidConfig>) -> Result<()> {
-    let connect_addr: SocketAddr = config.connect_url.parse()?;
-    let sub_addr: SocketAddr = config.subscriber_url.parse()?;
-    let cashier_addr: SocketAddr = config.cashier_url.parse()?;
-    let rpc_url: std::net::SocketAddr = config.rpc_url.parse()?;
+#[derive(Clone)]
+struct Darkfid {
+    verbose: bool,
+    config: DarkfidConfig,
+    wallet: Arc<WalletDb>,
+    // clientdb:
+    // mint_params:
+    // spend_params:
+}
 
-    let database_path = join_config_path(&PathBuf::from("database_client.db"))?;
-    let walletdb_path = join_config_path(&PathBuf::from("walletdb.db"))?;
+impl Darkfid {
+    fn new(verbose: bool, config_path: PathBuf) -> Result<Self, Error> {
+        let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
+        let wallet = WalletDb::new(
+            &PathBuf::from(config.walletdb_path.clone()),
+            config.password.clone(),
+        )?;
 
-    let rocks = Rocks::new(&database_path)?;
+        Ok(Self {
+            verbose,
+            config,
+            wallet,
+        })
+    }
 
-    let wallet = WalletDb::new(&walletdb_path, config.password.clone())?;
+    // TODO: ServerError codes should be part of the lib.
+    async fn handle_request(self, req: JsonRequest) -> JsonResult {
+        if req.params.as_array().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, req.id));
+        }
 
-    let mint_params_path = join_config_path(&PathBuf::from("mint.params"))?;
-    let spend_params_path = join_config_path(&PathBuf::from("spend.params"))?;
+        debug!(target: "RPC", "--> {:#?}", serde_json::to_string(&req).unwrap());
 
-    if let Err(_) = wallet.get_keypairs() {
-        wallet.init_db()?;
-        wallet.key_gen()?;
+        match req.method.as_str() {
+            Some("say_hello") => return self.say_hello(req.id, req.params).await,
+            Some("create_wallet") => return self.create_wallet(req.id, req.params).await,
+            Some("key_gen") => return self.key_gen(req.id, req.params).await,
+            Some("get_key") => return self.get_key(req.id, req.params).await,
+            Some("get_token_id") => return self.get_token_id(req.id, req.params).await,
+            Some("features") => return self.features(req.id, req.params).await,
+            Some("deposit") => return self.deposit(req.id, req.params).await,
+            Some("withdraw") => return self.withdraw(req.id, req.params).await,
+            Some("transfer") => return self.transfer(req.id, req.params).await,
+            Some(_) => {}
+            None => {}
+        };
+
+        return JsonResult::Err(jsonerr(MethodNotFound, None, req.id));
     }
 
-    let mut client = Client::new(
-        rocks,
-        (connect_addr, sub_addr),
-        (mint_params_path, spend_params_path),
-        wallet.clone(),
-    )?;
+    // --> {"method": "say_hello", "params": []}
+    // <-- {"result": "hello world"}
+    async fn say_hello(self, id: Value, _params: Value) -> JsonResult {
+        JsonResult::Resp(jsonresp(json!("hello world"), id))
+    }
 
-    client.start().await?;
+    // --> {"method": "create_wallet", "params": []}
+    // <-- {"result": true}
+    async fn create_wallet(self, id: Value, _params: Value) -> JsonResult {
+        match self.wallet.init_db() {
+            Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32001), Some(e.to_string()), id))
+            }
+        }
+    }
 
-    Client::connect_to_cashier(
-        client,
-        executor.clone(),
-        cashier_addr.clone(),
-        rpc_url.clone(),
-    )
-    .await?;
+    // --> {"method": "key_gen", "params": []}
+    // <-- {"result": true}
+    async fn key_gen(self, id: Value, _params: Value) -> JsonResult {
+        match self.wallet.key_gen() {
+            Ok((_, _)) => return JsonResult::Resp(jsonresp(json!(true), id)),
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32002), Some(e.to_string()), id))
+            }
+        }
+    }
 
-    Ok(())
-}
+    // --> {"method": "get_key", "params": []}
+    // <-- {"result": "vdNS7oBj7KvsMWWmo9r96SV4SqATLrGsH2a3PGpCfJC"}
+    async fn get_key(self, id: Value, _params: Value) -> JsonResult {
+        match self.wallet.get_keypairs() {
+            Ok(v) => {
+                let pk = v[0].public;
+                let b58 = bs58::encode(serialize(&pk)).into_string();
+                return JsonResult::Resp(jsonresp(json!(b58), id));
+            }
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
+            }
+        }
+    }
 
-fn main() -> Result<()> {
-    let options = Arc::new(DarkfidCli::load()?);
+    // --> {"jsonrpc": "2.0", "method": "get_token_id",
+    //      "params": [token],
+    //      "id": 42}
+    // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
+    async fn get_token_id(self, id: Value, params: Value) -> JsonResult {
+        let args = params.as_array().unwrap();
+        let symbol = &args[0];
 
-    let config_path: PathBuf;
+        if symbol.as_str().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        };
 
-    match options.config.as_ref() {
-        Some(path) => {
-            config_path = path.to_owned();
-        }
-        None => {
-            config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
+        let symbol = symbol.as_str().unwrap().to_uppercase();
+
+        let file_contents =
+            fs::read_to_string("token/solanatokenlist.json").expect("Can't find tokenlist file");
+        let root: Value = serde_json::from_str(&file_contents).unwrap();
+        let tokens = root["tokens"].as_array().unwrap();
+
+        for item in tokens {
+            if item["symbol"] == symbol {
+                let address = &item["address"];
+                return JsonResult::Resp(jsonresp(json!(address), id));
+            }
         }
+        return JsonResult::Err(jsonerr(InvalidParams, None, id));
     }
 
-    let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
+    // --> {"jsonrpc": "2.0", "method": "features", "params": [], "id": 42}
+    // <-- {"jsonrpc": "2.0", "result": ["network": "btc", "sol"], "id": 42}
+    async fn features(self, id: Value, _params: Value) -> JsonResult {
+        // TODO: return a dictionary of features
+        let req = jsonreq(json!("features"), json!([]));
+        let rep: JsonResult;
+        match send_request(self.config.cashier_url, json!(req)).await {
+            Ok(v) => rep = v,
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
+            }
+        }
 
-    let config = Arc::new(config);
+        match rep {
+            JsonResult::Resp(r) => return JsonResult::Resp(r),
+            JsonResult::Err(e) => return JsonResult::Err(e),
+            JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
+        }
+    }
 
-    let ex = Arc::new(Executor::new());
-    let (signal, shutdown) = async_channel::unbounded::<()>();
+    // --> {"jsonrpc": "2.0", "method": "deposit",
+    //      "params": [network, token, publickey],
+    //      "id": 42}
+    // The publickey sent here is used so the cashier can know where to send
+    // assets once the deposit is received.
+    // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
+    async fn deposit(self, id: Value, params: Value) -> JsonResult {
+        let args = params.as_array().unwrap();
+        if args.len() != 2 {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        }
 
-    {
-        use simplelog::*;
-        let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+        let network = &args[0];
+        let token = &args[1];
 
-        let debug_level = if options.verbose {
-            LevelFilter::Debug
-        } else {
-            LevelFilter::Off
+        if token.as_str().is_none() {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
         };
 
-        let log_path = config.log_path.clone();
-        CombinedLogger::init(vec![
-            TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
-            WriteLogger::new(
-                LevelFilter::Debug,
-                Config::default(),
-                std::fs::File::create(log_path).unwrap(),
-            ),
-        ])
-        .unwrap();
+        // TODO: Optional sanity checking here, but cashier *must* do so too.
+
+        let pubkey: String;
+        match self.wallet.get_keypairs() {
+            Ok(v) => {
+                let pk = v[0].public;
+                pubkey = bs58::encode(serialize(&pk)).into_string();
+            }
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
+            }
+        }
+
+        // Send request to cashier. If the cashier supports the requested network
+        // (and token), it shall return a valid address where assets can be deposited.
+        // If not, an error is returned, and forwarded to the method caller.
+        let req = jsonreq(json!("deposit"), json!([network, token, pubkey]));
+        let rep: JsonResult;
+        match send_request(self.config.cashier_url, json!(req)).await {
+            Ok(v) => rep = v,
+            Err(e) => {
+                return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
+            }
+        }
+
+        match rep {
+            JsonResult::Resp(r) => return JsonResult::Resp(r),
+            JsonResult::Err(e) => return JsonResult::Err(e),
+            JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
+        }
     }
 
-    let ex2 = ex.clone();
-
-    let (_, result) = Parallel::new()
-        // Run four executor threads.
-        .each(0..3, |_| 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).await?;
-                drop(signal);
-                Ok::<(), drk::Error>(())
-            })
-        });
+    // --> {"method": "withdraw", "params": [network, token, publickey, amount]}
+    // The publickey sent here is the address where the caller wants to receive
+    // the tokens they plan to withdraw.
+    // On request, send request to cashier to get deposit address, and then transfer
+    // dark assets to the cashier's wallet. Following that, the cashier should return
+    // a transaction ID of them sending the funds that are requested for withdrawal.
+    // <-- {"result": "txID"}
+    async fn withdraw(self, id: Value, params: Value) -> JsonResult {
+        let args = params.as_array().unwrap();
+        if args.len() != 4 {
+            return JsonResult::Err(jsonerr(InvalidParams, None, id));
+        }
+
+        let _network = &args[0];
+        let _token = &args[1];
+        let _address = &args[2];
+        let _amount = &args[3];
+
+        // 1. Send request to cashier.
+        // 2. Cashier checks if they support the network, and if so,
+        //    return adeposit address.
+        // 3. We issue a transfer of $amount to the given address.
+
+        return JsonResult::Err(jsonerr(
+            ServerError(-32005),
+            Some("failed to withdraw".to_string()),
+            id,
+        ));
+    }
+
+    // --> {"method": "transfer", [dToken, address, amount]}
+    // <-- {"result": "txID"}
+    async fn transfer(self, id: Value, _params: Value) -> JsonResult {
+        return JsonResult::Err(jsonerr(
+            ServerError(-32006),
+            Some("failed to transfer".to_string()),
+            id,
+        ));
+    }
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let args = clap_app!(darkfid =>
+        (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
+        (@arg verbose: -v --verbose "Increase verbosity")
+    )
+    .get_matches();
+
+    let config_path: PathBuf;
+    if args.is_present("CONFIG") {
+        config_path = PathBuf::from(args.value_of("CONFIG").unwrap());
+    } else {
+        config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
+    }
+
+    let darkfid = Darkfid::new(args.clone().is_present("verbose"), config_path)?;
+    // TODO: TLS
+    let listener = TcpListener::bind(darkfid.clone().config.rpc_url).await?;
+    debug!(target: "RPC SERVER", "Listening on {}", darkfid.clone().config.rpc_url);
+
+    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+    let debug_level = if args.is_present("verbose") {
+        LevelFilter::Debug
+    } else {
+        LevelFilter::Off
+    };
+
+    let log_path = darkfid.clone().config.log_path;
+    CombinedLogger::init(vec![
+        TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
+        WriteLogger::new(
+            LevelFilter::Debug,
+            SimLogConfig::default(),
+            std::fs::File::create(log_path).unwrap(),
+        ),
+    ])
+    .unwrap();
+
+    loop {
+        debug!(target: "RPC SERVER", "waiting for client");
+
+        let (mut socket, _) = listener.accept().await?;
+        let darkfid = darkfid.clone();
+
+        debug!(target: "RPC SERVER", "accepted client");
 
-    result
+        tokio::spawn(async move {
+            let mut buf = [0; 2048];
+
+            loop {
+                let n = match socket.read(&mut buf).await {
+                    Ok(n) if n == 0 => {
+                        debug!(target: "RPC SERVER", "closed connection");
+                        return;
+                    }
+                    Ok(n) => n,
+                    Err(e) => {
+                        debug!(target: "RPC SERVER", "failed to read from socket; err = {:?}", e);
+                        return;
+                    }
+                };
+
+                let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
+                    Ok(r) => r,
+                    Err(e) => {
+                        debug!(target: "RPC SERVER", "received invalid json; err = {:?}", e);
+                        return;
+                    }
+                };
+
+                let reply = darkfid.clone().handle_request(r).await;
+                let j = serde_json::to_string(&reply).unwrap();
+
+                debug!(target: "RPC", "<-- {:#?}", j);
+
+                // Write the data back
+                if let Err(e) = socket.write_all(j.as_bytes()).await {
+                    debug!(target: "RPC SERVER", "failed to write to socket; err = {:?}", e);
+                    return;
+                }
+            }
+        });
+    }
 }

+ 0 - 344
src/bin/darkfid2.rs

@@ -1,344 +0,0 @@
-use log::*;
-use std::fs;
-use std::path::PathBuf;
-
-use clap::clap_app;
-use serde_json::{json, Value};
-use simplelog::{
-    CombinedLogger, Config as SimLogConfig, ConfigBuilder, LevelFilter, TermLogger, TerminalMode,
-    WriteLogger,
-};
-
-use async_std::sync::Arc;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-use tokio::net::TcpListener;
-
-use drk::{
-    cli::{Config, DarkfidConfig},
-    rpc::{
-        jsonrpc::{error as jsonerr, request as jsonreq, response as jsonresp, send_request},
-        jsonrpc::{ErrorCode::*, JsonRequest, JsonResult},
-    },
-    serial::serialize,
-    util::join_config_path,
-    wallet::WalletDb,
-    Error,
-};
-
-#[derive(Clone)]
-struct Darkfid {
-    verbose: bool,
-    config: DarkfidConfig,
-    wallet: Arc<WalletDb>,
-    // clientdb:
-    // mint_params:
-    // spend_params:
-}
-
-impl Darkfid {
-    fn new(verbose: bool, config_path: PathBuf) -> Result<Self, Error> {
-        let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
-        let wallet = WalletDb::new(
-            &PathBuf::from(config.walletdb_path.clone()),
-            config.password.clone(),
-        )?;
-
-        Ok(Self {
-            verbose,
-            config,
-            wallet,
-        })
-    }
-
-    // TODO: ServerError codes should be part of the lib.
-    async fn handle_request(self, req: JsonRequest) -> 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("say_hello") => return self.say_hello(req.id, req.params).await,
-            Some("create_wallet") => return self.create_wallet(req.id, req.params).await,
-            Some("key_gen") => return self.key_gen(req.id, req.params).await,
-            Some("get_key") => return self.get_key(req.id, req.params).await,
-            Some("get_token_id") => return self.get_token_id(req.id, req.params).await,
-            Some("features") => return self.features(req.id, req.params).await,
-            Some("deposit") => return self.deposit(req.id, req.params).await,
-            Some("withdraw") => return self.withdraw(req.id, req.params).await,
-            Some("transfer") => return self.transfer(req.id, req.params).await,
-            Some(_) => {}
-            None => {}
-        };
-
-        return JsonResult::Err(jsonerr(MethodNotFound, None, req.id));
-    }
-
-    // --> {"method": "say_hello", "params": []}
-    // <-- {"result": "hello world"}
-    async fn say_hello(self, id: Value, _params: Value) -> JsonResult {
-        JsonResult::Resp(jsonresp(json!("hello world"), id))
-    }
-
-    // --> {"method": "create_wallet", "params": []}
-    // <-- {"result": true}
-    async fn create_wallet(self, id: Value, _params: Value) -> JsonResult {
-        match self.wallet.init_db() {
-            Ok(()) => return JsonResult::Resp(jsonresp(json!(true), id)),
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32001), Some(e.to_string()), id))
-            }
-        }
-    }
-
-    // --> {"method": "key_gen", "params": []}
-    // <-- {"result": true}
-    async fn key_gen(self, id: Value, _params: Value) -> JsonResult {
-        match self.wallet.key_gen() {
-            Ok((_, _)) => return JsonResult::Resp(jsonresp(json!(true), id)),
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32002), Some(e.to_string()), id))
-            }
-        }
-    }
-
-    // --> {"method": "get_key", "params": []}
-    // <-- {"result": "vdNS7oBj7KvsMWWmo9r96SV4SqATLrGsH2a3PGpCfJC"}
-    async fn get_key(self, id: Value, _params: Value) -> JsonResult {
-        match self.wallet.get_keypairs() {
-            Ok(v) => {
-                let pk = v[0].public;
-                let b58 = bs58::encode(serialize(&pk)).into_string();
-                return JsonResult::Resp(jsonresp(json!(b58), id));
-            }
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
-            }
-        }
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "get_token_id",
-    //      "params": [token],
-    //      "id": 42}
-    // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
-    async fn get_token_id(self, id: Value, params: Value) -> JsonResult {
-        let args = params.as_array().unwrap();
-        let symbol = &args[0];
-
-        if symbol.as_str().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        };
-
-        let symbol = symbol.as_str().unwrap().to_uppercase();
-
-        let file_contents =
-            fs::read_to_string("token/solanatokenlist.json").expect("Can't find tokenlist file");
-        let root: Value = serde_json::from_str(&file_contents).unwrap();
-        let tokens = root["tokens"].as_array().unwrap();
-
-        for item in tokens {
-            if item["symbol"] == symbol {
-                let address = &item["address"];
-                return JsonResult::Resp(jsonresp(json!(address), id));
-            }
-        }
-        return JsonResult::Err(jsonerr(InvalidParams, None, id));
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "features", "params": [], "id": 42}
-    // <-- {"jsonrpc": "2.0", "result": ["network": "btc", "sol"], "id": 42}
-    async fn features(self, id: Value, params: Value) -> JsonResult {
-        // TODO: return a dictionary of features
-        let req = jsonreq(json!("features"), json!([]));
-        let rep: JsonResult;
-        match send_request(self.config.cashier_url, json!(req)).await {
-            Ok(v) => rep = v,
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
-            }
-        }
-
-        match rep {
-            JsonResult::Resp(r) => return JsonResult::Resp(r),
-            JsonResult::Err(e) => return JsonResult::Err(e),
-            JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
-        }
-    }
-
-    // --> {"jsonrpc": "2.0", "method": "deposit",
-    //      "params": [network, token, publickey],
-    //      "id": 42}
-    // The publickey sent here is used so the cashier can know where to send
-    // assets once the deposit is received.
-    // <-- {"result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL"}
-    async fn deposit(self, id: Value, params: Value) -> JsonResult {
-        let args = params.as_array().unwrap();
-        if args.len() != 2 {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        }
-
-        let network = &args[0];
-        let token = &args[1];
-
-        if token.as_str().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        };
-
-        // TODO: Optional sanity checking here, but cashier *must* do so too.
-
-        let pubkey: String;
-        match self.wallet.get_keypairs() {
-            Ok(v) => {
-                let pk = v[0].public;
-                pubkey = bs58::encode(serialize(&pk)).into_string();
-            }
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32003), Some(e.to_string()), id))
-            }
-        }
-
-        // Send request to cashier. If the cashier supports the requested network
-        // (and token), it shall return a valid address where assets can be deposited.
-        // If not, an error is returned, and forwarded to the method caller.
-        let req = jsonreq(json!("deposit"), json!([network, token, pubkey]));
-        let rep: JsonResult;
-        match send_request(self.config.cashier_url, json!(req)).await {
-            Ok(v) => rep = v,
-            Err(e) => {
-                return JsonResult::Err(jsonerr(ServerError(-32004), Some(e.to_string()), id))
-            }
-        }
-
-        match rep {
-            JsonResult::Resp(r) => return JsonResult::Resp(r),
-            JsonResult::Err(e) => return JsonResult::Err(e),
-            JsonResult::Notif(_n) => return JsonResult::Err(jsonerr(InternalError, None, id)),
-        }
-    }
-
-    // --> {"method": "withdraw", "params": [network, token, publickey, amount]}
-    // The publickey sent here is the address where the caller wants to receive
-    // the tokens they plan to withdraw.
-    // On request, send request to cashier to get deposit address, and then transfer
-    // dark assets to the cashier's wallet. Following that, the cashier should return
-    // a transaction ID of them sending the funds that are requested for withdrawal.
-    // <-- {"result": "txID"}
-    async fn withdraw(self, id: Value, params: Value) -> JsonResult {
-        let args = params.as_array().unwrap();
-        if args.len() != 4 {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id));
-        }
-
-        let network = &args[0];
-        let token = &args[1];
-        let address = &args[2];
-        let amount = &args[3];
-
-        // 1. Send request to cashier.
-        // 2. Cashier checks if they support the network, and if so,
-        //    return adeposit address.
-        // 3. We issue a transfer of $amount to the given address.
-
-        return JsonResult::Err(jsonerr(
-            ServerError(-32005),
-            Some("failed to withdraw".to_string()),
-            id,
-        ));
-    }
-
-    // --> {"method": "transfer", [dToken, address, amount]}
-    // <-- {"result": "txID"}
-    async fn transfer(self, id: Value, _params: Value) -> JsonResult {
-        return JsonResult::Err(jsonerr(
-            ServerError(-32006),
-            Some("failed to transfer".to_string()),
-            id,
-        ));
-    }
-}
-
-#[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let args = clap_app!(darkfid =>
-        (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
-        (@arg verbose: -v --verbose "Increase verbosity")
-    )
-    .get_matches();
-
-    let config_path: PathBuf;
-    if args.is_present("CONFIG") {
-        config_path = PathBuf::from(args.value_of("CONFIG").unwrap());
-    } else {
-        config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
-    }
-
-    let darkfid = Darkfid::new(args.clone().is_present("verbose"), config_path)?;
-    // TODO: TLS
-    let listener = TcpListener::bind(darkfid.clone().config.rpc_url).await?;
-    debug!(target: "RPC SERVER", "Listening on {}", darkfid.clone().config.rpc_url);
-
-    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
-    let debug_level = if args.is_present("verbose") {
-        LevelFilter::Debug
-    } else {
-        LevelFilter::Off
-    };
-
-    let log_path = darkfid.clone().config.log_path;
-    CombinedLogger::init(vec![
-        TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
-        WriteLogger::new(
-            LevelFilter::Debug,
-            SimLogConfig::default(),
-            std::fs::File::create(log_path).unwrap(),
-        ),
-    ])
-    .unwrap();
-
-    loop {
-        debug!(target: "RPC SERVER", "waiting for client");
-
-        let (mut socket, _) = listener.accept().await?;
-        let darkfid = darkfid.clone();
-
-        debug!(target: "RPC SERVER", "accepted client");
-
-        tokio::spawn(async move {
-            let mut buf = [0; 2048];
-
-            loop {
-                let n = match socket.read(&mut buf).await {
-                    Ok(n) if n == 0 => {
-                        debug!(target: "RPC SERVER", "closed connection");
-                        return;
-                    }
-                    Ok(n) => n,
-                    Err(e) => {
-                        debug!(target: "RPC SERVER", "failed to read from socket; err = {:?}", e);
-                        return;
-                    }
-                };
-
-                let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
-                    Ok(r) => r,
-                    Err(e) => {
-                        debug!(target: "RPC SERVER", "received invalid json; err = {:?}", e);
-                        return;
-                    }
-                };
-
-                let reply = darkfid.clone().handle_request(r).await;
-                let j = serde_json::to_string(&reply).unwrap();
-
-                debug!(target: "RPC", "<-- {:#?}", j);
-
-                // Write the data back
-                if let Err(e) = socket.write_all(j.as_bytes()).await {
-                    debug!(target: "RPC SERVER", "failed to write to socket; err = {:?}", e);
-                    return;
-                }
-            }
-        });
-    }
-}

+ 115 - 0
src/bin/darkfid_old

@@ -0,0 +1,115 @@
+use drk::blockchain::Rocks;
+use drk::cli::{Config, DarkfidCli, DarkfidConfig};
+use drk::util::join_config_path;
+use drk::wallet::WalletDb;
+use drk::Result;
+
+use drk::client::Client;
+
+use async_executor::Executor;
+use easy_parallel::Parallel;
+
+use async_std::sync::Arc;
+use std::net::SocketAddr;
+use std::path::PathBuf;
+
+async fn start(executor: Arc<Executor<'_>>, config: Arc<DarkfidConfig>) -> Result<()> {
+    let connect_addr: SocketAddr = config.connect_url.parse()?;
+    let sub_addr: SocketAddr = config.subscriber_url.parse()?;
+    let cashier_addr: SocketAddr = config.cashier_url.parse()?;
+    let rpc_url: std::net::SocketAddr = config.rpc_url.parse()?;
+
+    let database_path = join_config_path(&PathBuf::from("database_client.db"))?;
+    let walletdb_path = join_config_path(&PathBuf::from("walletdb.db"))?;
+
+    let rocks = Rocks::new(&database_path)?;
+
+    let wallet = WalletDb::new(&walletdb_path, config.password.clone())?;
+
+    let mint_params_path = join_config_path(&PathBuf::from("mint.params"))?;
+    let spend_params_path = join_config_path(&PathBuf::from("spend.params"))?;
+
+    if let Err(_) = wallet.get_keypairs() {
+        wallet.init_db()?;
+        wallet.key_gen()?;
+    }
+
+    let mut client = Client::new(
+        rocks,
+        (connect_addr, sub_addr),
+        (mint_params_path, spend_params_path),
+        wallet.clone(),
+    )?;
+
+    client.start().await?;
+
+    Client::connect_to_cashier(
+        client,
+        executor.clone(),
+        cashier_addr.clone(),
+        rpc_url.clone(),
+    )
+    .await?;
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let options = Arc::new(DarkfidCli::load()?);
+
+    let config_path: PathBuf;
+
+    match options.config.as_ref() {
+        Some(path) => {
+            config_path = path.to_owned();
+        }
+        None => {
+            config_path = join_config_path(&PathBuf::from("darkfid.toml"))?;
+        }
+    }
+
+    let config: DarkfidConfig = Config::<DarkfidConfig>::load(config_path)?;
+
+    let config = Arc::new(config);
+
+    let ex = Arc::new(Executor::new());
+    let (signal, shutdown) = async_channel::unbounded::<()>();
+
+    {
+        use simplelog::*;
+        let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+
+        let debug_level = if options.verbose {
+            LevelFilter::Debug
+        } else {
+            LevelFilter::Off
+        };
+
+        let log_path = config.log_path.clone();
+        CombinedLogger::init(vec![
+            TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
+            WriteLogger::new(
+                LevelFilter::Debug,
+                Config::default(),
+                std::fs::File::create(log_path).unwrap(),
+            ),
+        ])
+        .unwrap();
+    }
+
+    let ex2 = ex.clone();
+
+    let (_, result) = Parallel::new()
+        // Run four executor threads.
+        .each(0..3, |_| 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).await?;
+                drop(signal);
+                Ok::<(), drk::Error>(())
+            })
+        });
+
+    result
+}

+ 3 - 6
src/cli/cli_config.rs

@@ -84,15 +84,12 @@ pub struct CashierdConfig {
     #[serde(rename = "gateway_url")]
     pub gateway_url: String,
 
+    #[serde(rename = "gateway_subscriber_url")]
+    pub gateway_subscriber_url: String,
+
     #[serde(rename = "log_path")]
     pub log_path: String,
 
-    #[serde(rename = "database_path")]
-    pub database_path: String,
-
-    #[serde(rename = "cashierdb_path")]
-    pub cashierdb_path: String,
-
     #[serde(rename = "password")]
     pub password: String,
 

+ 1 - 27
src/client/client.rs

@@ -7,11 +7,9 @@ use crate::crypto::{
     nullifier::Nullifier,
     save_params, setup_mint_prover, setup_spend_prover, OwnCoin,
 };
-use crate::rpc::adapter::{RpcClient, RpcClientAdapter};
-use crate::rpc::jsonserver;
 use crate::serial::Decodable;
 use crate::serial::Encodable;
-use crate::service::{CashierClient, GatewayClient, GatewaySlabsSubscriber};
+use crate::service::{GatewayClient, GatewaySlabsSubscriber};
 use crate::state::{state_transition, ProgramState, StateUpdate};
 use crate::wallet::{CashierDbPtr, WalletPtr};
 use crate::{tx, Result};
@@ -22,8 +20,6 @@ use async_executor::Executor;
 use bellman::groth16;
 use bls12_381::Bls12;
 
-use jsonrpc_core::IoHandler;
-
 use async_std::sync::{Arc, Mutex};
 use log::*;
 use std::net::SocketAddr;
@@ -93,30 +89,8 @@ impl Client {
     pub async fn connect_to_cashier(
         client: Client,
         executor: Arc<Executor<'_>>,
-        cashier_addr: SocketAddr,
-        rpc_url: SocketAddr,
     ) -> Result<()> {
-        // create cashier client
-        debug!(target: "CLIENT", "Creating cashier client");
-        let mut cashier_client = CashierClient::new(cashier_addr)?;
-
-        // start cashier_client
-        cashier_client.start().await?;
-
         let client_mutex = Arc::new(Mutex::new(client));
-        let cashier_mutex = Arc::new(Mutex::new(cashier_client));
-
-        let mut io = IoHandler::new();
-
-        let rpc_client_adapter = RpcClientAdapter::new(client_mutex.clone(), cashier_mutex.clone());
-
-        io.extend_with(rpc_client_adapter.to_delegate());
-
-        let io = Arc::new(io);
-
-        // start the rpc server
-        debug!(target: "CLIENT", "Start RPC server");
-        let _ = jsonserver::start(executor.clone(), rpc_url, io).await?;
 
         // start subscriber
         Client::connect_to_subscriber(client_mutex.clone(), executor.clone()).await?;

+ 0 - 0
src/rpc/adapter/client_adapter.rs → src/rpc/adapter_old/client_adapter.rs


+ 0 - 0
src/rpc/adapter/mod.rs → src/rpc/adapter_old/mod.rs


+ 1 - 1
src/rpc/mod.rs

@@ -1,3 +1,3 @@
-pub mod adapter;
+// pub mod adapter;
 pub mod jsonrpc;
 pub mod jsonserver;

+ 8 - 8
src/service/bridge.rs

@@ -32,19 +32,19 @@ pub struct BridgeSubscribtion {
     pub receiver: async_channel::Receiver<BridgeResponse>,
 }
 
-pub struct CoinSubscribtion {
+pub struct TokenSubscribtion {
     pub secret_key: Vec<u8>,
     pub public_key: Vec<u8>,
 }
 
-pub struct CoinNotification {
+pub struct TokenNotification {
     pub secret_key: Vec<u8>,
     pub received_balance: u64,
 }
 
 pub struct Bridge {
-    clients: Mutex<HashMap<Vec<u8>, Arc<dyn CoinClient + Send + Sync>>>,
-    notifiers: Mutex<HashMap<Vec<u8>, async_channel::Receiver<CoinNotification>>>,
+    clients: Mutex<HashMap<Vec<u8>, Arc<dyn TokenClient + Send + Sync>>>,
+    notifiers: Mutex<HashMap<Vec<u8>, async_channel::Receiver<TokenNotification>>>,
 }
 
 impl Bridge {
@@ -58,7 +58,7 @@ impl Bridge {
     pub async fn add_clients(
         self: Arc<Self>,
         asset_id: jubjub::Fr,
-        client: Arc<dyn CoinClient + Send + Sync>,
+        client: Arc<dyn TokenClient + Send + Sync>,
     ) -> Result<()> {
         let asset_id = serialize(&asset_id);
 
@@ -118,8 +118,8 @@ impl Bridge {
 }
 
 #[async_trait]
-pub trait CoinClient {
-    async fn subscribe(&self) -> Result<CoinSubscribtion>;
-    async fn get_notifier(&self) -> Result<async_channel::Receiver<CoinNotification>>;
+pub trait TokenClient {
+    async fn subscribe(&self) -> Result<TokenSubscribtion>;
+    async fn get_notifier(&self) -> Result<async_channel::Receiver<TokenNotification>>;
     async fn send(&self, address: Vec<u8>, amount: u64) -> Result<()>;
 }

+ 5 - 5
src/service/btc.rs

@@ -1,4 +1,4 @@
-use super::bridge::{CoinClient, CoinNotification, CoinSubscribtion};
+use super::bridge::{TokenClient, TokenNotification, TokenSubscribtion};
 use crate::serial::{serialize, Decodable, Encodable};
 use crate::Result;
 
@@ -155,8 +155,8 @@ impl BtcClient {
 }
 
 #[async_trait]
-impl CoinClient for BtcClient {
-    async fn subscribe(&self) -> Result<CoinSubscribtion> {
+impl TokenClient for BtcClient {
+    async fn subscribe(&self) -> Result<TokenSubscribtion> {
         //// Generate bitcoin Address
         let btc_keys = BitcoinKeys::new(self.client.clone(), self.network)?;
 
@@ -171,13 +171,13 @@ impl CoinClient for BtcClient {
         let (_txid, _balance) = btc_keys.start_subscribe().await?;
         //let _script = btc_keys.get_script();
 
-        Ok(CoinSubscribtion {
+        Ok(TokenSubscribtion {
             secret_key: serialize(&btc_priv.to_bytes()),
             public_key: serialize(&btc_pub.to_bytes()),
         })
     }
 
-    async fn get_notifier(&self) -> Result<async_channel::Receiver<CoinNotification>> {
+    async fn get_notifier(&self) -> Result<async_channel::Receiver<TokenNotification>> {
         // TODO this not implemented yet
         let (_, notifier) = async_channel::unbounded();
         Ok(notifier)

+ 1 - 2
src/service/mod.rs

@@ -1,4 +1,4 @@
-pub mod cashier;
+//pub mod cashier;
 pub mod gateway;
 pub mod reqrep;
 pub mod bridge;
@@ -15,5 +15,4 @@ pub use sol::{SolClient, SolFailed, SolResult};
 
 pub use gateway::{GatewayClient, GatewayService, GatewaySlabsSubscriber};
 
-pub use cashier::{CashierClient, CashierService};
 

+ 8 - 8
src/service/sol.rs

@@ -2,7 +2,7 @@ use crate::rpc::{jsonrpc, jsonrpc::JsonResult};
 use crate::serial::{deserialize, serialize, Decodable, Encodable};
 use crate::{Error, Result};
 
-use super::bridge::{ CoinSubscribtion, CoinNotification, CoinClient};
+use super::bridge::{ TokenSubscribtion, TokenNotification, TokenClient};
 
 use async_trait::async_trait;
 
@@ -44,8 +44,8 @@ pub struct SolClient {
     subscriptions: Arc<Mutex<HashMap<Pubkey, (Keypair, u64)>>>,
 
     notify_channel: (
-        async_channel::Sender<CoinNotification>,
-        async_channel::Receiver<CoinNotification>,
+        async_channel::Sender<TokenNotification>,
+        async_channel::Receiver<TokenNotification>,
     ),
 
     subscribe_channel: (
@@ -154,7 +154,7 @@ impl SolClient {
 
                     self.notify_channel
                         .0
-                        .send(CoinNotification {
+                        .send(TokenNotification {
                             secret_key: serialize(keypair),
                             received_balance,
                         })
@@ -203,8 +203,8 @@ impl SolClient {
 }
 
 #[async_trait]
-impl CoinClient for SolClient {
-    async fn subscribe(&self) -> Result<CoinSubscribtion> {
+impl TokenClient for SolClient {
+    async fn subscribe(&self) -> Result<TokenSubscribtion> {
         let keypair = Keypair::generate(&mut OsRng);
 
         // Parameters for subscription to events related to `pubkey`.
@@ -237,10 +237,10 @@ impl CoinClient for SolClient {
         //  send
         self.subscribe_channel.0.send(sub_msg).await?;
 
-        Ok(CoinSubscribtion { secret_key, public_key})
+        Ok(TokenSubscribtion { secret_key, public_key})
     }
 
-    async fn get_notifier(&self) -> Result<async_channel::Receiver<CoinNotification>>{
+    async fn get_notifier(&self) -> Result<async_channel::Receiver<TokenNotification>>{
         Ok(self.notify_channel.1.clone())
     }
 

+ 45 - 41
src/wallet/cashierdb.rs

@@ -48,7 +48,7 @@ impl CashierDb {
     }
 
     // return private and public keys as a tuple
-    pub fn get_deposit_coin_keys_by_dkey_public(
+    pub fn get_deposit_token_keys_by_dkey_public(
         &self,
         d_key_public: &jubjub::SubgroupPoint,
         asset_id: &Vec<u8>,
@@ -76,12 +76,11 @@ impl CashierDb {
         Ok(keys)
     }
 
-    // Update to take BitcoinKeys instance instead
     pub fn put_exchange_keys(
         &self,
         d_key_public: &jubjub::SubgroupPoint,
-        coin_private: &Vec<u8>,
-        coin_public: &Vec<u8>,
+        token_private: &Vec<u8>,
+        token_public: &Vec<u8>,
         asset_id: &Vec<u8>,
     ) -> Result<()> {
         debug!(target: "CASHIERDB", "Put exchange keys");
@@ -94,17 +93,19 @@ impl CashierDb {
         conn.pragma_update(None, "key", &self.password)?;
 
         conn.execute(
-            "INSERT INTO deposit_keypairs(d_key_public, coin_key_private, coin_key_public, asset_id)
-            VALUES (:d_key_public, :coin_key_private, :coin_key_public, :asset_id)",
+            "INSERT INTO deposit_keypairs(d_key_public, token_key_private, token_public_key_public, asset_id)
+            VALUES (:d_key_public, :token_key_private, :token_key_public, :asset_id)",
             named_params! {
                 ":d_key_public": d_key_public,
-                ":coin_key_private": coin_private,
-                ":coin_key_public": coin_public,
+                ":token_key_private": token_private,
+                ":token_key_public": token_public,
                 ":asset_id": asset_id,
             },
         )?;
         Ok(())
     }
+
+    // TODO convert this to generic function work with different tokens
     pub fn put_btc_utxo(
         &self,
         tx_id: &Vec<u8>,
@@ -131,6 +132,7 @@ impl CashierDb {
         )?;
         Ok(())
     }
+
     pub fn get_withdraw_private_keys(&self) -> Result<Vec<jubjub::Fr>> {
         debug!(target: "CASHIERDB", "Get withdraw private keys");
         // open connection
@@ -158,12 +160,12 @@ impl CashierDb {
         Ok(private_keys)
     }
 
-    pub fn get_withdraw_keys_by_coin_public_key(
+    pub fn get_withdraw_keys_by_token_public_key(
         &self,
-        coin_public_key: &Vec<u8>,
+        token_public_key: &Vec<u8>,
         asset_id: &Vec<u8>,
     ) -> Result<Option<Keypair>> {
-        debug!(target: "CASHIERDB", "Check for existing coin address");
+        debug!(target: "CASHIERDB", "Check for existing token address");
         // open connection
         let conn = Connection::open(&self.path)?;
         // unlock database
@@ -173,11 +175,11 @@ impl CashierDb {
 
         let mut stmt =
             conn.prepare(
-                "SELECT * FROM withdraw_keypairs WHERE coin_key_id = :coin_key_id AND asset_id = :asset_id AND confirm = :confirm;")?;
+                "SELECT * FROM withdraw_keypairs WHERE token_key_id = :token_key_id AND asset_id = :asset_id AND confirm = :confirm;")?;
 
         let addr_iter = stmt.query_map::<Keypair, _, _>(
             &[
-                (":coin_key_id", &coin_public_key),
+                (":token_key_id", &token_public_key),
                 (":asset_id", &asset_id),
                 (":confirm", &&confirm),
             ],
@@ -201,12 +203,11 @@ impl CashierDb {
         Ok(addresses.pop())
     }
 
-    pub fn get_withdraw_coin_public_key_by_dkey_public(
+    pub fn get_withdraw_token_public_key_by_dkey_public(
         &self,
         pub_key: &jubjub::SubgroupPoint,
-        asset_id: &Vec<u8>,
-    ) -> Result<Option<Vec<u8>>> {
-        debug!(target: "CASHIERDB", "Get coin address by pub_key");
+    ) -> Result<Option<(Vec<u8>, jubjub::Fr)>> {
+        debug!(target: "CASHIERDB", "Get token address by pub_key");
         // open connection
         let conn = Connection::open(&self.path)?;
         // unlock database
@@ -217,29 +218,32 @@ impl CashierDb {
         let confirm = self.get_value_serialized(&false)?;
 
         let mut stmt = conn.prepare(
-            "SELECT coin_key_id FROM withdraw_keypairs WHERE d_key_public = :d_key_public AND asset_id = :asset_id AND confirm = :confirm;",
+            "SELECT token_key_id, asset_id FROM withdraw_keypairs WHERE d_key_public = :d_key_public AND confirm = :confirm;",
         )?;
-        let addr_iter = stmt.query_map::<Vec<u8>, _, _>(
-            &[
-                (":d_key_public", &d_key_public),
-                (":asset_id", &asset_id),
-                (":confirm", &&confirm),
-            ],
-            |row| Ok(row.get(0)?),
+        let addr_iter = stmt.query_map::<(Vec<u8>, jubjub::Fr), _, _>(
+            &[(":d_key_public", &d_key_public), (":confirm", &&confirm)],
+            |row| {
+                let token_public_key = row.get(0)?;
+                let asset_id = row.get(1)?;
+                let asset_id: jubjub::Fr = self
+                    .get_value_deserialized(asset_id)
+                    .expect("deserialize asset_id");
+                Ok((token_public_key, asset_id))
+            },
         )?;
 
-        let mut coin_addresses = vec![];
+        let mut token_addresses = vec![];
 
         for addr in addr_iter {
-            coin_addresses.push(addr?);
+            token_addresses.push(addr?);
         }
 
-        Ok(coin_addresses.pop())
+        Ok(token_addresses.pop())
     }
 
     pub fn confirm_withdraw_key_record(
         &self,
-        coin_address: &Vec<u8>,
+        token_address: &Vec<u8>,
         asset_id: &Vec<u8>,
     ) -> Result<()> {
         debug!(target: "CASHIERDB", "Confirm withdraw keys");
@@ -252,8 +256,8 @@ impl CashierDb {
         let confirm = self.get_value_serialized(&true)?;
 
         conn.execute(
-            "UPDATE withdraw_keypairs SET confirm = ?1  WHERE coin_key_id = ?2 AND asset_id = ?3;",
-            params![confirm, coin_address, asset_id],
+            "UPDATE withdraw_keypairs SET confirm = ?1  WHERE token_key_id = ?2 AND asset_id = ?3;",
+            params![confirm, token_address, asset_id],
         )?;
 
         Ok(())
@@ -261,7 +265,7 @@ impl CashierDb {
 
     pub fn put_withdraw_keys(
         &self,
-        coin_key_id: &Vec<u8>,
+        token_key_id: &Vec<u8>,
         d_key_public: &jubjub::SubgroupPoint,
         d_key_private: &jubjub::Fr,
         asset_id: &Vec<u8>,
@@ -279,10 +283,10 @@ impl CashierDb {
         let confirm = self.get_value_serialized(&false)?;
 
         conn.execute(
-            "INSERT INTO withdraw_keypairs(coin_key_id, d_key_private, d_key_public, asset_id, confirm)
-            VALUES (:coin_key_id, :d_key_private, :d_key_public, :asset_id, :confirm)",
+            "INSERT INTO withdraw_keypairs(token_key_id, d_key_private, d_key_public, asset_id, confirm)
+            VALUES (:token_key_id, :d_key_private, :d_key_public, :asset_id, :confirm)",
             named_params! {
-                ":coin_key_id": coin_key_id,
+                ":token_key_id": token_key_id,
                 ":d_key_private": d_key_private,
                 ":d_key_public": d_key_public,
                 ":asset_id": asset_id,
@@ -306,7 +310,7 @@ mod tests {
     // TODO add more tests
 
     #[test]
-    pub fn test_put_withdraw_keys_and_load_them_with_coin_key() -> Result<()> {
+    pub fn test_put_withdraw_keys_and_load_them_with_token_key() -> Result<()> {
         let walletdb_path = join_config_path(&PathBuf::from("cashier_wallet_test.db"))?;
         let wallet = CashierDb::new(&walletdb_path, "darkfi".into())?;
         wallet.init_db()?;
@@ -315,19 +319,19 @@ mod tests {
         let public2 = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret2;
 
         // btc addr testnet
-        let coin_addr = serialize(&String::from("mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk"));
+        let token_addr = serialize(&String::from("mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk"));
 
         let asset_id = serialize(&1);
 
-        wallet.put_withdraw_keys(&coin_addr, &public2, &secret2, &asset_id)?;
+        wallet.put_withdraw_keys(&token_addr, &public2, &secret2, &asset_id)?;
 
-        let addr = wallet.get_withdraw_keys_by_coin_public_key(&coin_addr, &asset_id)?;
+        let addr = wallet.get_withdraw_keys_by_token_public_key(&token_addr, &asset_id)?;
 
         assert_eq!(addr.is_some(), true);
 
-        wallet.confirm_withdraw_key_record(&coin_addr, &asset_id)?;
+        wallet.confirm_withdraw_key_record(&token_addr, &asset_id)?;
 
-        let addr = wallet.get_withdraw_keys_by_coin_public_key(&coin_addr, &asset_id)?;
+        let addr = wallet.get_withdraw_keys_by_token_public_key(&token_addr, &asset_id)?;
 
         assert_eq!(addr.is_none(), true);
 

+ 1 - 0
src/wallet/wallet_api.rs

@@ -14,6 +14,7 @@ pub trait WalletApi {
         Ok(v)
     }
 
+    // TODO pass a reference of Vec<u8>
     fn get_value_deserialized<D: Decodable>(&self, key: Vec<u8>) -> Result<D> {
         let v: D = deserialize(&key)?;
         Ok(v)