use std::result;
use hex;
use bitcoin;
use jsonrpc;
use serde;
use serde_json;
use bitcoin::util::hash::Sha256dHash;
use bitcoin::{Address, Block, BlockHeader, Transaction};
use bitcoin_amount::Amount;
use log::Level::Trace;
use num_bigint::BigUint;
use secp256k1::Signature;
use std::collections::HashMap;
use error::*;
use json;
use queryable;
pub type Result<T> = result::Result<T, Error>;
fn into_json<T>(val: T) -> Result<serde_json::Value>
where
T: serde::ser::Serialize,
{
Ok(serde_json::to_value(val)?)
}
fn opt_into_json<T>(opt: Option<T>) -> Result<serde_json::Value>
where
T: serde::ser::Serialize,
{
match opt {
Some(val) => Ok(into_json(val)?),
None => Ok(serde_json::Value::Null),
}
}
#[allow(unused)]
fn null() -> serde_json::Value {
serde_json::Value::Null
}
fn handle_defaults<'a, 'b>(
args: &'a mut [serde_json::Value],
defaults: &'b [serde_json::Value],
) -> &'a [serde_json::Value] {
assert!(args.len() >= defaults.len());
let mut first_non_null_optional_idx = None;
for i in 0..defaults.len() {
let args_i = args.len() - 1 - i;
let defaults_i = defaults.len() - 1 - i;
if args[args_i] == serde_json::Value::Null {
if first_non_null_optional_idx.is_some() {
if defaults[defaults_i] == serde_json::Value::Null {
panic!("Missing `default` for argument idx {}", args_i);
}
args[args_i] = defaults[defaults_i].clone();
}
} else if first_non_null_optional_idx.is_none() {
first_non_null_optional_idx = Some(args_i);
}
}
let required_num = args.len() - defaults.len();
if let Some(i) = first_non_null_optional_idx {
&args[..i+1]
} else {
&args[..required_num]
}
}
pub struct Client {
client: jsonrpc::client::Client,
}
impl Client {
pub fn new(url: String, user: Option<String>, pass: Option<String>) -> Self {
debug_assert!(pass.is_none() || user.is_some());
Client {
client: jsonrpc::client::Client::new(url, user, pass),
}
}
pub fn from_jsonrpc(client: jsonrpc::client::Client) -> Client {
Client {
client: client,
}
}
pub fn get_by_id<T: queryable::Queryable>(
&self,
id: &<T as queryable::Queryable>::Id,
) -> Result<T> {
T::query(self, &id)
}
pub fn call<T: for<'a> serde::de::Deserialize<'a>>(
&self,
cmd: &str,
args: &[serde_json::Value],
) -> Result<T> {
let req = self.client.build_request(cmd.to_owned(), args.to_owned());
if log_enabled!(Trace) {
trace!("JSON-RPC request: {}", serde_json::to_string(&req).unwrap());
}
let resp = self.client.send_request(&req).map_err(Error::from);
if log_enabled!(Trace) && resp.is_ok() {
let resp = resp.as_ref().unwrap();
trace!("JSON-RPC response: {}", serde_json::to_string(resp).unwrap());
}
Ok(resp?.into_result()?)
}
pub fn add_multisig_address(
&self,
nrequired: usize,
keys: Vec<json::PubKeyOrAddress>,
label: Option<&str>,
address_type: Option<json::AddressType>,
) -> Result<json::AddMultiSigAddressResult> {
let mut args = [
into_json(nrequired)?,
into_json(keys)?,
opt_into_json(label)?,
opt_into_json(address_type)?,
];
self.call("addmultisigaddress", handle_defaults(&mut args, &[into_json("")?, null()]))
}
pub fn backup_wallet(&self, destination: Option<&str>) -> Result<()> {
let mut args = [opt_into_json(destination)?];
self.call("backupwallet", handle_defaults(&mut args, &[null()]))
}
pub fn dump_priv_key(&self, address: &Address) -> Result<String> {
self.call("dumpprivkey", &[into_json(address)?])
}
pub fn encrypt_wallet(&self, passphrase: &str) -> Result<()> {
self.call("encryptwallet", &[into_json(passphrase)?])
}
pub fn get_difficulty(&self) -> Result<BigUint> {
self.call("getdifficulty", &[])
}
pub fn get_connection_count(&self) -> Result<usize> {
self.call("getconnectioncount", &[])
}
pub fn get_block(&self, hash: &Sha256dHash) -> Result<Block> {
let hex: String = self.call("getblock", &[into_json(hash)?, 0.into()])?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
pub fn get_block_hex(&self, hash: &Sha256dHash) -> Result<String> {
self.call("getblock", &[into_json(hash)?, 0.into()])
}
pub fn get_block_info(&self, hash: &Sha256dHash) -> Result<json::GetBlockResult> {
self.call("getblock", &[into_json(hash)?, 1.into()])
}
pub fn get_block_header_raw(&self, hash: &Sha256dHash) -> Result<BlockHeader> {
let hex: String = self.call("getblockheader", &[into_json(hash)?, false.into()])?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
pub fn get_block_header_verbose(
&self,
hash: &Sha256dHash,
) -> Result<json::GetBlockHeaderResult> {
self.call("getblockheader", &[into_json(hash)?, true.into()])
}
pub fn get_mining_info(&self) -> Result<json::GetMiningInfoResult> {
self.call("getmininginfo", &[])
}
pub fn get_blockchain_info(&self) -> Result<json::GetBlockchainInfoResult> {
self.call("getblockchaininfo", &[])
}
pub fn get_block_count(&self) -> Result<u64> {
self.call("getblockcount", &[])
}
pub fn get_best_block_hash(&self) -> Result<Sha256dHash> {
self.call("getbestblockhash", &[])
}
pub fn get_block_hash(&self, height: u64) -> Result<Sha256dHash> {
self.call("getblockhash", &[height.into()])
}
pub fn get_raw_transaction(
&self,
txid: &Sha256dHash,
block_hash: Option<&Sha256dHash>,
) -> Result<Transaction> {
let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
let hex: String = self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
pub fn get_raw_transaction_hex(
&self,
txid: &Sha256dHash,
block_hash: Option<&Sha256dHash>,
) -> Result<String> {
let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
}
pub fn get_raw_transaction_verbose(
&self,
txid: &Sha256dHash,
block_hash: Option<&Sha256dHash>,
) -> Result<json::GetRawTransactionResult> {
let mut args = [into_json(txid)?, into_json(true)?, opt_into_json(block_hash)?];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
}
pub fn get_received_by_address(
&self,
address: &Address,
minconf: Option<u32>,
) -> Result<Amount> {
let mut args = [into_json(address)?, opt_into_json(minconf)?];
self.call("getreceivedbyaddress", handle_defaults(&mut args, &[null()]))
}
pub fn get_transaction(
&self,
txid: &Sha256dHash,
include_watchonly: Option<bool>,
) -> Result<json::GetTransactionResult> {
let mut args = [into_json(txid)?, opt_into_json(include_watchonly)?];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
}
pub fn get_tx_out(
&self,
txid: &Sha256dHash,
vout: u32,
include_mempool: Option<bool>,
) -> Result<Option<json::GetTxOutResult>> {
let mut args = [into_json(txid)?, into_json(vout)?, opt_into_json(include_mempool)?];
self.call("gettxout", handle_defaults(&mut args, &[null()]))
}
pub fn import_priv_key(
&self,
privkey: &str,
label: Option<&str>,
rescan: Option<bool>,
) -> Result<()> {
let mut args = [into_json(privkey)?, into_json(label)?, opt_into_json(rescan)?];
self.call("importprivkey", handle_defaults(&mut args, &[into_json("")?, null()]))
}
pub fn key_pool_refill(&self, new_size: Option<usize>) -> Result<()> {
let mut args = [opt_into_json(new_size)?];
self.call("keypoolrefill", handle_defaults(&mut args, &[null()]))
}
pub fn list_unspent(
&self,
minconf: Option<usize>,
maxconf: Option<usize>,
addresses: Option<Vec<&Address>>,
include_unsafe: Option<bool>,
query_options: Option<HashMap<&str, &str>>,
) -> Result<Vec<json::ListUnspentResult>> {
let mut args = [
opt_into_json(minconf)?,
opt_into_json(maxconf)?,
opt_into_json(addresses)?,
opt_into_json(include_unsafe)?,
opt_into_json(query_options)?,
];
let defaults = [
into_json(0)?,
into_json(9999999)?,
into_json::<&[Address]>(&[])?,
into_json(true)?,
null(),
];
self.call("listunspent", handle_defaults(&mut args, &defaults))
}
pub fn create_raw_transaction_hex(
&self,
utxos: &[json::CreateRawTransactionInput],
outs: Option<&HashMap<String, f64>>,
locktime: Option<i64>,
replaceable: Option<bool>,
) -> Result<String> {
let mut args = [
into_json(utxos)?,
opt_into_json(outs)?,
opt_into_json(locktime)?,
opt_into_json(replaceable)?,
];
let defaults =
[into_json::<&[json::CreateRawTransactionInput]>(&[])?, into_json(0i64)?, null()];
self.call("createrawtransaction", handle_defaults(&mut args, &defaults))
}
pub fn create_raw_transaction(
&self,
utxos: &[json::CreateRawTransactionInput],
outs: Option<&HashMap<String, f64>>,
locktime: Option<i64>,
replaceable: Option<bool>,
) -> Result<Transaction> {
let hex: String = self.create_raw_transaction_hex(utxos, outs, locktime, replaceable)?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
pub fn sign_raw_transaction(
&self,
tx: json::HexBytes,
utxos: Option<&[json::SignRawTransactionInput]>,
private_keys: Option<&[&str]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [
into_json(tx)?,
opt_into_json(utxos)?,
opt_into_json(private_keys)?,
opt_into_json(sighash_type)?,
];
let defaults = [
into_json::<&[json::SignRawTransactionInput]>(&[])?,
into_json::<&[&str]>(&[])?,
null(),
];
self.call("signrawtransaction", handle_defaults(&mut args, &defaults))
}
pub fn stop(&self) -> Result<()> {
self.call("stop", &[])
}
pub fn sign_raw_transaction_with_wallet(
&self,
tx: json::HexBytes,
utxos: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [into_json(tx)?, opt_into_json(utxos)?, opt_into_json(sighash_type)?];
let defaults = [into_json::<&[json::SignRawTransactionInput]>(&[])?, null()];
self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults))
}
pub fn verify_message(
&self,
address: &Address,
signature: &Signature,
message: &str,
) -> Result<bool> {
let args = [into_json(address)?, into_json(signature)?, into_json(message)?];
self.call("verifymessage", &args)
}
pub fn get_new_address(
&self,
account: Option<&str>,
address_type: Option<json::AddressType>
) -> Result<String> {
self.call("getnewaddress", &[opt_into_json(account)?, opt_into_json(address_type)?])
}
pub fn generate_to_address(
&self,
block_num: u64,
address: &str,
) -> Result<Vec<Sha256dHash>> {
self.call("generatetoaddress", &[block_num.into(), address.into()])
}
pub fn invalidate_block(&self, block_hash: &Sha256dHash) -> Result<()> {
self.call("invalidateblock", &[into_json(block_hash)?])
}
pub fn send_to_address(
&self,
addr: &str,
amount: f64,
comment: Option<&str>,
comment_to: Option<&str>,
substract_fee: Option<bool>,
) -> Result<Sha256dHash> {
let mut args = [
into_json(addr)?,
into_json(amount)?,
opt_into_json(comment)?,
opt_into_json(comment_to)?,
opt_into_json(substract_fee)?
];
self.call("sendtoaddress", handle_defaults(&mut args, &["".into(), "".into(), null()]))
}
pub fn get_peer_info(&self) -> Result<Vec<json::GetPeerInfoResult>> {
self.call("getpeerinfo", &[])
}
pub fn ping(&self) -> Result<()> {
self.call("ping", &[])
}
pub fn send_raw_transaction(&self, tx: &str) -> Result<String> {
self.call("sendrawtransaction", &[into_json(tx)?])
}
pub fn estimate_smartfee<E>(
&self,
conf_target: u16,
estimate_mode: Option<json::EstimateMode>,
) -> Result<json::EstimateSmartFeeResult> {
let mut args = [into_json(conf_target)?, opt_into_json(estimate_mode)?];
self.call("estimatesmartfee", handle_defaults(&mut args, &[null()]))
}
pub fn wait_for_new_block(&self, timeout: u64) -> Result<json::BlockRef> {
self.call("waitfornewblock", &[into_json(timeout)?])
}
pub fn wait_for_block(
&self,
blockhash: &Sha256dHash,
timeout: u64,
) -> Result<json::BlockRef> {
let args = [into_json(blockhash)?, into_json(timeout)?];
self.call("waitforblock", &args)
}
}
#[cfg(tests)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_handle_defaults() -> Result<()> {
{
let mut args = [into_json(0)?, null(), null()];
let defaults = [into_json(1)?, into_json(2)?];
let res = [into_json(0)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [into_json(0)?, into_json(1)?, null()];
let defaults = [into_json(2)?];
let res = [into_json(0)?, into_json(1)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [into_json(0)?, null(), into_json(5)?];
let defaults = [into_json(2)?, into_json(3)?];
let res = [into_json(0)?, into_json(2)?, into_json(5)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [into_json(0)?, null(), into_json(5)?, null()];
let defaults = [into_json(2)?, into_json(3)?, into_json(4)?];
let res = [into_json(0)?, into_json(2)?, into_json(5)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [null(), null()];
let defaults = [into_json(2)?, into_json(3)?];
let res: [serde_json::Value; 0] = [];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [null(), into_json(1)?];
let defaults = [];
let res = [null(), into_json(1)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [];
let defaults = [];
let res: [serde_json::Value; 0] = [];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
{
let mut args = [into_json(0)?];
let defaults = [into_json(2)?];
let res = [into_json(0)?];
assert_eq!(handle_defaults(&mut args, &defaults), &res);
}
Ok(())
}
}