Browse Source

WIP implementation of darkfid with new API.

parazyd 4 năm trước cách đây
mục cha
commit
a8b1771a4a
4 tập tin đã thay đổi với 247 bổ sung4 xóa
  1. 4 2
      Cargo.toml
  2. 234 0
      src/bin/darkfid2.rs
  3. 8 2
      src/cli/cli_config.rs
  4. 1 0
      src/wallet/walletdb.rs

+ 4 - 2
Cargo.toml

@@ -74,6 +74,9 @@ toml = "0.5.8"
 # serde
 serde = { version = "1.0.126", features = ["derive"]}
 
+# tokio async
+tokio = { version = "1.11.0", features = ["full"] }
+
 # zmq
 zeromq = { git="https://github.com/zeromq/zmq.rs", default-features = false, features = ["async-std-runtime", "all-transport"] }
 bytes = "1.0.1"
@@ -86,7 +89,6 @@ dirs = "3.0.2"
 solana-sdk = {version = "1.7.11", optional = true}
 solana-client = {version = "1.7.11", optional = true}
 tokio-tungstenite = {version = "0.15.0", optional = true} 
-tokio = {version = "1.11.0", features = ["full"], optional = true}
 
 ## Cashier Bitcoin Dependencies
 bitcoin = {version = "0.27.0", optional = true }
@@ -99,7 +101,7 @@ features = ["bundled", "sqlcipher"]
 
 [features]
 default = ["bitcoin", "secp256k1", "electrum-client"]
-sol = ["solana-sdk", "solana-client", "tokio-tungstenite", "tokio" ]
+sol = ["solana-sdk", "solana-client", "tokio-tungstenite"]
 
 [[bin]]
 name = "gatewayd"

+ 234 - 0
src/bin/darkfid2.rs

@@ -0,0 +1,234 @@
+use async_std::sync::Arc;
+use std::path::PathBuf;
+
+use clap::clap_app;
+use serde_json::{json, Value};
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::net::TcpListener;
+
+use drk::{
+    cli::{Config, DarkfidConfig},
+    rpc::{
+        jsonrpc,
+        jsonrpc::{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,
+        })
+    }
+
+    async fn handle_request(self, req: JsonRequest) -> JsonResult {
+        if req.params.as_array().is_none() {
+            return JsonResult::Err(jsonrpc::error(
+                -69,
+                "invalid parameters".to_string(),
+                req.id,
+            ));
+        }
+
+        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("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(jsonrpc::error(
+            -69,
+            "method not implemented".to_string(),
+            req.id,
+        ));
+    }
+
+    async fn say_hello(self, id: Value, _params: Value) -> JsonResult {
+        JsonResult::Resp(jsonrpc::response(json!("hello world"), id))
+    }
+
+    async fn create_wallet(self, id: Value, _params: Value) -> JsonResult {
+        match self.wallet.init_db() {
+            Ok(()) => return JsonResult::Resp(jsonrpc::response(json!(true), id)),
+            Err(e) => return JsonResult::Err(jsonrpc::error(-69, e.to_string(), id)),
+        }
+    }
+
+    async fn key_gen(self, id: Value, _params: Value) -> JsonResult {
+        match self.wallet.key_gen() {
+            Ok((_, _)) => return JsonResult::Resp(jsonrpc::response(json!(true), id)),
+            Err(e) => return JsonResult::Err(jsonrpc::error(-69, e.to_string(), id)),
+        }
+    }
+
+    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(jsonrpc::response(json!(b58), id));
+            }
+            Err(e) => return JsonResult::Err(jsonrpc::error(-69, e.to_string(), 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.
+    async fn deposit(self, id: Value, params: Value) -> JsonResult {
+        let args = params.as_array().unwrap();
+        if args.len() != 2 {
+            return JsonResult::Err(jsonrpc::error(-69, "missing parameters".to_string(), id));
+        }
+
+        let network = &args[0];
+        let token = &args[1];
+        // 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(jsonrpc::error(-69, 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 = jsonrpc::request(json!("deposit"), json!([network, token, pubkey]));
+        let rep: JsonResult;
+        match jsonrpc::send_request(self.config.cashier_url, json!(req)).await {
+            Ok(v) => rep = v,
+            Err(e) => return JsonResult::Err(jsonrpc::error(-69, 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(jsonrpc::error(
+                    -69,
+                    "invalid reply from cashier".to_string(),
+                    id,
+                ))
+            }
+        }
+    }
+
+    async fn withdraw(self, id: Value, params: Value) -> JsonResult {
+        let args = params.as_array().unwrap();
+        if args.len() != 4 {
+            return JsonResult::Err(jsonrpc::error(-69, "missing parameters".to_string(), 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(jsonrpc::error(-69, "failed to withdraw".to_string(), id));
+    }
+
+    async fn transfer(self, id: Value, _params: Value) -> JsonResult {
+        return JsonResult::Err(jsonrpc::error(-69, "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?;
+
+    loop {
+        let (mut socket, _) = listener.accept().await?;
+
+        println!("Accepted client");
+        let darkfid = darkfid.clone();
+
+        tokio::spawn(async move {
+            let mut buf = [0; 2048];
+
+            loop {
+                let n = match socket.read(&mut buf).await {
+                    Ok(n) if n == 0 => {
+                        println!("Closed connection");
+                        return;
+                    }
+                    Ok(n) => n,
+                    Err(e) => {
+                        eprintln!("failed to read from socket; err = {:?}", e);
+                        return;
+                    }
+                };
+
+                let r: JsonRequest = match serde_json::from_slice(&buf[0..n]) {
+                    Ok(r) => r,
+                    Err(_) => {
+                        eprintln!("received invalid json");
+                        return;
+                    }
+                };
+
+                let reply = darkfid.clone().handle_request(r).await;
+                let j = serde_json::to_string(&reply).unwrap();
+
+                // Write the data back
+                if let Err(e) = socket.write_all(j.as_bytes()).await {
+                    eprintln!("failed to writeto socket; err = {:?}", e);
+                    return;
+                }
+            }
+        });
+    }
+}

+ 8 - 2
src/cli/cli_config.rs

@@ -9,7 +9,7 @@ use std::{
     str,
 };
 
-#[derive(Default)]
+#[derive(Clone, Default)]
 pub struct Config<T> {
     config: PhantomData<T>,
 }
@@ -34,7 +34,7 @@ pub struct DrkConfig {
     pub log_path: String,
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Clone, Serialize, Deserialize, Debug)]
 pub struct DarkfidConfig {
     #[serde(rename = "connect_url")]
     pub connect_url: String,
@@ -48,6 +48,12 @@ pub struct DarkfidConfig {
     #[serde(rename = "rpc_url")]
     pub rpc_url: String,
 
+    #[serde(rename = "database_path")]
+    pub database_path: String,
+
+    #[serde(rename = "walletdb_path")]
+    pub walletdb_path: String,
+
     #[serde(rename = "log_path")]
     pub log_path: String,
 

+ 1 - 0
src/wallet/walletdb.rs

@@ -22,6 +22,7 @@ pub struct Keypair {
     pub private: jubjub::Fr,
 }
 
+#[derive(Clone)]
 pub struct WalletDb {
     pub path: PathBuf,
     pub password: String,