darkfid: More WIP for new RPC.

This commit is contained in:
parazyd
2022-04-13 15:03:10 +02:00
parent f42e935700
commit ecca1a26ce
7 changed files with 226 additions and 33 deletions

7
Cargo.lock generated
View File

@@ -1728,6 +1728,7 @@ dependencies = [
"darkfi",
"easy-parallel",
"futures-lite",
"lazy-init",
"log",
"serde",
"serde_derive",
@@ -3198,6 +3199,12 @@ dependencies = [
"log",
]
[[package]]
name = "lazy-init"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23517540be87a91d06324e6bf6286ba8214171123ee8862ae9a5e7d938d71815"
[[package]]
name = "lazy_static"
version = "1.4.0"

View File

@@ -13,9 +13,10 @@ async-channel = "1.6.1"
async-executor = "1.4.1"
async-std = "1.11.0"
async-trait = "0.1.53"
darkfi = {path = "../../", features = ["crypto", "rpc", "net"]}
darkfi = {path = "../../", features = ["blockchain2", "wallet", "rpc", "net"]}
easy-parallel = "3.2.0"
futures-lite = "1.12.0"
lazy-init = "0.5.0"
log = "0.4.16"
serde_json = "1.0.79"
simplelog = "0.12.0-alpha1"

View File

@@ -9,6 +9,9 @@
# Path to the wallet database
#wallet_path = "~/.config/darkfi/darkfid_wallet.db"
# Wallet password
wallet_pass = "changeme"
# JSON-RPC listening url
#rpc_listen = "tcp://127.0.0.1:5397"
#rpc_listen = "tls://127.0.0.1:5397"

View File

@@ -0,0 +1,52 @@
use lazy_init::Lazy;
use log::info;
use darkfi::{
crypto::{address::Address, keypair::Keypair, proof::ProvingKey},
wallet::walletdb::WalletPtr,
Result,
};
pub struct Client {
main_keypair: Keypair,
wallet: WalletPtr,
mint_pk: Lazy<ProvingKey>,
burn_pk: Lazy<ProvingKey>,
}
impl Client {
pub async fn new(wallet: WalletPtr) -> Result<Self> {
// Initialize or load the wallet
wallet.init_db().await?;
// Check if there is a default keypair and generate one in
// case we don't have any.
if wallet.get_default_keypair().await.is_err() {
// TODO: Clean this up with Option<T> to have less calls.
if wallet.get_keypairs().await?.is_empty() {
wallet.keygen().await?;
}
wallet.set_default_keypair(&wallet.get_keypairs().await?[0].public).await?;
}
// Generate Merkle Tree if we don't have one.
// if wallet.get_tree().await.is_err() {
// wallet.tree_gen().await?;
// }
let main_keypair = wallet.get_default_keypair().await?;
info!(target: "CLIENT", "Main keypair: {}", Address::from(main_keypair.public));
Ok(Self { main_keypair, wallet, mint_pk: Lazy::new(), burn_pk: Lazy::new() })
}
pub async fn keygen(&self) -> Result<Address> {
let kp = self.wallet.keygen().await?;
Ok(Address::from(kp.public))
}
pub async fn get_keypairs(&self) -> Result<Vec<Keypair>> {
self.wallet.get_keypairs().await
}
}

34
bin/darkfid2/src/error.rs Normal file
View File

@@ -0,0 +1,34 @@
use serde_json::Value;
use darkfi::rpc::{
jsonrpc,
jsonrpc::{ErrorCode::ServerError, JsonResult},
};
const ERROR_KEYGEN: i64 = -32101;
const ERROR_NAN: i64 = -32102;
const ERROR_LT1: i64 = -32103;
const ERROR_KP_FETCH: i64 = -32104;
pub fn err_keygen(id: Value) -> JsonResult {
jsonrpc::error(ServerError(ERROR_KEYGEN), Some("Failed generating keypair".to_string()), id)
.into()
}
pub fn err_nan(id: Value) -> JsonResult {
jsonrpc::error(ServerError(ERROR_NAN), Some("Not a number".to_string()), id).into()
}
pub fn err_lt1(id: Value) -> JsonResult {
jsonrpc::error(ServerError(ERROR_LT1), Some("Number cannot be lower than -1".to_string()), id)
.into()
}
pub fn err_kp_fetch(id: Value) -> JsonResult {
jsonrpc::error(
ServerError(ERROR_KP_FETCH),
Some("Failed fetching keypairs from wallet".to_string()),
id,
)
.into()
}

View File

@@ -3,6 +3,7 @@ use async_std::sync::Arc;
use async_trait::async_trait;
use easy_parallel::Parallel;
use futures_lite::future;
use log::error;
use serde_derive::Deserialize;
use serde_json::{json, Value};
use simplelog::{ColorChoice, TermLogger, TerminalMode};
@@ -12,7 +13,7 @@ use url::Url;
use darkfi::{
cli_desc,
net::transport::{TcpTransport, TlsTransport},
crypto::address::Address,
rpc::{
jsonrpc,
jsonrpc::{ErrorCode, JsonRequest, JsonResult},
@@ -20,11 +21,19 @@ use darkfi::{
},
util::{
cli::{log_config, spawn_config},
expand_path,
path::get_config_path,
},
Error, Result,
wallet::walletdb::{WalletDb, WalletPtr},
Result,
};
mod client;
use client::Client;
mod error;
use error::*;
const CONFIG_FILE: &str = "darkfid_config.toml";
const CONFIG_FILE_CONTENTS: &str = include_str!("../darkfid_config.toml");
@@ -36,76 +45,163 @@ struct Args {
/// Configuration file to use
config: Option<String>,
#[structopt(long, default_value = "~/.config/darkfi/darkfid_client.db")]
/// Path to the client database
database_path: String,
#[structopt(long, default_value = "~/.config/darkfi/darkfid_wallet.db")]
/// Path to the wallet database
wallet_path: String,
#[structopt(long, default_value = "changeme")]
/// Password for the wallet database
wallet_pass: String,
#[structopt(long, default_value = "tcp://127.0.0.1:5397")]
/// JSON-RPC listen URL
rpc_listen: String,
rpc_listen: Url,
#[structopt(short, parse(from_occurrences))]
/// Increase verbosity (-vvv supported)
verbose: u8,
}
pub struct Darkfid;
pub struct Darkfid {
client: Client,
}
#[async_trait]
impl RequestHandler for Darkfid {
async fn handle_request(&self, req: JsonRequest) -> JsonResult {
if req.params.as_array().is_none() {
if !req.params.is_array() {
return jsonrpc::error(ErrorCode::InvalidParams, None, req.id).into()
}
let params = req.params.as_array().unwrap();
match req.method.as_str() {
Some("ping") => return self.pong(req.id, req.params).await,
Some("ping") => return self.pong(req.id, params).await,
Some("keygen") => return self.keygen(req.id, params).await,
Some("get_key") => return self.get_key(req.id, params).await,
Some(_) | None => return jsonrpc::error(ErrorCode::MethodNotFound, None, req.id).into(),
}
}
}
impl Darkfid {
pub async fn new(wallet: WalletPtr) -> Result<Self> {
let client = Client::new(wallet).await?;
Ok(Self { client })
}
// RPCAPI:
// Returns a `pong` to the `ping` request.
// --> {"jsonrpc":"2.0","method":"ping","params":[],"id":1}
// <-- {"jsonrpc":"2.0","result":"pong","id":1}
async fn pong(&self, id: Value, _params: Value) -> JsonResult {
// --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 1}
// <-- {"jsonrpc": "2.0", "result": "pong", "id": 1}
async fn pong(&self, id: Value, _params: &[Value]) -> JsonResult {
jsonrpc::response(json!("pong"), id).into()
}
// RPCAPI:
// Attempts to generate a new keypair and returns its address upon success.
// --> {"jsonrpc": "2.0", "method": "keygen", "params": [], "id": 1}
// <-- {"jsonrpc": "2.0", "result": "1DarkFi...", "id": 1}
async fn keygen(&self, id: Value, _params: &[Value]) -> JsonResult {
match self.client.keygen().await {
Ok(a) => jsonrpc::response(json!(a.to_string()), id).into(),
Err(e) => {
error!("Failed creating keypair: {}", e);
err_keygen(id)
}
}
}
// RPCAPI:
// Fetches a keypair by given indexes from the wallet and returns it in an
// encoded format. `-1` is supported to fetch all available keys.
// --> {"jsonrpc": "2.0", "method": "get_key", "params": [1, 2], "id": 1}
// <-- {"jsonrpc": "2.0", "result": ["foo", "bar"], "id": 1}
async fn get_key(&self, id: Value, params: &[Value]) -> JsonResult {
let mut fetch_all = false;
for i in params {
if !i.is_i64() {
return err_nan(id)
}
if i.as_i64() == Some(-1) {
fetch_all = true;
break
}
if i.as_i64() < Some(-1) {
return err_lt1(id)
}
}
let keypairs = match self.client.get_keypairs().await {
Ok(v) => v,
Err(e) => {
error!("Failed fetching keypairs: {}", e);
return err_kp_fetch(id)
}
};
let mut ret = vec![];
if fetch_all {
ret = keypairs.iter().map(|x| Some(Address::from(x.public).to_string())).collect()
} else {
for i in params {
// This cast is safe since we've already sorted out
// all negative cases above.
let idx = i.as_i64().unwrap() as usize;
if let Some(kp) = keypairs.get(idx) {
ret.push(Some(Address::from(kp.public).to_string()));
} else {
ret.push(None)
}
}
}
jsonrpc::response(json!(ret), id).into()
}
}
async fn init_wallet(wallet_path: &str, wallet_pass: &str) -> Result<WalletPtr> {
let expanded = expand_path(wallet_path)?;
let wallet_path = format!("sqlite://{}", expanded.to_str().unwrap());
let wallet = WalletDb::new(&wallet_path, wallet_pass).await?;
Ok(wallet)
}
fn main() -> Result<()> {
let args = Args::from_args_with_toml("").unwrap();
let cfg_path = get_config_path(args.config.clone(), CONFIG_FILE)?;
let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
spawn_config(&cfg_path, CONFIG_FILE_CONTENTS.as_bytes())?;
let args = Args::from_args_with_toml(&std::fs::read_to_string(cfg_path)?).unwrap();
let (lvl, conf) = log_config(args.verbose.into())?;
TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
// Validate args
// Initialize or load wallet
let ex = Executor::new();
let wallet = future::block_on(ex.run(init_wallet(&args.wallet_path, &args.wallet_pass)))?;
// Initialize state
let darkfid = future::block_on(ex.run(Darkfid::new(wallet)))?;
let darkfid = Arc::new(darkfid);
drop(ex);
// https://docs.rs/smol/latest/smol/struct.Executor.html#examples
let ex = Executor::new();
let (signal, shutdown) = async_channel::unbounded::<()>();
let (_, result) = Parallel::new()
// Run four executor threads
.each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
// Run the main future on the current thread.
.finish(|| {
future::block_on(async {
listen_and_serve(args.rpc_listen, darkfid).await?;
drop(signal);
Ok::<(), darkfi::Error>(())
})
});
// // https://docs.rs/smol/latest/smol/struct.Executor.html#examples
// let (_, result) = Parallel::new()
// // Run four executor threads
// .each(0..4, |_| future::block_on(ex.run(shutdown.recv())))
// // Run the main future on the current thread.
// .finish(|| {
// future::block_on(async {
// realmain(args).await?;
// drop(signal);
// Ok::<(), darkfi::Error>(())
// })
// });
// result
Ok(())
result
}

View File

@@ -94,11 +94,11 @@ impl WalletDb {
Ok(())
}
pub async fn key_gen(&self) -> Result<()> {
pub async fn keygen(&self) -> Result<Keypair> {
debug!("Attempting to generate keypairs");
let keypair = Keypair::random(&mut OsRng);
self.put_keypair(&keypair).await?;
Ok(())
Ok(keypair)
}
pub async fn put_keypair(&self, keypair: &Keypair) -> Result<()> {