From e8a400def27c39f062368b48ac54e9bb3d439dbb Mon Sep 17 00:00:00 2001 From: rachel-rose Date: Tue, 11 May 2021 15:33:43 +0200 Subject: [PATCH] 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() { +} +