extern crate jsonrpc;
extern crate serde;
extern crate strason;
extern crate bitcoin;
extern crate bitcoin_rpc_json;
extern crate failure;
#[macro_use]
extern crate failure_derive;
pub mod blockchain {
pub use bitcoin_rpc_json::blockchain::*;
}
pub mod mining {
pub use bitcoin_rpc_json::mining::*;
}
pub mod net {
pub use bitcoin_rpc_json::net::*;
}
use jsonrpc::client::Client;
use strason::Json;
use failure::ResultExt;
macro_rules! rpc_request {
($client:expr, $name:expr, $params:expr) => {
{
let request = $client.build_request($name, $params);
let response = $client.send_request(&request)
.context(ErrorKind::BadResponse)?;
response.into_result()
.context(ErrorKind::MalformedResponse)?
}
}
}
macro_rules! rpc_method {
(
$(#[$outer:meta])*
pub fn $rpc_method:ident(&self) -> RpcResult<$ty:ty>;
) => {
$(#[$outer])*
pub fn $rpc_method(&self) -> $crate::RpcResult<$ty> {
let v: $ty = rpc_request!(&self.client,
stringify!($rpc_method).to_string(),
vec![]);
Ok(v)
}
};
(
$(#[$outer:meta])*
pub fn $rpc_method:ident(&self, $($param:ident : $pty:ty),+) -> RpcResult<$ty:ty>;
) => {
$(#[$outer])*
pub fn $rpc_method(&self, $($param: $pty),+) -> $crate::RpcResult<$ty> {
let mut params = Vec::new();
$(
params.push(Json::from_serialize(&$param).unwrap());
)+
let v: $ty = rpc_request!(&self.client,
stringify!($rpc_method).to_string(),
params);
Ok(v)
}
}
}
pub type RpcResult<T> = Result<T, Error>;
pub struct BitcoinRpc {
client: Client,
}
impl BitcoinRpc {
pub fn new(url: String, user: Option<String>, pass: Option<String>) -> Self {
debug_assert!(pass.is_none() || user.is_some());
BitcoinRpc { client: Client::new(url, user, pass) }
}
rpc_method! {
pub fn getblockcount(&self) -> RpcResult<u64>;
}
rpc_method! {
pub fn getbestblockhash(&self) -> RpcResult<String>;
}
rpc_method! {
pub fn waitfornewblock(
&self,
timeout: u64
) -> RpcResult<blockchain::BlockRef>;
}
rpc_method! {
pub fn waitforblock(
&self,
blockhash: String,
timeout: u64
) -> RpcResult<blockchain::BlockRef>;
}
rpc_method! {
pub fn getblockchaininfo(&self) -> RpcResult<blockchain::BlockchainInfo>;
}
pub fn estimatesmartfee<E>(
&self,
conf_target: u16,
estimate_mode: E,
) -> Result<mining::EstimateSmartFee, Error>
where E:
Into<Option<mining::EstimateMode>>
{
let mut params = Vec::new();
params.push(Json::from_serialize(conf_target).unwrap());
if let Some(estimate_mode) = estimate_mode.into() {
params.push(Json::from_serialize(estimate_mode).unwrap())
}
let response = rpc_request!(&self.client,
"estimatesmartfee".to_string(),
params);
Ok(response)
}
rpc_method! {
pub fn getconnectioncount(&self) -> RpcResult<u64>;
}
rpc_method! {
pub fn ping(&self) -> RpcResult<()>;
}
rpc_method! {
pub fn getpeerinfo(&self) -> RpcResult<Vec<net::PeerInfo>>;
}
rpc_method! {
pub fn addnode(
&self,
node: &str,
commnad: net::AddNode
) -> RpcResult<()>;
}
rpc_method! {
pub fn getnetworkinfo(&self) -> RpcResult<net::NetworkInfo>;
}
}
#[derive(Debug)]
pub struct Error {
kind: failure::Context<ErrorKind>,
}
impl From<ErrorKind> for Error {
fn from(e: ErrorKind) -> Error {
Error {
kind: failure::Context::new(e),
}
}
}
impl From<failure::Context<ErrorKind>> for Error {
fn from(e: failure::Context<ErrorKind>) -> Error {
Error {
kind: e,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)]
pub enum ErrorKind {
#[fail(display = "Request resulted in an error")]
BadResponse,
#[fail(display = "Response format is invalid")]
MalformedResponse,
}