bitcoin_rpc/lib.rs
1// Copyright 2018 Jean Pierre Dudey <jeandudey@hotmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9extern crate jsonrpc;
10
11extern crate serde;
12extern crate strason;
13
14extern crate bitcoin;
15extern crate bitcoin_rpc_json;
16
17extern crate failure;
18#[macro_use]
19extern crate failure_derive;
20
21/// Blockchain related RPC result types.
22pub mod blockchain {
23 pub use bitcoin_rpc_json::blockchain::*;
24}
25
26/// Mining related RPC result types.
27pub mod mining {
28 pub use bitcoin_rpc_json::mining::*;
29}
30
31/// Network related RPC result types.
32pub mod net {
33 pub use bitcoin_rpc_json::net::*;
34}
35
36use jsonrpc::client::Client;
37use strason::Json;
38
39use failure::ResultExt;
40
41macro_rules! rpc_request {
42 ($client:expr, $name:expr, $params:expr) => {
43 {
44 let request = $client.build_request($name, $params);
45 let response = $client.send_request(&request)
46 .context(ErrorKind::BadResponse)?;
47 response.into_result()
48 .context(ErrorKind::MalformedResponse)?
49 }
50 }
51}
52
53macro_rules! rpc_method {
54 (
55 $(#[$outer:meta])*
56 pub fn $rpc_method:ident(&self) -> RpcResult<$ty:ty>;
57 ) => {
58 $(#[$outer])*
59 pub fn $rpc_method(&self) -> $crate::RpcResult<$ty> {
60 let v: $ty = rpc_request!(&self.client,
61 stringify!($rpc_method).to_string(),
62 vec![]);
63 Ok(v)
64 }
65 };
66 (
67 $(#[$outer:meta])*
68 pub fn $rpc_method:ident(&self, $($param:ident : $pty:ty),+) -> RpcResult<$ty:ty>;
69 ) => {
70 $(#[$outer])*
71 pub fn $rpc_method(&self, $($param: $pty),+) -> $crate::RpcResult<$ty> {
72 let mut params = Vec::new();
73 $(
74 params.push(Json::from_serialize(&$param).unwrap());
75 )+
76
77 let v: $ty = rpc_request!(&self.client,
78 stringify!($rpc_method).to_string(),
79 params);
80 Ok(v)
81 }
82 }
83}
84
85pub type RpcResult<T> = Result<T, Error>;
86
87/// A Handle to a Bitcoin JSON-RPC connection
88pub struct BitcoinRpc {
89 client: Client,
90}
91
92impl BitcoinRpc {
93 /// Creates a client to a bitcoind JSON-RPC server.
94 pub fn new(url: String, user: Option<String>, pass: Option<String>) -> Self {
95 // Check that if we have a password, we have a username; other way
96 // around is ok.
97 debug_assert!(pass.is_none() || user.is_some());
98
99 BitcoinRpc { client: Client::new(url, user, pass) }
100 }
101
102 // blockchain
103
104 rpc_method! {
105 /// Returns the numbers of block in the longest chain.
106 pub fn getblockcount(&self) -> RpcResult<u64>;
107 }
108
109 rpc_method! {
110 // TODO: Use Sha256dHash from rust-bitcoin
111 /// Returns the hash of the best (tip) block in the longest blockchain.
112 pub fn getbestblockhash(&self) -> RpcResult<String>;
113 }
114
115 rpc_method! {
116 /// Waits for a specific new block and returns useful info about it.
117 /// Returns the current block on timeout or exit.
118 ///
119 /// # Arguments
120 ///
121 /// 1. `timeout`: Time in milliseconds to wait for a response. 0
122 /// indicates no timeout.
123 pub fn waitfornewblock(
124 &self,
125 timeout: u64
126 ) -> RpcResult<blockchain::BlockRef>;
127 }
128
129 rpc_method! {
130 /// Waits for a specific new block and returns useful info about it.
131 /// Returns the current block on timeout or exit.
132 ///
133 /// # Arguments
134 ///
135 /// 1. `blockhash`: Block hash to wait for.
136 /// 2. `timeout`: Time in milliseconds to wait for a response. 0
137 /// indicates no timeout.
138 // TODO: Use Sha256dHash
139 pub fn waitforblock(
140 &self,
141 blockhash: String,
142 timeout: u64
143 ) -> RpcResult<blockchain::BlockRef>;
144 }
145
146 rpc_method! {
147 /// Returns a data structure containing various state info regarding
148 /// blockchain processing.
149 pub fn getblockchaininfo(&self) -> RpcResult<blockchain::BlockchainInfo>;
150 }
151
152 // mining
153
154 pub fn estimatesmartfee<E>(
155 &self,
156 conf_target: u16,
157 estimate_mode: E,
158 ) -> Result<mining::EstimateSmartFee, Error>
159 where E:
160 Into<Option<mining::EstimateMode>>
161 {
162 let mut params = Vec::new();
163 params.push(Json::from_serialize(conf_target).unwrap());
164 if let Some(estimate_mode) = estimate_mode.into() {
165 params.push(Json::from_serialize(estimate_mode).unwrap())
166 }
167
168 let response = rpc_request!(&self.client,
169 "estimatesmartfee".to_string(),
170 params);
171 Ok(response)
172 }
173
174 // net
175
176 rpc_method! {
177 /// Returns the number of connections to other nodes.
178 pub fn getconnectioncount(&self) -> RpcResult<u64>;
179 }
180
181 rpc_method! {
182 /// Requests that a ping be sent to all other nodes, to measure ping
183 /// time.
184 ///
185 /// Results provided in `getpeerinfo`, `pingtime` and `pingwait` fields
186 /// are decimal seconds.
187 ///
188 /// Ping command is handled in queue with all other commands, so it
189 /// measures processing backlog, not just network ping.
190 pub fn ping(&self) -> RpcResult<()>;
191 }
192
193 rpc_method! {
194 /// Returns data about each connected network node as an array of
195 /// [`PeerInfo`][]
196 ///
197 /// [`PeerInfo`]: net/struct.PeerInfo.html
198 pub fn getpeerinfo(&self) -> RpcResult<Vec<net::PeerInfo>>;
199 }
200
201 rpc_method! {
202 /// Attempts to add or remove a node from the addnode list.
203 ///
204 /// Or try a connection to a node once.
205 ///
206 /// Nodes added using `addnode` (or `-connect`) are protected from DoS
207 /// disconnection and are not required to be full nodes/support SegWit
208 /// as other outbound peers are (though such peers will not be synced
209 /// from).
210 ///
211 /// # Arguments:
212 ///
213 /// 1. `node`: The node (see [`getpeerinfo`][] for nodes)
214 /// 2. `command`: `AddNode::Add` to add a node to the list,
215 /// `AddNode::Remove` to remove a node from the list, `AddNode::OneTry`
216 /// to try a connection to the node once
217 ///
218 /// [`getpeerinfo`]: #method.getpeerinfo
219 pub fn addnode(
220 &self,
221 node: &str,
222 commnad: net::AddNode
223 ) -> RpcResult<()>;
224 }
225
226 rpc_method! {
227 pub fn getnetworkinfo(&self) -> RpcResult<net::NetworkInfo>;
228 }
229}
230
231/// The error type for bitcoin JSON-RPC operations.
232#[derive(Debug)]
233pub struct Error {
234 kind: failure::Context<ErrorKind>,
235}
236
237impl From<ErrorKind> for Error {
238 fn from(e: ErrorKind) -> Error {
239 Error {
240 kind: failure::Context::new(e),
241 }
242 }
243}
244
245impl From<failure::Context<ErrorKind>> for Error {
246 fn from(e: failure::Context<ErrorKind>) -> Error {
247 Error {
248 kind: e,
249 }
250 }
251}
252
253/// The kind of error.
254#[derive(Clone, Copy, Debug, Eq, PartialEq, Fail)]
255pub enum ErrorKind {
256 /// The request resulted in an error.
257 #[fail(display = "Request resulted in an error")]
258 BadResponse,
259 /// The received response format is malformed.
260 #[fail(display = "Response format is invalid")]
261 MalformedResponse,
262}