use std::fs::File;
use std::result;
use bitcoin;
use hex;
use jsonrpc;
use secp256k1;
use serde;
use serde_json;
use bitcoin::{Address, Block, BlockHeader, PrivateKey, Transaction};
use bitcoin_amount::Amount;
use bitcoin_hashes::sha256d;
use log::Level::Trace;
use num_bigint::BigUint;
use secp256k1::{SecretKey, 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),
}
}
fn null() -> serde_json::Value {
serde_json::Value::Null
}
fn empty_arr() -> serde_json::Value {
serde_json::Value::Array(vec![])
}
fn empty_obj() -> serde_json::Value {
serde_json::Value::Object(Default::default())
}
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]
}
}
fn opt_result<T: for<'a> serde::de::Deserialize<'a>>(
result: serde_json::Value,
) -> Result<Option<T>> {
if result == serde_json::Value::Null {
Ok(None)
} else {
Ok(serde_json::from_value(result)?)
}
}
pub trait RawTx: Sized {
fn raw_hex(self) -> String;
}
impl<'a> RawTx for &'a Transaction {
fn raw_hex(self) -> String {
hex::encode(bitcoin::consensus::encode::serialize(self))
}
}
impl<'a> RawTx for &'a [u8] {
fn raw_hex(self) -> String {
hex::encode(self)
}
}
impl<'a> RawTx for &'a Vec<u8> {
fn raw_hex(self) -> String {
hex::encode(self)
}
}
impl<'a> RawTx for &'a str {
fn raw_hex(self) -> String {
self.to_owned()
}
}
impl RawTx for String {
fn raw_hex(self) -> String {
self
}
}
pub enum Auth<'a> {
None,
UserPass(String, String),
CookieFile(&'a str),
}
impl<'a> Auth<'a> {
fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
use std::io::Read;
match self {
Auth::None => Ok((None, None)),
Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
Auth::CookieFile(path) => {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let mut split = contents.splitn(2, ":");
Ok((
Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
))
}
}
}
}
pub trait RpcApi: Sized {
fn call<T: for<'a> serde::de::Deserialize<'a>>(
&self,
cmd: &str,
args: &[serde_json::Value],
) -> Result<T>;
fn get_by_id<T: queryable::Queryable<Self>>(
&self,
id: &<T as queryable::Queryable<Self>>::Id,
) -> Result<T> {
T::query(&self, &id)
}
fn add_multisig_address(
&self,
nrequired: usize,
keys: &[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()]))
}
fn backup_wallet(&self, destination: Option<&str>) -> Result<()> {
let mut args = [opt_into_json(destination)?];
self.call("backupwallet", handle_defaults(&mut args, &[null()]))
}
fn dump_priv_key(&self, address: &Address) -> Result<SecretKey> {
let hex: String = self.call("dumpprivkey", &[address.to_string().into()])?;
let bytes = hex::decode(hex)?;
Ok(secp256k1::SecretKey::from_slice(&bytes)?)
}
fn encrypt_wallet(&self, passphrase: &str) -> Result<()> {
self.call("encryptwallet", &[into_json(passphrase)?])
}
fn get_difficulty(&self) -> Result<BigUint> {
self.call("getdifficulty", &[])
}
fn get_connection_count(&self) -> Result<usize> {
self.call("getconnectioncount", &[])
}
fn get_block(&self, hash: &sha256d::Hash) -> Result<Block> {
let hex: String = self.call("getblock", &[into_json(hash)?, 0.into()])?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
fn get_block_hex(&self, hash: &sha256d::Hash) -> Result<String> {
self.call("getblock", &[into_json(hash)?, 0.into()])
}
fn get_block_info(&self, hash: &sha256d::Hash) -> Result<json::GetBlockResult> {
self.call("getblock", &[into_json(hash)?, 1.into()])
}
fn get_block_header_raw(&self, hash: &sha256d::Hash) -> Result<BlockHeader> {
let hex: String = self.call("getblockheader", &[into_json(hash)?, false.into()])?;
let bytes = hex::decode(hex)?;
Ok(bitcoin::consensus::encode::deserialize(&bytes)?)
}
fn get_block_header_verbose(&self, hash: &sha256d::Hash) -> Result<json::GetBlockHeaderResult> {
self.call("getblockheader", &[into_json(hash)?, true.into()])
}
fn get_mining_info(&self) -> Result<json::GetMiningInfoResult> {
self.call("getmininginfo", &[])
}
fn get_blockchain_info(&self) -> Result<json::GetBlockchainInfoResult> {
self.call("getblockchaininfo", &[])
}
fn get_block_count(&self) -> Result<u64> {
self.call("getblockcount", &[])
}
fn get_best_block_hash(&self) -> Result<sha256d::Hash> {
self.call("getbestblockhash", &[])
}
fn get_block_hash(&self, height: u64) -> Result<sha256d::Hash> {
self.call("getblockhash", &[height.into()])
}
fn get_raw_transaction(
&self,
txid: &sha256d::Hash,
block_hash: Option<&sha256d::Hash>,
) -> 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)?)
}
fn get_raw_transaction_hex(
&self,
txid: &sha256d::Hash,
block_hash: Option<&sha256d::Hash>,
) -> Result<String> {
let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
}
fn get_raw_transaction_verbose(
&self,
txid: &sha256d::Hash,
block_hash: Option<&sha256d::Hash>,
) -> 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()]))
}
fn get_received_by_address(&self, address: &Address, minconf: Option<u32>) -> Result<Amount> {
let mut args = [address.to_string().into(), opt_into_json(minconf)?];
self.call("getreceivedbyaddress", handle_defaults(&mut args, &[null()]))
}
fn get_transaction(
&self,
txid: &sha256d::Hash,
include_watchonly: Option<bool>,
) -> Result<json::GetTransactionResult> {
let mut args = [into_json(txid)?, opt_into_json(include_watchonly)?];
self.call("gettransaction", handle_defaults(&mut args, &[null()]))
}
fn get_tx_out(
&self,
txid: &sha256d::Hash,
vout: u32,
include_mempool: Option<bool>,
) -> Result<Option<json::GetTxOutResult>> {
let mut args = [into_json(txid)?, into_json(vout)?, opt_into_json(include_mempool)?];
opt_result(self.call("gettxout", handle_defaults(&mut args, &[null()]))?)
}
fn import_priv_key(
&self,
privkey: &SecretKey,
label: Option<&str>,
rescan: Option<bool>,
) -> Result<()> {
let mut args = [privkey.to_string().into(), into_json(label)?, opt_into_json(rescan)?];
self.call("importprivkey", handle_defaults(&mut args, &[into_json("")?, null()]))
}
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()]))
}
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)?, empty_arr(), into_json(true)?, null()];
self.call("listunspent", handle_defaults(&mut args, &defaults))
}
fn create_raw_transaction_hex(
&self,
utxos: &[json::CreateRawTransactionInput],
outs: &HashMap<String, f64>,
locktime: Option<i64>,
replaceable: Option<bool>,
) -> Result<String> {
let mut args = [
into_json(utxos)?,
into_json(outs)?,
opt_into_json(locktime)?,
opt_into_json(replaceable)?,
];
let defaults = [into_json(0i64)?, null()];
self.call("createrawtransaction", handle_defaults(&mut args, &defaults))
}
fn create_raw_transaction(
&self,
utxos: &[json::CreateRawTransactionInput],
outs: &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)?)
}
fn fund_raw_transaction<R: RawTx>(
&self,
tx: R,
options: Option<json::FundRawTransactionOptions>,
is_witness: Option<bool>,
) -> Result<json::FundRawTransactionResult> {
let mut args = [tx.raw_hex().into(), opt_into_json(options)?, opt_into_json(is_witness)?];
let defaults = [empty_obj(), null()];
self.call("fundrawtransaction", handle_defaults(&mut args, &defaults))
}
#[deprecated]
fn sign_raw_transaction<R: RawTx>(
&self,
tx: R,
utxos: Option<&[json::SignRawTransactionInput]>,
private_keys: Option<&[&PrivateKey]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [
tx.raw_hex().into(),
opt_into_json(utxos)?,
opt_into_json(private_keys)?,
opt_into_json(sighash_type)?,
];
let defaults = [empty_arr(), empty_arr(), null()];
self.call("signrawtransaction", handle_defaults(&mut args, &defaults))
}
fn sign_raw_transaction_with_wallet<R: RawTx>(
&self,
tx: R,
utxos: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [tx.raw_hex().into(), opt_into_json(utxos)?, opt_into_json(sighash_type)?];
let defaults = [empty_arr(), null()];
self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults))
}
fn sign_raw_transaction_with_key<R: RawTx>(
&self,
tx: R,
privkeys: &[&PrivateKey],
prevtxs: Option<&[json::SignRawTransactionInput]>,
sighash_type: Option<json::SigHashType>,
) -> Result<json::SignRawTransactionResult> {
let mut args = [
tx.raw_hex().into(),
into_json(privkeys)?,
opt_into_json(prevtxs)?,
opt_into_json(sighash_type)?,
];
let defaults = [empty_arr(), null()];
self.call("signrawtransactionwithkey", handle_defaults(&mut args, &defaults))
}
fn test_mempool_accept(&self, rawtxs: &[&str]) -> Result<Vec<json::TestMempoolAccept>> {
self.call("testmempoolaccept", &[into_json(rawtxs)?])
}
fn stop(&self) -> Result<()> {
self.call("stop", &[])
}
fn verify_message(
&self,
address: &Address,
signature: &Signature,
message: &str,
) -> Result<bool> {
let args = [address.to_string().into(), signature.to_string().into(), into_json(message)?];
self.call("verifymessage", &args)
}
fn get_new_address(
&self,
label: Option<&str>,
address_type: Option<json::AddressType>,
) -> Result<String> {
self.call("getnewaddress", &[opt_into_json(label)?, opt_into_json(address_type)?])
}
fn generate_to_address(&self, block_num: u64, address: &str) -> Result<Vec<sha256d::Hash>> {
self.call("generatetoaddress", &[block_num.into(), address.into()])
}
fn generate(&self, block_num: u64, maxtries: Option<u64>) -> Result<Vec<sha256d::Hash>> {
self.call("generate", &[block_num.into(), opt_into_json(maxtries)?])
}
fn invalidate_block(&self, block_hash: &sha256d::Hash) -> Result<()> {
self.call("invalidateblock", &[into_json(block_hash)?])
}
fn send_to_address(
&self,
address: &Address,
amount: f64,
comment: Option<&str>,
comment_to: Option<&str>,
substract_fee: Option<bool>,
replaceable: Option<bool>,
confirmation_target: Option<u32>,
estimate_mode: Option<json::EstimateMode>,
) -> Result<sha256d::Hash> {
let mut args = [
address.to_string().into(),
into_json(amount)?,
opt_into_json(comment)?,
opt_into_json(comment_to)?,
opt_into_json(substract_fee)?,
opt_into_json(replaceable)?,
opt_into_json(confirmation_target)?,
opt_into_json(estimate_mode)?,
];
self.call("sendtoaddress", handle_defaults(&mut args, &vec![null(); 6]))
}
fn get_peer_info(&self) -> Result<Vec<json::GetPeerInfoResult>> {
self.call("getpeerinfo", &[])
}
fn ping(&self) -> Result<()> {
self.call("ping", &[])
}
fn send_raw_transaction<R: RawTx>(&self, tx: R) -> Result<sha256d::Hash> {
self.call("sendrawtransaction", &[tx.raw_hex().into()])
}
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()]))
}
fn wait_for_new_block(&self, timeout: u64) -> Result<json::BlockRef> {
self.call("waitfornewblock", &[into_json(timeout)?])
}
fn wait_for_block(&self, blockhash: &sha256d::Hash, timeout: u64) -> Result<json::BlockRef> {
let args = [into_json(blockhash)?, into_json(timeout)?];
self.call("waitforblock", &args)
}
}
pub struct Client {
client: jsonrpc::client::Client,
}
impl Client {
pub fn new(url: String, auth: Auth) -> Result<Self> {
let (user, pass) = auth.get_user_pass()?;
Ok(Client {
client: jsonrpc::client::Client::new(url, user, pass),
})
}
pub fn from_jsonrpc(client: jsonrpc::client::Client) -> Client {
Client {
client: client,
}
}
}
impl RpcApi for Client {
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, &args);
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()?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin;
use serde_json;
#[test]
fn test_raw_tx() {
use bitcoin::consensus::encode;
let client = Client::new("http://localhost/".into(), Auth::None).unwrap();
let tx: bitcoin::Transaction = encode::deserialize(&hex::decode("0200000001586bd02815cf5faabfec986a4e50d25dbee089bd2758621e61c5fab06c334af0000000006b483045022100e85425f6d7c589972ee061413bcf08dc8c8e589ce37b217535a42af924f0e4d602205c9ba9cb14ef15513c9d946fa1c4b797883e748e8c32171bdf6166583946e35c012103dae30a4d7870cd87b45dd53e6012f71318fdd059c1c2623b8cc73f8af287bb2dfeffffff021dc4260c010000001976a914f602e88b2b5901d8aab15ebe4a97cf92ec6e03b388ac00e1f505000000001976a914687ffeffe8cf4e4c038da46a9b1d37db385a472d88acfd211500").unwrap()).unwrap();
assert!(client.send_raw_transaction(&tx).is_err());
assert!(client.send_raw_transaction(&encode::serialize(&tx)).is_err());
assert!(client.send_raw_transaction("deadbeef").is_err());
assert!(client.send_raw_transaction("deadbeef".to_owned()).is_err());
}
fn test_handle_defaults_inner() -> 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(())
}
#[test]
fn test_handle_defaults() {
test_handle_defaults_inner().unwrap();
}
}