|
@@ -6,10 +6,9 @@ use async_trait::async_trait;
|
|
|
use easy_parallel::Parallel;
|
|
use easy_parallel::Parallel;
|
|
|
use futures_lite::future;
|
|
use futures_lite::future;
|
|
|
use lazy_init::Lazy;
|
|
use lazy_init::Lazy;
|
|
|
-use log::{debug, error, info};
|
|
|
|
|
|
|
+use log::{error, info};
|
|
|
use rand::Rng;
|
|
use rand::Rng;
|
|
|
use serde_derive::Deserialize;
|
|
use serde_derive::Deserialize;
|
|
|
-use serde_json::{json, Value};
|
|
|
|
|
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
|
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
|
|
use structopt::StructOpt;
|
|
use structopt::StructOpt;
|
|
|
use structopt_toml::StructOptToml;
|
|
use structopt_toml::StructOptToml;
|
|
@@ -29,17 +28,13 @@ use darkfi::{
|
|
|
util::Timestamp,
|
|
util::Timestamp,
|
|
|
ValidatorState, MAINNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_HASH_BYTES,
|
|
ValidatorState, MAINNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_HASH_BYTES,
|
|
|
},
|
|
},
|
|
|
- crypto::{
|
|
|
|
|
- address::Address,
|
|
|
|
|
- keypair::{Keypair, PublicKey, SecretKey},
|
|
|
|
|
- },
|
|
|
|
|
net,
|
|
net,
|
|
|
net::P2pPtr,
|
|
net::P2pPtr,
|
|
|
node::{Client, State},
|
|
node::{Client, State},
|
|
|
rpc::{
|
|
rpc::{
|
|
|
jsonrpc,
|
|
jsonrpc,
|
|
|
jsonrpc::{
|
|
jsonrpc::{
|
|
|
- ErrorCode::{InternalError, InvalidParams, MethodNotFound},
|
|
|
|
|
|
|
+ ErrorCode::{InvalidParams, MethodNotFound},
|
|
|
JsonRequest, JsonResult,
|
|
JsonRequest, JsonResult,
|
|
|
},
|
|
},
|
|
|
rpcserver2::{listen_and_serve, RequestHandler},
|
|
rpcserver2::{listen_and_serve, RequestHandler},
|
|
@@ -149,6 +144,11 @@ pub struct Darkfid {
|
|
|
state: Arc<Mutex<State>>,
|
|
state: Arc<Mutex<State>>,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// JSON-RPC methods
|
|
|
|
|
+mod rpc_blockchain;
|
|
|
|
|
+mod rpc_misc;
|
|
|
|
|
+mod rpc_wallet;
|
|
|
|
|
+
|
|
|
#[async_trait]
|
|
#[async_trait]
|
|
|
impl RequestHandler for Darkfid {
|
|
impl RequestHandler for Darkfid {
|
|
|
async fn handle_request(&self, req: JsonRequest) -> JsonResult {
|
|
async fn handle_request(&self, req: JsonRequest) -> JsonResult {
|
|
@@ -160,12 +160,14 @@ impl RequestHandler for Darkfid {
|
|
|
|
|
|
|
|
match req.method.as_str() {
|
|
match req.method.as_str() {
|
|
|
Some("ping") => return self.pong(req.id, 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("export_keypair") => return self.export_keypair(req.id, params).await,
|
|
|
|
|
- Some("import_keypair") => return self.import_keypair(req.id, params).await,
|
|
|
|
|
- Some("set_default_address") => return self.set_default_address(req.id, params).await,
|
|
|
|
|
- Some("get_slot") => return self.get_slot(req.id, params).await,
|
|
|
|
|
|
|
+ Some("blockchain.get_slot") => return self.get_slot(req.id, params).await,
|
|
|
|
|
+ Some("wallet.keygen") => return self.keygen(req.id, params).await,
|
|
|
|
|
+ Some("wallet.get_key") => return self.get_key(req.id, params).await,
|
|
|
|
|
+ Some("wallet.export_keypair") => return self.export_keypair(req.id, params).await,
|
|
|
|
|
+ Some("wallet.import_keypair") => return self.import_keypair(req.id, params).await,
|
|
|
|
|
+ Some("wallet.set_default_address") => {
|
|
|
|
|
+ return self.set_default_address(req.id, params).await
|
|
|
|
|
+ }
|
|
|
Some(_) | None => return jsonrpc::error(MethodNotFound, None, req.id).into(),
|
|
Some(_) | None => return jsonrpc::error(MethodNotFound, None, req.id).into(),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -205,216 +207,6 @@ impl Darkfid {
|
|
|
state,
|
|
state,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 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::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);
|
|
|
|
|
- server_error(RpcError::Keygen, id)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // RPCAPI:
|
|
|
|
|
- // Fetches public keys 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 {
|
|
|
|
|
- if params.is_empty() {
|
|
|
|
|
- return jsonrpc::error(InvalidParams, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let mut fetch_all = false;
|
|
|
|
|
- for i in params {
|
|
|
|
|
- if !i.is_i64() {
|
|
|
|
|
- return server_error(RpcError::Nan, id)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if i.as_i64() == Some(-1) {
|
|
|
|
|
- fetch_all = true;
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if i.as_i64() < Some(-1) {
|
|
|
|
|
- return server_error(RpcError::LessThanNegOne, id)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let keypairs = match self.client.get_keypairs().await {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed fetching keypairs: {}", e);
|
|
|
|
|
- return server_error(RpcError::KeypairFetch, 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 on 64bit 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()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // RPCAPI:
|
|
|
|
|
- // Exports the given keypair index.
|
|
|
|
|
- // Returns the encoded secret key upon success.
|
|
|
|
|
- // --> {"jsonrpc": "2.0", "method": "export_keypair", "params": [0], "id": 1}
|
|
|
|
|
- // <-- {"jsonrpc": "2.0", "result": "foobar", "id": 1}
|
|
|
|
|
- async fn export_keypair(&self, id: Value, params: &[Value]) -> JsonResult {
|
|
|
|
|
- if params.len() != 1 || !params[0].is_u64() {
|
|
|
|
|
- return jsonrpc::error(InvalidParams, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let keypairs = match self.client.get_keypairs().await {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed fetching keypairs: {}", e);
|
|
|
|
|
- return server_error(RpcError::KeypairFetch, id)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- if let Some(kp) = keypairs.get(params[0].as_u64().unwrap() as usize) {
|
|
|
|
|
- return jsonrpc::response(json!(kp.secret.to_bytes()), id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- server_error(RpcError::KeypairNotFound, id)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // RPCAPI:
|
|
|
|
|
- // Imports a given secret key into the wallet as a keypair.
|
|
|
|
|
- // Returns the public counterpart as the result upon success.
|
|
|
|
|
- // --> {"jsonrpc": "2.0", "method": "import_keypair", "params": ["foobar"], "id": 1}
|
|
|
|
|
- // <-- {"jsonrpc": "2.0", "result": "pubfoobar", "id": 1}
|
|
|
|
|
- async fn import_keypair(&self, id: Value, params: &[Value]) -> JsonResult {
|
|
|
|
|
- if params.len() != 1 || !params[0].is_string() {
|
|
|
|
|
- return jsonrpc::error(InvalidParams, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let bytes: [u8; 32] = match serde_json::from_str(params[0].as_str().unwrap()) {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed parsing secret key from string: {}", e);
|
|
|
|
|
- return server_error(RpcError::InvalidKeypair, id)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let secret = match SecretKey::from_bytes(bytes) {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed parsing secret key from string: {}", e);
|
|
|
|
|
- return server_error(RpcError::InvalidKeypair, id)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let public = PublicKey::from_secret(secret);
|
|
|
|
|
- let keypair = Keypair { secret, public };
|
|
|
|
|
- let address = Address::from(public).to_string();
|
|
|
|
|
-
|
|
|
|
|
- match self.client.put_keypair(&keypair).await {
|
|
|
|
|
- Ok(()) => {}
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed inserting keypair into wallet: {}", e);
|
|
|
|
|
- return jsonrpc::error(InternalError, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- jsonrpc::response(json!(address), id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // RPCAPI:
|
|
|
|
|
- // Sets the default wallet address to the given index.
|
|
|
|
|
- // Returns `true` upon success.
|
|
|
|
|
- // --> {"jsonrpc": "2.0", "method": "set_default_address", "params": [2], "id": 1}
|
|
|
|
|
- // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
|
|
|
|
|
- async fn set_default_address(&self, id: Value, params: &[Value]) -> JsonResult {
|
|
|
|
|
- if params.len() != 1 || !params[0].is_u64() {
|
|
|
|
|
- return jsonrpc::error(InvalidParams, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let idx = params[0].as_u64().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- let keypairs = match self.client.get_keypairs().await {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed fetching keypairs: {}", e);
|
|
|
|
|
- return server_error(RpcError::KeypairFetch, id)
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- if keypairs.len() as u64 != idx - 1 {
|
|
|
|
|
- return server_error(RpcError::KeypairNotFound, id)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let kp = keypairs[idx as usize];
|
|
|
|
|
- match self.client.set_default_keypair(&kp.public).await {
|
|
|
|
|
- Ok(()) => {}
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed setting default keypair: {}", e);
|
|
|
|
|
- return jsonrpc::error(InternalError, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- jsonrpc::response(json!(true), id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // RPCAPI:
|
|
|
|
|
- // Queries the blockchain database for a block in the given slot.
|
|
|
|
|
- // Returns a readable block upon success.
|
|
|
|
|
- // --> {"jsonrpc": "2.0", "method": "get_slot", "params": [0], "id": 1}
|
|
|
|
|
- // <-- {"jsonrpc": "2.0", "result": {...}, "id": 1}
|
|
|
|
|
- async fn get_slot(&self, id: Value, params: &[Value]) -> JsonResult {
|
|
|
|
|
- if params.len() != 1 || !params[0].is_u64() {
|
|
|
|
|
- return jsonrpc::error(InvalidParams, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let blocks = match self
|
|
|
|
|
- .validator_state
|
|
|
|
|
- .read()
|
|
|
|
|
- .await
|
|
|
|
|
- .blockchain
|
|
|
|
|
- .get_blocks_by_slot(&[params[0].as_u64().unwrap()])
|
|
|
|
|
- {
|
|
|
|
|
- Ok(v) => v,
|
|
|
|
|
- Err(e) => {
|
|
|
|
|
- error!("Failed fetching block by slot: {}", e);
|
|
|
|
|
- return jsonrpc::error(InternalError, None, id).into()
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- if blocks.is_empty() {
|
|
|
|
|
- return server_error(RpcError::UnknownSlot, id)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- debug!("{:#?}", blocks[0]);
|
|
|
|
|
- jsonrpc::response(json!(true), id).into()
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async_daemonize!(realmain);
|
|
async_daemonize!(realmain);
|
|
@@ -454,7 +246,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'_>>) -> Result<()> {
|
|
|
let state = ValidatorState::new(&sled_db, id, genesis_ts, genesis_data)?;
|
|
let state = ValidatorState::new(&sled_db, id, genesis_ts, genesis_data)?;
|
|
|
|
|
|
|
|
let sync_p2p = {
|
|
let sync_p2p = {
|
|
|
- info!("Registering sync P2P protocols...");
|
|
|
|
|
|
|
+ info!("Registering block sync P2P protocols...");
|
|
|
let sync_network_settings = net::Settings {
|
|
let sync_network_settings = net::Settings {
|
|
|
inbound: args.sync_p2p_accept,
|
|
inbound: args.sync_p2p_accept,
|
|
|
outbound_connections: args.sync_slots,
|
|
outbound_connections: args.sync_slots,
|