/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2022 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::{process::exit, str::FromStr};
use darkfi_sdk::crypto::{Address, MerkleNode, TokenId};
use darkfi_serial::{deserialize, serialize};
use serde_json::json;
use darkfi::{
crypto::{
coin::OwnCoin,
note::{EncryptedNote, Note},
},
rpc::{client::RpcClient, jsonrpc::JsonRequest},
Result,
};
/// The RPC object with functionality for connecting to darkfid.
pub struct Rpc {
pub rpc_client: RpcClient,
}
impl Rpc {
/// Fetch wallet balance of given token ID and return its u64 representation.
pub async fn balance_of(&self, token_id: TokenId) -> Result {
let req = JsonRequest::new("wallet.get_balances", json!([]));
let rep = self.rpc_client.request(req).await?;
if !rep.is_object() {
eprintln!("Error: Invalid balance data received from darkfid RPC endpoint.");
exit(1);
}
for i in rep.as_object().unwrap().keys() {
if TokenId::try_from(i.as_str()).unwrap() == token_id {
if let Some(balance) = rep[i].as_u64() {
return Ok(balance)
}
eprintln!("Error: Invalid balance data received from darkfid RPC endpoint.");
exit(1);
}
}
Ok(0)
}
/// Fetch default wallet address from the darkfid RPC endpoint.
pub async fn wallet_address(&self) -> Result {
let req = JsonRequest::new("wallet.get_addrs", json!([0_i64]));
let rep = self.rpc_client.request(req).await?;
if !rep.is_array() || !rep.as_array().unwrap()[0].is_string() {
eprintln!("Error: Invalid wallet address received from darkfid RPC endpoint.");
exit(1);
}
match Address::from_str(rep[0].as_str().unwrap()) {
Ok(v) => Ok(v),
Err(e) => {
eprintln!(
"Error: Invalid wallet address received from darkfid RPC endpoint: {}",
e
);
exit(1)
}
}
}
/// Query wallet for unspent coins in wallet matching value and token_id.
pub async fn get_coins_valtok(&self, value: u64, token_id: TokenId) -> Result> {
let req = JsonRequest::new(
"wallet.get_coins_valtok",
json!([value, format!("{}", token_id), true]),
);
let rep = self.rpc_client.request(req).await?;
if !rep.is_array() {
eprintln!("Error: Invalid coin data received from darkfid RPC endpoint.");
exit(1);
}
let rep = rep.as_array().unwrap();
let mut ret = vec![];
for i in rep {
if !i.is_string() {
eprintln!(
"Error: Invalid base58 data for OwnCoin received from darkfid RPC endpoint."
);
exit(1);
}
let data = match bs58::decode(i.as_str().unwrap()).into_vec() {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed decoding base58 data for OwnCoin: {}", e);
exit(1);
}
};
let oc = match deserialize(&data) {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed deserializing OwnCoin: {}", e);
exit(1);
}
};
ret.push(oc);
}
Ok(ret)
}
/// Fetch the merkle path for a given leaf position in the coin tree
pub async fn get_merkle_path(&self, leaf_pos: usize) -> Result> {
let req = JsonRequest::new("wallet.get_merkle_path", json!([leaf_pos as u64]));
let rep = self.rpc_client.request(req).await?;
if !rep.is_array() {
eprintln!("Error: Invalid merkle path data received from darkfid RPC endpoint.");
exit(1);
}
let rep = rep.as_array().unwrap();
let mut ret = vec![];
for i in rep {
if !i.is_string() {
eprintln!("Error: Invalid base58 data received for MerkleNode");
exit(1);
}
let n = match bs58::decode(i.as_str().unwrap()).into_vec() {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed decoding base58 for MerkleNode: {}", e);
exit(1);
}
};
if n.len() != 32 {
eprintln!("error: MerkleNode byte length is not 32");
exit(1);
}
let n = MerkleNode::from_bytes(n.try_into().unwrap());
if n.is_none() {
eprintln!("Error: Noncanonical bytes of MerkleNode");
exit(1);
}
ret.push(n.unwrap());
}
Ok(ret)
}
/// Try to decrypt a given `EncryptedNote`
pub async fn decrypt_note(&self, enc_note: &EncryptedNote) -> Result