diff --git a/bin/darkfid/src/error.rs b/bin/darkfid/src/error.rs index a2d0c1c25..a210e95d6 100644 --- a/bin/darkfid/src/error.rs +++ b/bin/darkfid/src/error.rs @@ -19,7 +19,7 @@ pub enum RpcError { TxSimulationFail = -32112, // State-related errors, - NotYetSynced = -32120, + NotSynced = -32120, UnknownSlot = -32121, // Parsing errors @@ -42,7 +42,7 @@ fn to_tuple(e: RpcError) -> (i64, String) { RpcError::TxBroadcastFail => "Failed broadcasting transaction", RpcError::TxSimulationFail => "Failed simulating transaction state change", // State-related errors - RpcError::NotYetSynced => "Blockchain not yet synced", + RpcError::NotSynced => "Blockchain is not synced", RpcError::UnknownSlot => "Did not find slot", // Parsing errors RpcError::ParseError => "Parse error", @@ -53,7 +53,12 @@ fn to_tuple(e: RpcError) -> (i64, String) { (e as i64, msg.to_string()) } -pub fn server_error(e: RpcError, id: Value) -> JsonResult { - let (code, msg) = to_tuple(e); - JsonError::new(ServerError(code), Some(msg), id).into() +pub fn server_error(e: RpcError, id: Value, msg: Option<&str>) -> JsonResult { + let (code, default_msg) = to_tuple(e); + + if let Some(message) = msg { + return JsonError::new(ServerError(code), Some(message.to_string()), id).into() + } + + JsonError::new(ServerError(code), Some(default_msg), id).into() } diff --git a/bin/darkfid/src/rpc_blockchain.rs b/bin/darkfid/src/rpc_blockchain.rs index 1d7f641d2..13ebb9e52 100644 --- a/bin/darkfid/src/rpc_blockchain.rs +++ b/bin/darkfid/src/rpc_blockchain.rs @@ -16,6 +16,7 @@ impl Darkfid { // RPCAPI: // Queries the blockchain database for a block in the given slot. // Returns a readable block upon success. + // // --> {"jsonrpc": "2.0", "method": "blockchain.get_slot", "params": [0], "id": 1} // <-- {"jsonrpc": "2.0", "result": {...}, "id": 1} pub async fn blockchain_get_slot(&self, id: Value, params: &[Value]) -> JsonResult { @@ -23,22 +24,22 @@ impl Darkfid { return JsonError::new(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, + let slot = params[0].as_u64().unwrap(); + let validator_state = self.validator_state.read().await; + + let blocks = match validator_state.blockchain.get_blocks_by_slot(&[slot]) { + Ok(v) => { + drop(validator_state); + v + } Err(e) => { - error!("Failed fetching block by slot: {}", e); + error!("[RPC] blockchain.get_slot: Failed fetching block by slot: {}", e); return JsonError::new(InternalError, None, id).into() } }; if blocks.is_empty() { - return server_error(RpcError::UnknownSlot, id) + return server_error(RpcError::UnknownSlot, id, None) } // TODO: Return block as JSON @@ -48,17 +49,26 @@ impl Darkfid { // RPCAPI: // Queries the blockchain database for all available merkle roots. + // // --> {"jsonrpc": "2.0", "method": "blockchain.merkle_roots", "params": [], "id": 1} // <-- {"jsonrpc": "2.0", "result": [..., ..., ...], "id": 1} - pub async fn blockchain_merkle_roots(&self, id: Value, _params: &[Value]) -> JsonResult { - let roots: Vec = - match self.validator_state.read().await.blockchain.merkle_roots.get_all() { - Ok(v) => v, - Err(e) => { - error!("Failed getting merkle roots from rootstore: {}", e); - return JsonError::new(InternalError, None, id).into() - } - }; + pub async fn blockchain_merkle_roots(&self, id: Value, params: &[Value]) -> JsonResult { + if !params.is_empty() { + return JsonError::new(InvalidParams, None, id).into() + } + + let validator_state = self.validator_state.read().await; + + let roots: Vec = match validator_state.blockchain.merkle_roots.get_all() { + Ok(v) => { + drop(validator_state); + v + } + Err(e) => { + error!("[RPC] blockchain.merkle_roots: Failed fetching merkle roots from rootstore: {}", e); + return JsonError::new(InternalError, None, id).into() + } + }; JsonResponse::new(json!(roots), id).into() } diff --git a/bin/darkfid/src/rpc_misc.rs b/bin/darkfid/src/rpc_misc.rs index bbcc323a2..efcb4ed52 100644 --- a/bin/darkfid/src/rpc_misc.rs +++ b/bin/darkfid/src/rpc_misc.rs @@ -10,6 +10,7 @@ use super::Darkfid; impl Darkfid { // RPCAPI: // Returns a `pong` to the `ping` request. + // // --> {"jsonrpc": "2.0", "method": "ping", "params": [], "id": 1} // <-- {"jsonrpc": "2.0", "result": "pong", "id": 1} pub async fn misc_pong(&self, id: Value, _params: &[Value]) -> JsonResult { @@ -18,6 +19,7 @@ impl Darkfid { // RPCAPI: // Returns current system clock in `Timestamp` format. + // // --> {"jsonrpc": "2.0", "method": "clock", "params": [], "id": 1} // <-- {"jsonrpc": "2.0", "result": {...}, "id": 1} pub async fn misc_clock(&self, id: Value, _params: &[Value]) -> JsonResult { diff --git a/bin/darkfid/src/rpc_tx.rs b/bin/darkfid/src/rpc_tx.rs index 3338dee28..ef3b7e03b 100644 --- a/bin/darkfid/src/rpc_tx.rs +++ b/bin/darkfid/src/rpc_tx.rs @@ -36,8 +36,8 @@ impl Darkfid { } if !(*self.synced.lock().await) { - error!("tx.transfer: Blockchain is not yet synced"); - return server_error(RpcError::NotYetSynced, id) + error!("[RPC] tx.transfer: Blockchain is not synced"); + return server_error(RpcError::NotSynced, id, None) } let address = params[0].as_str().unwrap(); @@ -47,24 +47,24 @@ impl Darkfid { let address = match Address::from_str(address) { Ok(v) => v, Err(e) => { - error!("tx.transfer: Failed parsing address from string: {}", e); - return server_error(RpcError::InvalidAddressParam, id) + error!("[RPC] tx.transfer: Failed parsing address from string: {}", e); + return server_error(RpcError::InvalidAddressParam, id, None) } }; let pubkey = match PublicKey::try_from(address) { Ok(v) => v, Err(e) => { - error!("tx.transfer: Failed parsing PublicKey from Address: {}", e); - return server_error(RpcError::ParseError, id) + error!("[RPC] tx.transfer: Failed parsing PublicKey from Address: {}", e); + return server_error(RpcError::ParseError, id, None) } }; let token_id = match token_id::parse_b58(token) { Ok(v) => v, Err(e) => { - error!("tx.transfer: Failed parsing Token ID from string: {}", e); - return server_error(RpcError::ParseError, id) + error!("[RPC] tx.transfer: Failed parsing Token ID from string: {}", e); + return server_error(RpcError::ParseError, id, None) } }; @@ -82,18 +82,18 @@ impl Darkfid { Ok(v) => v, Err(e) => { error!("tx.transfer: Failed building transaction: {}", e); - return server_error(RpcError::TxBuildFail, id) + return server_error(RpcError::TxBuildFail, id, None) } }; if let Some(sync_p2p) = &self.sync_p2p { if let Err(e) = sync_p2p.broadcast(tx.clone()).await { - error!("tx.transfer: Failed broadcasting transaction: {}", e); - return server_error(RpcError::TxBroadcastFail, id) + error!("[RPC] tx.transfer: Failed broadcasting transaction: {}", e); + return server_error(RpcError::TxBroadcastFail, id, None) } } else { - warn!("tx.transfer: No sync P2P network, not broadcasting transaction."); - return server_error(RpcError::TxBroadcastFail, id) + warn!("[RPC] tx.transfer: No sync P2P network, not broadcasting transaction."); + return server_error(RpcError::TxBroadcastFail, id, None) } let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string(); @@ -114,46 +114,48 @@ impl Darkfid { } if !(*self.synced.lock().await) { - error!("tx.transfer: Blockchain is not yet synced"); - return server_error(RpcError::NotYetSynced, id) + error!("[RPC] tx.transfer: Blockchain is not synced"); + return server_error(RpcError::NotSynced, id, None) } // Try to deserialize the transaction let tx_bytes = match bs58::decode(params[0].as_str().unwrap()).into_vec() { Ok(v) => v, Err(e) => { - error!("tx.broadcast: Failed decoding base58 transaction: {}", e); - return server_error(RpcError::ParseError, id) + error!("[RPC] tx.broadcast: Failed decoding base58 transaction: {}", e); + return server_error(RpcError::ParseError, id, None) } }; let tx: Transaction = match deserialize(&tx_bytes) { Ok(v) => v, Err(e) => { - error!("tx.broadcast: Failed deserializing bytes into Transaction: {}", e); - return server_error(RpcError::ParseError, id) + error!("[RPC] tx.broadcast: Failed deserializing bytes into Transaction: {}", e); + return server_error(RpcError::ParseError, id, None) } }; // Grab the current state and apply a new MemoryState - let state_machine = self.validator_state.read().await.state_machine.lock().await.clone(); - let mem_state = MemoryState::new(state_machine.clone()); - drop(state_machine); + let validator_state = self.validator_state.read().await; + let state = validator_state.state_machine.lock().await; + let mem_state = MemoryState::new(state.clone()); + drop(state); + drop(validator_state); // Simulate state transition if let Err(e) = ValidatorState::validate_state_transitions(mem_state, &[tx.clone()]) { - error!("tx.broadcast: Failed to validate state transition: {}", e); - return server_error(RpcError::TxSimulationFail, id) + error!("[RPC] tx.broadcast: Failed to validate state transition: {}", e); + return server_error(RpcError::TxSimulationFail, id, None) } if let Some(sync_p2p) = &self.sync_p2p { if let Err(e) = sync_p2p.broadcast(tx.clone()).await { - error!("tx.broadcast: Failed broadcasting transaction: {}", e); - return server_error(RpcError::TxBroadcastFail, id) + error!("[RPC] tx.broadcast: Failed broadcasting transaction: {}", e); + return server_error(RpcError::TxBroadcastFail, id, None) } } else { - warn!("tx.broadcast: No sync P2P network, not broadcasting transaction."); - return server_error(RpcError::TxBroadcastFail, id) + warn!("[RPC] tx.broadcast: No sync P2P network, not broadcasting transaction."); + return server_error(RpcError::TxBroadcastFail, id, None) } let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string(); diff --git a/bin/darkfid/src/rpc_wallet.rs b/bin/darkfid/src/rpc_wallet.rs index 0e97be2db..6a4bc6958 100644 --- a/bin/darkfid/src/rpc_wallet.rs +++ b/bin/darkfid/src/rpc_wallet.rs @@ -24,14 +24,19 @@ use crate::{server_error, RpcError}; impl Darkfid { // RPCAPI: // Attempts to generate a new keypair and returns its address upon success. + // // --> {"jsonrpc": "2.0", "method": "wallet.keygen", "params": [], "id": 1} // <-- {"jsonrpc": "2.0", "result": "1DarkFi...", "id": 1} - pub async fn wallet_keygen(&self, id: Value, _params: &[Value]) -> JsonResult { + pub async fn wallet_keygen(&self, id: Value, params: &[Value]) -> JsonResult { + if !params.is_empty() { + return JsonError::new(InvalidParams, None, id).into() + } + match self.client.keygen().await { Ok(a) => JsonResponse::new(json!(a.to_string()), id).into(), Err(e) => { - error!("Failed creating keypair: {}", e); - server_error(RpcError::Keygen, id) + error!("[RPC] wallet.keygen: Failed creating keypair: {}", e); + server_error(RpcError::Keygen, id, None) } } } @@ -39,6 +44,7 @@ impl Darkfid { // 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": "wallet.get_addrs", "params": [1, 2], "id": 1} // <-- {"jsonrpc": "2.0", "result": ["foo", "bar"], "id": 1} pub async fn wallet_get_addrs(&self, id: Value, params: &[Value]) -> JsonResult { @@ -47,43 +53,54 @@ impl Darkfid { } let mut fetch_all = false; - for i in params { - if !i.is_i64() { - return server_error(RpcError::NaN, id) + + for (i, elem) in params.iter().enumerate() { + if !elem.is_i64() { + error!("[RPC] wallet.get_addrs: Param {} is not i64", i); + return server_error(RpcError::NaN, id, Some(&format!("Param {} is not i64", i))) } - if i.as_i64() == Some(-1) { + if elem.as_i64() == Some(-1) { + if params.len() != 1 { + return server_error( + RpcError::ParseError, + id, + Some("-1 can only be used as a single param"), + ) + } + fetch_all = true; break } - if i.as_i64() < Some(-1) { - return server_error(RpcError::LessThanNegOne, id) + if elem.as_i64() < Some(-1) { + return server_error(RpcError::LessThanNegOne, id, None) } } let keypairs = match self.client.get_keypairs().await { Ok(v) => v, Err(e) => { - error!("Failed fetching keypairs: {}", e); - return server_error(RpcError::KeypairFetch, id) + error!("[RPC] wallet.get_addrs: Failed fetching keypairs: {}", e); + return server_error(RpcError::KeypairFetch, id, None) } }; - 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) - } + let ret: Vec = + keypairs.iter().map(|x| Address::from(x.public).to_string()).collect(); + return JsonResponse::new(json!(ret), id).into() + } + + let mut ret = vec![]; + 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) } } @@ -93,6 +110,7 @@ impl Darkfid { // RPCAPI: // Exports the given keypair index. // Returns the encoded secret key upon success. + // // --> {"jsonrpc": "2.0", "method": "wallet.export_keypair", "params": [0], "id": 1} // <-- {"jsonrpc": "2.0", "result": "foobar", "id": 1} pub async fn wallet_export_keypair(&self, id: Value, params: &[Value]) -> JsonResult { @@ -103,8 +121,8 @@ impl Darkfid { let keypairs = match self.client.get_keypairs().await { Ok(v) => v, Err(e) => { - error!("Failed fetching keypairs: {}", e); - return server_error(RpcError::KeypairFetch, id) + error!("[RPC] wallet.export_keypair: Failed fetching keypairs: {}", e); + return server_error(RpcError::KeypairFetch, id, None) } }; @@ -112,12 +130,13 @@ impl Darkfid { return JsonResponse::new(json!(kp.secret.to_bytes()), id).into() } - server_error(RpcError::KeypairNotFound, id) + server_error(RpcError::KeypairNotFound, id, None) } // 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": "wallet.import_keypair", "params": ["foobar"], "id": 1} // <-- {"jsonrpc": "2.0", "result": "pubfoobar", "id": 1} pub async fn wallet_import_keypair(&self, id: Value, params: &[Value]) -> JsonResult { @@ -128,16 +147,16 @@ impl Darkfid { 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) + error!("[RPC] wallet.import_keypair: Failed parsing secret key from string: {}", e); + return server_error(RpcError::InvalidKeypair, id, None) } }; 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) + error!("[RPC] wallet.import_keypair: Failed parsing secret key from string: {}", e); + return server_error(RpcError::InvalidKeypair, id, None) } }; @@ -145,13 +164,10 @@ impl Darkfid { 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 JsonError::new(InternalError, None, id).into() - } - }; + if let Err(e) = self.client.put_keypair(&keypair).await { + error!("[RPC] wallet.import_keypair: Failed inserting keypair into wallet: {}", e); + return JsonError::new(InternalError, None, id).into() + } JsonResponse::new(json!(address), id).into() } @@ -159,6 +175,7 @@ impl Darkfid { // RPCAPI: // Sets the default wallet address to the given index. // Returns `true` upon success. + // // --> {"jsonrpc": "2.0", "method": "wallet.set_default_address", "params": [2], "id": 1} // <-- {"jsonrpc": "2.0", "result": true, "id": 1} pub async fn wallet_set_default_address(&self, id: Value, params: &[Value]) -> JsonResult { @@ -171,23 +188,21 @@ impl Darkfid { let keypairs = match self.client.get_keypairs().await { Ok(v) => v, Err(e) => { - error!("Failed fetching keypairs: {}", e); - return server_error(RpcError::KeypairFetch, id) + error!("[RPC] wallet.set_default_address: Failed fetching keypairs: {}", e); + return server_error(RpcError::KeypairFetch, id, None) } }; if keypairs.len() as u64 != idx - 1 { - return server_error(RpcError::KeypairNotFound, id) + return server_error(RpcError::KeypairNotFound, id, None) } 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 JsonError::new(InternalError, None, id).into() - } - }; + + if let Err(e) = self.client.set_default_keypair(&kp.public).await { + error!("[RPC] wallet.set_default_address: Failed setting default keypair: {}", e); + return JsonError::new(InternalError, None, id).into() + } JsonResponse::new(json!(true), id).into() } @@ -195,13 +210,14 @@ impl Darkfid { // RPCAPI: // Queries the wallet for known tokens with active balances. // Returns a map of balances, indexed by the token ID. + // // --> {"jsonrpc": "2.0", "method": "wallet.get_balances", "params": [], "id": 1} // <-- {"jsonrpc": "2.0", "result": [{"1Foobar...": 100}, {...}]", "id": 1} pub async fn wallet_get_balances(&self, id: Value, _params: &[Value]) -> JsonResult { let balances = match self.client.get_balances().await { Ok(v) => v, Err(e) => { - error!("Failed fetching balances from wallet: {}", e); + error!("[RPC] wallet.get_balances: Failed fetching balances from wallet: {}", e); return JsonError::new(InternalError, None, id).into() } }; @@ -243,7 +259,7 @@ impl Darkfid { let token_id = match token_id::parse_b58(params[1].as_str().unwrap()) { Ok(v) => v, Err(e) => { - error!("Failed parsing token_id from base58 string: {}", e); + error!("[RPC] wallet.get_coins_valtok: Failed parsing token_id from base58: {}", e); return JsonError::new(ParseError, None, id).into() } }; @@ -251,7 +267,7 @@ impl Darkfid { let coins = match self.client.get_coins_valtok(value, token_id, unspent).await { Ok(v) => v, Err(e) => { - error!("Failed fetching coins by valtok from wallet: {}", e); + error!("[RPC] wallet.get_coins_valtok: Failed fetching from wallet: {}", e); return JsonError::new(InternalError, None, id).into() } }; @@ -263,6 +279,7 @@ impl Darkfid { // RPCAPI: // Query the state merkle tree for the merkle path of a given leaf position. + // // --> {"jsonrpc": "2.0", "method": "wallet.get_merkle_path", "params": [3], "id": 1} // <-- {"jsonrpc": "2.0", "result": ["f091uf1...", "081ff0h10w1h0...", ...], "id": 1} pub async fn wallet_get_merkle_path(&self, id: Value, params: &[Value]) -> JsonResult { @@ -288,6 +305,7 @@ impl Darkfid { // RPCAPI: // Try to decrypt a given encrypted note with the secret keys // found in the wallet. + // // --> {"jsonrpc": "2.0", "method": "wallet.decrypt_note", params": [ciphertext], "id": 1} // <-- {"jsonrpc": "2.0", "result": "base58_encoded_plain_note", "id": 1} pub async fn wallet_decrypt_note(&self, id: Value, params: &[Value]) -> JsonResult { @@ -298,7 +316,7 @@ impl Darkfid { let bytes = match bs58::decode(params[0].as_str().unwrap()).into_vec() { Ok(v) => v, Err(e) => { - error!("decrypt_note(): Failed decoding base58 string: {}", e); + error!("[RPC] wallet.decrypt_note: Failed decoding base58 string: {}", e); return JsonError::new(ParseError, None, id).into() } }; @@ -306,7 +324,7 @@ impl Darkfid { let enc_note = match deserialize(&bytes) { Ok(v) => v, Err(e) => { - error!("decrypt_note(): Failed deserializing bytes into EncryptedNote: {}", e); + error!("[RPC] wallet.decrypt_note: Failed deserializing into EncryptedNote: {}", e); return JsonError::new(InternalError, None, id).into() } }; @@ -314,7 +332,7 @@ impl Darkfid { let keypairs = match self.client.get_keypairs().await { Ok(v) => v, Err(e) => { - error!("decrypt_note(): Failed fetching keypairs: {}", e); + error!("[RPC] wallet.decrypt_note: Failed fetching keypairs: {}", e); return JsonError::new(InternalError, None, id).into() } }; @@ -326,6 +344,6 @@ impl Darkfid { } } - server_error(RpcError::DecryptionFailed, id) + server_error(RpcError::DecryptionFailed, id, None) } }