mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
darkfid: More WIP for new RPC.
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
52
bin/darkfid2/src/client.rs
Normal file
52
bin/darkfid2/src/client.rs
Normal 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
34
bin/darkfid2/src/error.rs
Normal 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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
Reference in New Issue
Block a user