From d4d74f109f621d4119c5a7039ae1b3066c014603 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 15:31:38 +0200 Subject: [PATCH 1/8] removed old wallet run code --- Cargo.toml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02ad0948f..111d4ca5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,14 +133,6 @@ path = "src/bin/services.rs" name = "demowallet" path = "src/bin/demowallet.rs" -[[bin]] -name = "wallet" -path = "src/bin/wallet/test.rs" - -[[bin]] -name = "darkd" -path = "src/bin/wallet/darkd.rs" - [profile.release] debug = 1 From 35c02d7e77369fc20a4f56c445fea7a4704d3a48 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 15:33:11 +0200 Subject: [PATCH 2/8] moved cli to top level directory --- darkcli.py | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 darkcli.py diff --git a/darkcli.py b/darkcli.py new file mode 100644 index 000000000..7dc951a07 --- /dev/null +++ b/darkcli.py @@ -0,0 +1,114 @@ +import argparse +import requests +import json + +def arg_parser(client): + parser = argparse.ArgumentParser(prog='dark', + usage='%(prog)s [commands]', + description="""DarkFi wallet + command-line tool""") + parser.add_argument("-k", "--key", action='store_true', help="Generate a new keypair") + parser.add_argument("-i", "--info", action='store_true', help="Request info from daemon") + parser.add_argument("-s", "--stop", action='store_true', help="Send a stop signal to the daemon") + parser.add_argument("-hi", "--hello", action='store_true', help="Say hello") + args = parser.parse_args() + + if args.key: + try: + print("Attemping to generate a new key pair...") + client.key_gen(client.payload) + except Exception: + raise + + if args.info: + try: + print("Info was entered") + client.get_info(client.payload) + print("Requesting daemon info...") + except Exception: + raise + + if args.stop: + try: + print("Stop was entered") + client.stop(client.payload) + print("Sending a stop signal...") + except Exception: + raise + + if args.hello: + try: + print("Hello was entered") + client.say_hello(client.payload) + except Exception: + raise + + +class DarkClient: + # TODO: generate random ID (4 byte unsigned int) (rand range 0 - max size + # uint32 + def __init__(self): + self.url = "http://localhost:8000/" + self.payload = { + "method": [], + "params": [], + "jsonrpc": [], + "id": [], + } + + def key_gen(self, payload): + payload['method'] = "key_gen" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + key = self.__request(payload) + print(key) + + def get_info(self, payload): + payload['method'] = "get_info" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + info = self.__request(payload) + print(info) + + def stop(self, payload): + payload['method'] = "stop" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + stop = self.__request(payload) + print(stop) + + def say_hello(self, payload): + payload['method'] = "say_hello" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + hello = self.__request(payload) + print(hello) + + def __request(self, payload): + response = requests.post(self.url, json=payload).json() + # print something better + # parse into data structure + print(response) + assert response["jsonrpc"] + + +if __name__ == "__main__": + client = DarkClient() + arg_parser(client) + + #rpc() + ## Example echo method + #payload = { + # #"method:": args, + # #"method": "stop", + # "method": "get_info", + # #"method": "say_hello", + # #"params": [], + # "jsonrpc": "2.0", + # "id": 0, + #} + #response = requests.post(url, json=payload).json() + + #print(response) + #assert response["result"] == "Hello World!" + #assert response["jsonrpc"] From e8a400def27c39f062368b48ac54e9bb3d439dbb Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 15:33:43 +0200 Subject: [PATCH 3/8] created rpc module --- src/rpc/__init__.py | 2 + src/rpc/adapter.rs | 2 + src/rpc/jsonclient.py | 53 +++++++++++++++++++ src/rpc/jsonserver.rs | 118 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/mod.rs | 3 ++ src/rpc/test.rs | 3 ++ 6 files changed, 181 insertions(+) create mode 100644 src/rpc/__init__.py create mode 100644 src/rpc/adapter.rs create mode 100644 src/rpc/jsonclient.py create mode 100644 src/rpc/jsonserver.rs create mode 100644 src/rpc/mod.rs create mode 100644 src/rpc/test.rs diff --git a/src/rpc/__init__.py b/src/rpc/__init__.py new file mode 100644 index 000000000..2fbf57db8 --- /dev/null +++ b/src/rpc/__init__.py @@ -0,0 +1,2 @@ +from rpc import jsonclient +from .jsonclient import RpcClient diff --git a/src/rpc/adapter.rs b/src/rpc/adapter.rs new file mode 100644 index 000000000..73aab926c --- /dev/null +++ b/src/rpc/adapter.rs @@ -0,0 +1,2 @@ +// Adapter class goes here +// diff --git a/src/rpc/jsonclient.py b/src/rpc/jsonclient.py new file mode 100644 index 000000000..cb1775181 --- /dev/null +++ b/src/rpc/jsonclient.py @@ -0,0 +1,53 @@ +import requests + +# TODO: generate random ID (4 byte unsigned int) (rand range 0 - max size uint32) +# TODO: make functions async +# TODO: parse json replies into something more legible + +class RpcClient: + def __init__(self): + self.url = "http://localhost:8000/" + self.payload = { + "method": [], + "params": [], + "jsonrpc": [], + "id": [], + } + + def key_gen(self, payload): + payload['method'] = "key_gen" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + key = self.__request(payload) + print(key) + + def get_info(self, payload): + payload['method'] = "get_info" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + info = self.__request(payload) + print(info) + + def stop(self, payload): + payload['method'] = "stop" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + stop = self.__request(payload) + print(stop) + + def say_hello(self, payload): + payload['method'] = "say_hello" + payload['jsonrpc'] = "2.0" + payload['id'] = "0" + hello = self.__request(payload) + print(hello) + + def __request(self, payload): + response = requests.post(self.url, json=payload).json() + print(response) + assert response["jsonrpc"] + + +if __name__ == "__main__": + client = RpcClient() + diff --git a/src/rpc/jsonserver.rs b/src/rpc/jsonserver.rs new file mode 100644 index 000000000..1a5aa242c --- /dev/null +++ b/src/rpc/jsonserver.rs @@ -0,0 +1,118 @@ +#[macro_use] extern crate clap; +use async_executor::Executor; +use async_native_tls::TlsAcceptor; +use async_std::sync::Mutex; +use easy_parallel::Parallel; +use ff::Field; +use http_types::{Request, Response, StatusCode}; +use log::*; +use rand::rngs::OsRng; +use sapvi::serial; +use serde_json::json; +use smol::Async; +use std::fs::File; +use std::io::prelude::*; +use std::io::BufReader; +use std::net::SocketAddr; +use std::net::TcpListener; +use std::sync::Arc; +use rusqlite::Connection; + +use sapvi::{net, Result, Error}; +// json RPC server goes here +struct RpcInterface { + p2p: Arc, + started: Mutex, + stop_send: async_channel::Sender<()>, + stop_recv: async_channel::Receiver<()>, +} + +impl RpcInterface { + fn new(p2p: Arc) -> Arc { + let (stop_send, stop_recv) = async_channel::unbounded::<()>(); + + Arc::new(Self { + p2p, + started: Mutex::new(false), + stop_send, + stop_recv, + }) + } + + async fn db_connect() -> Connection { + let path = dirs::home_dir() + .expect("Cannot find home directory.") + .as_path() + .join(".config/darkfi/wallet.db"); + let connector = Connection::open(&path); + connector.expect("Failed to connect to database.") + } + + async fn generate_key() -> (Vec, Vec) { + let secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng); + let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret; + let pubkey = serial::serialize(&public); + let privkey = serial::serialize(&secret); + (privkey, pubkey) + } + + // TODO: fix this + async fn store_key(conn: &Connection, pubkey: Vec, privkey: Vec) -> Result<()> { + let mut db_file = File::open("wallet.sql")?; + let mut contents = String::new(); + db_file.read_to_string(&mut contents)?; + Ok(conn.execute_batch(&mut contents)?) + } + + // add new methods to handle wallet commands + async fn serve(self: Arc, mut req: Request) -> http_types::Result { + info!("RPC serving {}", req.url()); + + let request = req.body_string().await?; + + let mut io = jsonrpc_core::IoHandler::new(); + io.add_sync_method("say_hello", |_| { + Ok(jsonrpc_core::Value::String("Hello World!".into())) + }); + + let self2 = self.clone(); + io.add_method("get_info", move |_| { + let self2 = self2.clone(); + async move { + Ok(json!({ + "started": *self2.started.lock().await, + "connections": self2.p2p.connections_count().await + })) + } + }); + + let stop_send = self.stop_send.clone(); + io.add_method("stop", move |_| { + let stop_send = stop_send.clone(); + async move { + let _ = stop_send.send(()).await; + Ok(jsonrpc_core::Value::Null) + } + }); + io.add_method("key_gen", move |_| async move { + RpcInterface::db_connect().await; + let (pubkey, privkey) = RpcInterface::generate_key().await; + //println!("{}", pubkey, "{}", privkey); + Ok(jsonrpc_core::Value::Null) + }); + + let response = io + .handle_request_sync(&request) + .ok_or(sapvi::Error::BadOperationType)?; + + let mut res = Response::new(StatusCode::Ok); + res.insert_header("Content-Type", "text/plain"); + res.set_body(response); + Ok(res) + } + + async fn wait_for_quit(self: Arc) -> Result<()> { + Ok(self.stop_recv.recv().await?) + } +} + diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs new file mode 100644 index 000000000..585092d1d --- /dev/null +++ b/src/rpc/mod.rs @@ -0,0 +1,3 @@ +pub mod test; +pub mod jsonserver; + diff --git a/src/rpc/test.rs b/src/rpc/test.rs new file mode 100644 index 000000000..6ddc38c4f --- /dev/null +++ b/src/rpc/test.rs @@ -0,0 +1,3 @@ +fn foo() { +} + From f6de2257c1f5132d281a9bfb220f1fe7fa847fd2 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 15:34:09 +0200 Subject: [PATCH 4/8] created wallet module --- src/wallet/mod.rs | 1 + src/wallet/schema.sql | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 src/wallet/mod.rs create mode 100644 src/wallet/schema.sql diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs new file mode 100644 index 000000000..78d84fdb0 --- /dev/null +++ b/src/wallet/mod.rs @@ -0,0 +1 @@ +// Empty mod.rs file for now diff --git a/src/wallet/schema.sql b/src/wallet/schema.sql new file mode 100644 index 000000000..cbd99b361 --- /dev/null +++ b/src/wallet/schema.sql @@ -0,0 +1,9 @@ +ATTACH DATABASE 'wallet.db' AS wallet KEY 'testkey'; +SELECT sqlcipher_export('wallet'); +CREATE TABLE IF NOT EXISTS keys( + key_id INT PRIMARY KEY NOT NULL, + key_public BLOB NOT NULL, + key_private BLOB NOT NULL +); +CREATE INDEX IF NOT EXISTS key_public on keys(key_public); + From 6c8b1738e15b376d94e1a0846a9e2588fb74c19a Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 16:20:19 +0200 Subject: [PATCH 5/8] updated daemon to work with rpc interface --- src/bin/dfi.rs | 128 ++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/bin/dfi.rs b/src/bin/dfi.rs index ab1b4f560..a40eae9c6 100644 --- a/src/bin/dfi.rs +++ b/src/bin/dfi.rs @@ -11,7 +11,7 @@ use smol::Async; use std::net::SocketAddr; use std::net::TcpListener; use std::sync::Arc; - +use sapvi::rpc::jsonserver::RpcInterface; use sapvi::{net, Result}; /// Listens for incoming connections and serves them. @@ -73,69 +73,69 @@ async fn listen( } } -struct RpcInterface { - p2p: Arc, - started: Mutex, - stop_send: async_channel::Sender<()>, - stop_recv: async_channel::Receiver<()>, -} - -impl RpcInterface { - fn new(p2p: Arc) -> Arc { - let (stop_send, stop_recv) = async_channel::unbounded::<()>(); - - Arc::new(Self { - p2p, - started: Mutex::new(false), - stop_send, - stop_recv, - }) - } - - async fn serve(self: Arc, mut req: Request) -> http_types::Result { - info!("RPC serving {}", req.url()); - - let request = req.body_string().await?; - - let mut io = jsonrpc_core::IoHandler::new(); - io.add_sync_method("say_hello", |_| { - Ok(jsonrpc_core::Value::String("Hello World!".into())) - }); - - let self2 = self.clone(); - io.add_method("get_info", move |_| { - let self2 = self2.clone(); - async move { - Ok(json!({ - "started": *self2.started.lock().await, - "connections": self2.p2p.connections_count().await - })) - } - }); - - let stop_send = self.stop_send.clone(); - io.add_method("stop", move |_| { - let stop_send = stop_send.clone(); - async move { - let _ = stop_send.send(()).await; - Ok(jsonrpc_core::Value::Null) - } - }); - - let response = io - .handle_request_sync(&request) - .ok_or(sapvi::Error::BadOperationType)?; - - let mut res = Response::new(StatusCode::Ok); - res.insert_header("Content-Type", "text/plain"); - res.set_body(response); - Ok(res) - } - - async fn wait_for_quit(self: Arc) -> Result<()> { - Ok(self.stop_recv.recv().await?) - } -} +//struct RpcInterface { +// p2p: Arc, +// started: Mutex, +// stop_send: async_channel::Sender<()>, +// stop_recv: async_channel::Receiver<()>, +//} +// +//impl RpcInterface { +// fn new(p2p: Arc) -> Arc { +// let (stop_send, stop_recv) = async_channel::unbounded::<()>(); +// +// Arc::new(Self { +// p2p, +// started: Mutex::new(false), +// stop_send, +// stop_recv, +// }) +// } +// +// async fn serve(self: Arc, mut req: Request) -> http_types::Result { +// info!("RPC serving {}", req.url()); +// +// let request = req.body_string().await?; +// +// let mut io = jsonrpc_core::IoHandler::new(); +// io.add_sync_method("say_hello", |_| { +// Ok(jsonrpc_core::Value::String("Hello World!".into())) +// }); +// +// let self2 = self.clone(); +// io.add_method("get_info", move |_| { +// let self2 = self2.clone(); +// async move { +// Ok(json!({ +// "started": *self2.started.lock().await, +// "connections": self2.p2p.connections_count().await +// })) +// } +// }); +// +// let stop_send = self.stop_send.clone(); +// io.add_method("stop", move |_| { +// let stop_send = stop_send.clone(); +// async move { +// let _ = stop_send.send(()).await; +// Ok(jsonrpc_core::Value::Null) +// } +// }); +// +// let response = io +// .handle_request_sync(&request) +// .ok_or(sapvi::Error::BadOperationType)?; +// +// let mut res = Response::new(StatusCode::Ok); +// res.insert_header("Content-Type", "text/plain"); +// res.set_body(response); +// Ok(res) +// } +// +// async fn wait_for_quit(self: Arc) -> Result<()> { +// Ok(self.stop_recv.recv().await?) +// } +//} async fn start(executor: Arc>, options: ProgramOptions) -> Result<()> { let p2p = net::P2p::new(options.network_settings); From 4368c4837fc0c23daf67f9e06f8e90a791c019e0 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 16:22:52 +0200 Subject: [PATCH 6/8] added rpc module --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c71316a81..c34e708ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#[macro_use] extern crate clap; use bellman::groth16; use bls12_381::{Bls12, Scalar}; use std::collections::{HashMap, HashSet}; @@ -11,6 +12,7 @@ pub mod error; pub mod gfx; pub mod gui; pub mod net; +pub mod rpc; pub mod serial; pub mod service; pub mod system; From e19596d388cf79ebafd2effe24d4ad5c930df024 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 16:23:15 +0200 Subject: [PATCH 7/8] fixed dependencies to work with new architecture --- src/rpc/jsonserver.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/rpc/jsonserver.rs b/src/rpc/jsonserver.rs index 1a5aa242c..80a820fcb 100644 --- a/src/rpc/jsonserver.rs +++ b/src/rpc/jsonserver.rs @@ -1,4 +1,3 @@ -#[macro_use] extern crate clap; use async_executor::Executor; use async_native_tls::TlsAcceptor; use async_std::sync::Mutex; @@ -7,7 +6,6 @@ use ff::Field; use http_types::{Request, Response, StatusCode}; use log::*; use rand::rngs::OsRng; -use sapvi::serial; use serde_json::json; use smol::Async; use std::fs::File; @@ -17,18 +15,18 @@ use std::net::SocketAddr; use std::net::TcpListener; use std::sync::Arc; use rusqlite::Connection; +use crate::{net, serial, Result, Error}; -use sapvi::{net, Result, Error}; // json RPC server goes here -struct RpcInterface { +pub struct RpcInterface { p2p: Arc, - started: Mutex, + pub started: Mutex, stop_send: async_channel::Sender<()>, stop_recv: async_channel::Receiver<()>, } impl RpcInterface { - fn new(p2p: Arc) -> Arc { + pub fn new(p2p: Arc) -> Arc { let (stop_send, stop_recv) = async_channel::unbounded::<()>(); Arc::new(Self { @@ -65,7 +63,7 @@ impl RpcInterface { } // add new methods to handle wallet commands - async fn serve(self: Arc, mut req: Request) -> http_types::Result { + pub async fn serve(self: Arc, mut req: Request) -> http_types::Result { info!("RPC serving {}", req.url()); let request = req.body_string().await?; @@ -103,7 +101,7 @@ impl RpcInterface { let response = io .handle_request_sync(&request) - .ok_or(sapvi::Error::BadOperationType)?; + .ok_or(Error::BadOperationType)?; let mut res = Response::new(StatusCode::Ok); res.insert_header("Content-Type", "text/plain"); @@ -111,7 +109,7 @@ impl RpcInterface { Ok(res) } - async fn wait_for_quit(self: Arc) -> Result<()> { + pub async fn wait_for_quit(self: Arc) -> Result<()> { Ok(self.stop_recv.recv().await?) } } From 71631d11307875186dcacd40d71aa12d524e4177 Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 16:23:57 +0200 Subject: [PATCH 8/8] clean up old repo structure --- src/bin/wallet/darkcli.py | 114 --------- src/bin/wallet/darkd.rs | 407 ------------------------------- src/bin/wallet/rpc/__init__.py | 2 - src/bin/wallet/rpc/jsonclient.py | 53 ---- src/bin/wallet/test.rs | 86 ------- src/bin/wallet/wallet.sql | 9 - 6 files changed, 671 deletions(-) delete mode 100644 src/bin/wallet/darkcli.py delete mode 100644 src/bin/wallet/darkd.rs delete mode 100644 src/bin/wallet/rpc/__init__.py delete mode 100644 src/bin/wallet/rpc/jsonclient.py delete mode 100644 src/bin/wallet/test.rs delete mode 100644 src/bin/wallet/wallet.sql diff --git a/src/bin/wallet/darkcli.py b/src/bin/wallet/darkcli.py deleted file mode 100644 index 7dc951a07..000000000 --- a/src/bin/wallet/darkcli.py +++ /dev/null @@ -1,114 +0,0 @@ -import argparse -import requests -import json - -def arg_parser(client): - parser = argparse.ArgumentParser(prog='dark', - usage='%(prog)s [commands]', - description="""DarkFi wallet - command-line tool""") - parser.add_argument("-k", "--key", action='store_true', help="Generate a new keypair") - parser.add_argument("-i", "--info", action='store_true', help="Request info from daemon") - parser.add_argument("-s", "--stop", action='store_true', help="Send a stop signal to the daemon") - parser.add_argument("-hi", "--hello", action='store_true', help="Say hello") - args = parser.parse_args() - - if args.key: - try: - print("Attemping to generate a new key pair...") - client.key_gen(client.payload) - except Exception: - raise - - if args.info: - try: - print("Info was entered") - client.get_info(client.payload) - print("Requesting daemon info...") - except Exception: - raise - - if args.stop: - try: - print("Stop was entered") - client.stop(client.payload) - print("Sending a stop signal...") - except Exception: - raise - - if args.hello: - try: - print("Hello was entered") - client.say_hello(client.payload) - except Exception: - raise - - -class DarkClient: - # TODO: generate random ID (4 byte unsigned int) (rand range 0 - max size - # uint32 - def __init__(self): - self.url = "http://localhost:8000/" - self.payload = { - "method": [], - "params": [], - "jsonrpc": [], - "id": [], - } - - def key_gen(self, payload): - payload['method'] = "key_gen" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - key = self.__request(payload) - print(key) - - def get_info(self, payload): - payload['method'] = "get_info" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - info = self.__request(payload) - print(info) - - def stop(self, payload): - payload['method'] = "stop" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - stop = self.__request(payload) - print(stop) - - def say_hello(self, payload): - payload['method'] = "say_hello" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - hello = self.__request(payload) - print(hello) - - def __request(self, payload): - response = requests.post(self.url, json=payload).json() - # print something better - # parse into data structure - print(response) - assert response["jsonrpc"] - - -if __name__ == "__main__": - client = DarkClient() - arg_parser(client) - - #rpc() - ## Example echo method - #payload = { - # #"method:": args, - # #"method": "stop", - # "method": "get_info", - # #"method": "say_hello", - # #"params": [], - # "jsonrpc": "2.0", - # "id": 0, - #} - #response = requests.post(url, json=payload).json() - - #print(response) - #assert response["result"] == "Hello World!" - #assert response["jsonrpc"] diff --git a/src/bin/wallet/darkd.rs b/src/bin/wallet/darkd.rs deleted file mode 100644 index 88ae6156d..000000000 --- a/src/bin/wallet/darkd.rs +++ /dev/null @@ -1,407 +0,0 @@ -#[macro_use] -extern crate clap; -use async_executor::Executor; -use async_native_tls::TlsAcceptor; -use async_std::sync::Mutex; -use easy_parallel::Parallel; -use ff::Field; -use http_types::{Request, Response, StatusCode}; -use log::*; -use rand::rngs::OsRng; -use sapvi::serial; -use serde_json::json; -use smol::Async; -use std::fs::File; -use std::io::prelude::*; -use std::io::BufReader; -use std::net::SocketAddr; -use std::net::TcpListener; -use std::sync::Arc; -use rusqlite::Connection; - -use sapvi::{net, Result, Error}; - -/// Listens for incoming connections and serves them. -async fn listen( - executor: Arc>, - rpc: Arc, - listener: Async, - tls: Option, -) -> Result<()> { - // Format the full host address. - let host = match &tls { - None => format!("http://{}", listener.get_ref().local_addr()?), - Some(_) => format!("https://{}", listener.get_ref().local_addr()?), - }; - println!("Listening on {}", host); - - loop { - // Accept the next connection. - let (stream, _) = listener.accept().await?; - - // Spawn a background task serving this connection. - let task = match &tls { - None => { - let stream = async_dup::Arc::new(stream); - let rpc = rpc.clone(); - executor.spawn(async move { - if let Err(err) = async_h1::accept(stream, move |req| { - let rpc = rpc.clone(); - rpc.serve(req) - }) - .await - { - println!("Connection error: {:#?}", err); - } - }) - } - Some(tls) => { - // In case of HTTPS, establish a secure TLS connection first. - match tls.accept(stream).await { - Ok(stream) => { - let _stream = async_dup::Arc::new(async_dup::Mutex::new(stream)); - executor.spawn(async move { - /*if let Err(err) = async_h1::accept(stream, serve).await { - println!("Connection error: {:#?}", err); - }*/ - unimplemented!(); - }) - } - Err(err) => { - println!("Failed to establish secure TLS connection: {:#?}", err); - continue; - } - } - } - }; - - // Detach the task to let it run in the background. - task.detach(); - } -} - -struct RpcInterface { - p2p: Arc, - started: Mutex, - stop_send: async_channel::Sender<()>, - stop_recv: async_channel::Receiver<()>, -} - -impl RpcInterface { - fn new(p2p: Arc) -> Arc { - let (stop_send, stop_recv) = async_channel::unbounded::<()>(); - - Arc::new(Self { - p2p, - started: Mutex::new(false), - stop_send, - stop_recv, - }) - } - - async fn db_connect() -> Connection { - let path = dirs::home_dir() - .expect("Cannot find home directory.") - .as_path() - .join(".config/darkfi/wallet.db"); - let connector = Connection::open(&path); - connector.expect("Failed to connect to database.") - } - - async fn generate_key() -> (Vec, Vec) { - let secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng); - let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret; - let pubkey = serial::serialize(&public); - let privkey = serial::serialize(&secret); - (privkey, pubkey) - } - - // TODO: fix this - async fn store_key(conn: &Connection, pubkey: Vec, privkey: Vec) -> Result<()> { - let mut db_file = File::open("wallet.sql")?; - let mut contents = String::new(); - db_file.read_to_string(&mut contents)?; - Ok(conn.execute_batch(&mut contents)?) - } - - // add new methods to handle wallet commands - async fn serve(self: Arc, mut req: Request) -> http_types::Result { - info!("RPC serving {}", req.url()); - - let request = req.body_string().await?; - - let mut io = jsonrpc_core::IoHandler::new(); - io.add_sync_method("say_hello", |_| { - Ok(jsonrpc_core::Value::String("Hello World!".into())) - }); - - let self2 = self.clone(); - io.add_method("get_info", move |_| { - let self2 = self2.clone(); - async move { - Ok(json!({ - "started": *self2.started.lock().await, - "connections": self2.p2p.connections_count().await - })) - } - }); - - let stop_send = self.stop_send.clone(); - io.add_method("stop", move |_| { - let stop_send = stop_send.clone(); - async move { - let _ = stop_send.send(()).await; - Ok(jsonrpc_core::Value::Null) - } - }); - io.add_method("key_gen", move |_| async move { - RpcInterface::db_connect().await; - let (pubkey, privkey) = RpcInterface::generate_key().await; - //println!("{}", pubkey, "{}", privkey); - Ok(jsonrpc_core::Value::Null) - }); - - let response = io - .handle_request_sync(&request) - .ok_or(sapvi::Error::BadOperationType)?; - - let mut res = Response::new(StatusCode::Ok); - res.insert_header("Content-Type", "text/plain"); - res.set_body(response); - Ok(res) - } - - async fn wait_for_quit(self: Arc) -> Result<()> { - Ok(self.stop_recv.recv().await?) - } -} - -async fn start(executor: Arc>, options: ProgramOptions) -> Result<()> { - let p2p = net::P2p::new(options.network_settings); - - let rpc = RpcInterface::new(p2p.clone()); - let http = listen( - executor.clone(), - rpc.clone(), - Async::::bind(([127, 0, 0, 1], options.rpc_port))?, - None, - ); - - let http_task = executor.spawn(http); - - *rpc.started.lock().await = true; - - p2p.clone().start(executor.clone()).await?; - - p2p.run(executor).await?; - - rpc.wait_for_quit().await?; - - http_task.cancel().await; - - Ok(()) -} - -/* -async fn start2(executor: Arc>, options: ProgramOptions) -> Result<()> { - let connections = Arc::new(Mutex::new(HashMap::new())); - - let stored_addrs = Arc::new(Mutex::new(Vec::new())); - - let executor2 = executor.clone(); - let stored_addrs2 = stored_addrs.clone(); - - let mut server_task = None; - if let Some(accept_addr) = options.accept_addr { - let accept_addr = accept_addr.clone(); - - let protocol = ServerProtocol::new(connections.clone(), accept_addr, stored_addrs2); - server_task = Some(executor.spawn(async move { - protocol.start(executor2).await?; - Ok::<(), sapvi::Error>(()) - })); - } - - let mut seed_protocols = Vec::with_capacity(options.seed_addrs.len()); - - // Normally we query this from a server - let accept_addr = options.accept_addr.clone(); - - for seed_addr in options.seed_addrs.iter() { - let protocol = SeedProtocol::new(seed_addr.clone(), accept_addr, stored_addrs.clone()); - protocol.clone().start(executor.clone()).await; - seed_protocols.push(protocol); - } - - debug!("Waiting for seed node queries to finish..."); - - for seed_protocol in seed_protocols { - seed_protocol.await_finish().await; - } - - debug!("Seed nodes queried."); - - let mut client_slots = vec![]; - for i in 0..options.connection_slots { - debug!("Starting connection slot {}", i); - - let client = Channel::new( - connections.clone(), - accept_addr.clone(), - stored_addrs.clone(), - ); - client.clone().start(executor.clone()).await; - client_slots.push(client); - } - - for remote_addr in options.manual_connects { - debug!("Starting connection (manual) to {}", remote_addr); - - let client = Channel::new( - connections.clone(), - accept_addr.clone(), - stored_addrs.clone(), - ); - client - .clone() - .start_manual(remote_addr, executor.clone()) - .await; - client_slots.push(client); - } - - let rpc = RpcInterface::new(); - let http = listen( - executor.clone(), - rpc.clone(), - Async::::bind(([127, 0, 0, 1], 8000))?, - None, - ); - - let http_task = executor.spawn(http); - - rpc.stop_recv.recv().await?; - - http_task.cancel().await; - - match server_task { - None => {} - Some(server_task) => { - server_task.cancel().await; - } - } - Ok(()) -} -*/ - -struct ProgramOptions { - network_settings: net::Settings, - log_path: Box, - rpc_port: u16, -} - -impl ProgramOptions { - fn load() -> Result { - let app = clap_app!(dfi => - (version: "0.1.0") - (author: "Amir Taaki ") - (about: "Dark node") - (@arg ACCEPT: -a --accept +takes_value "Accept address") - (@arg SEED_NODES: -s --seeds ... "Seed nodes") - (@arg CONNECTS: -c --connect ... "Manual connections") - (@arg CONNECT_SLOTS: --slots +takes_value "Connection slots") - (@arg LOG_PATH: --log +takes_value "Logfile path") - (@arg RPC_PORT: -r --rpc +takes_value "RPC port") - ) - .get_matches(); - - let accept_addr = if let Some(accept_addr) = app.value_of("ACCEPT") { - Some(accept_addr.parse()?) - } else { - None - }; - - let mut seed_addrs: Vec = vec![]; - if let Some(seeds) = app.values_of("SEED_NODES") { - for seed in seeds { - seed_addrs.push(seed.parse()?); - } - } - - let mut manual_connects: Vec = vec![]; - if let Some(connections) = app.values_of("CONNECTS") { - for connect in connections { - manual_connects.push(connect.parse()?); - } - } - - let connection_slots = if let Some(connection_slots) = app.value_of("CONNECT_SLOTS") { - connection_slots.parse()? - } else { - 0 - }; - - let log_path = Box::new( - if let Some(log_path) = app.value_of("LOG_PATH") { - std::path::Path::new(log_path) - } else { - std::path::Path::new("/tmp/darkfid.log") - } - .to_path_buf(), - ); - - let rpc_port = if let Some(rpc_port) = app.value_of("RPC_PORT") { - rpc_port.parse()? - } else { - 8000 - }; - - Ok(ProgramOptions { - network_settings: net::Settings { - inbound: accept_addr, - outbound_connections: connection_slots, - external_addr: accept_addr, - peers: manual_connects, - seeds: seed_addrs, - ..Default::default() - }, - log_path, - rpc_port, - }) - } -} - -fn main() -> Result<()> { - use simplelog::*; - - let options = ProgramOptions::load()?; - - let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build(); - - CombinedLogger::init(vec![ - TermLogger::new(LevelFilter::Debug, logger_config, TerminalMode::Mixed).unwrap(), - WriteLogger::new( - LevelFilter::Debug, - Config::default(), - std::fs::File::create(options.log_path.as_path()).unwrap(), - ), - ]) - .unwrap(); - - let ex = Arc::new(Executor::new()); - let (signal, shutdown) = async_channel::unbounded::<()>(); - 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, options).await?; - drop(signal); - Ok::<(), sapvi::Error>(()) - }) - }); - - result -} diff --git a/src/bin/wallet/rpc/__init__.py b/src/bin/wallet/rpc/__init__.py deleted file mode 100644 index 2fbf57db8..000000000 --- a/src/bin/wallet/rpc/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from rpc import jsonclient -from .jsonclient import RpcClient diff --git a/src/bin/wallet/rpc/jsonclient.py b/src/bin/wallet/rpc/jsonclient.py deleted file mode 100644 index cb1775181..000000000 --- a/src/bin/wallet/rpc/jsonclient.py +++ /dev/null @@ -1,53 +0,0 @@ -import requests - -# TODO: generate random ID (4 byte unsigned int) (rand range 0 - max size uint32) -# TODO: make functions async -# TODO: parse json replies into something more legible - -class RpcClient: - def __init__(self): - self.url = "http://localhost:8000/" - self.payload = { - "method": [], - "params": [], - "jsonrpc": [], - "id": [], - } - - def key_gen(self, payload): - payload['method'] = "key_gen" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - key = self.__request(payload) - print(key) - - def get_info(self, payload): - payload['method'] = "get_info" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - info = self.__request(payload) - print(info) - - def stop(self, payload): - payload['method'] = "stop" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - stop = self.__request(payload) - print(stop) - - def say_hello(self, payload): - payload['method'] = "say_hello" - payload['jsonrpc'] = "2.0" - payload['id'] = "0" - hello = self.__request(payload) - print(hello) - - def __request(self, payload): - response = requests.post(self.url, json=payload).json() - print(response) - assert response["jsonrpc"] - - -if __name__ == "__main__": - client = RpcClient() - diff --git a/src/bin/wallet/test.rs b/src/bin/wallet/test.rs deleted file mode 100644 index eddf81387..000000000 --- a/src/bin/wallet/test.rs +++ /dev/null @@ -1,86 +0,0 @@ -// rocksdb is the blockchain database -// it is a key value store -// sqlite is the encrypted wallet - -use rocksdb::DB; -use rusqlite::{Connection, Result}; -use std::path::{Path, PathBuf}; - -fn main() -> Result<()> { - wallet()?; - blockchain()?; - Ok(()) -} - -fn wallet() -> Result<()> { - let connector = connect()?; - encrypt(&connector)?; - println!("Created encrypted database."); - decrypt(&connector)?; - println!("Decrypted database."); - Ok(()) -} - -fn connect() -> Result { - println!("Attempting to establish a connection..."); - let path = dirs::home_dir() - .expect("Cannot find home directory!") - .as_path() - .join(".config/darkfi/wallet.db"); - let connector = Connection::open(&path); - println!("Connection established"); - connector -} - -fn encrypt(conn: &Connection) -> Result<()> { - println!("Attempting to create an encrypted database..."); - conn.execute_batch( - "ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'testkey'; - SELECT sqlcipher_export('encrypted'); - DETACH DATABASE encrypted;", - ) -} - -fn decrypt(conn: &Connection) -> Result<()> { - println!("Attempting to decrypt database..."); - conn.execute_batch( - "ATTACH DATABASE 'plaintext.db' AS plaintext KEY 'testkey'; - SELECT sqlcipher_export('plaintext'); - DETACH DATABASE plaintext;", - ) -} - -fn blockchain() -> Result<()> { - let db = create_db(); - write_db(&db)?; - test_db(&db); - Ok(()) -} - -fn create_db() -> DB { - println!("Creating a blockchain database..."); - let path = dirs::home_dir() - .expect("Cannot find home directory!") - .as_path() - .join(".config/darkfi/chain"); - let db = DB::open_default(path).unwrap(); - db -} - -fn write_db(db: &DB) -> Result<()> { - println!("Writing to the blockchain..."); - db.put(b"test-value", b"test-key").unwrap(); - Ok(()) -} - -fn test_db(db: &DB) { - println!("Testing if write was successful..."); - match db.get(b"test-value") { - Ok(Some(value)) => println!("retrieved value {}", String::from_utf8(value).unwrap()), - Ok(None) => println!("value not found"), - Err(e) => println!("operational problem encountered: {}", e), - } -} - -// TODO: macro to load file as a string. load wallet tables in sqlite at run -// Table includes: maintain a list of coins and whether they are spent diff --git a/src/bin/wallet/wallet.sql b/src/bin/wallet/wallet.sql deleted file mode 100644 index cbd99b361..000000000 --- a/src/bin/wallet/wallet.sql +++ /dev/null @@ -1,9 +0,0 @@ -ATTACH DATABASE 'wallet.db' AS wallet KEY 'testkey'; -SELECT sqlcipher_export('wallet'); -CREATE TABLE IF NOT EXISTS keys( - key_id INT PRIMARY KEY NOT NULL, - key_public BLOB NOT NULL, - key_private BLOB NOT NULL -); -CREATE INDEX IF NOT EXISTS key_public on keys(key_public); -