1
2pub use bdk_bitcoind_rpc::bitcoincore_rpc::{self, json, jsonrpc, Auth, Client, Error, RpcApi};
3
4use std::borrow::Borrow;
5
6use bdk_bitcoind_rpc::bitcoincore_rpc::Result as RpcResult;
7use bitcoin::address::NetworkUnchecked;
8use bitcoin::hex::FromHex;
9use bitcoin::{Address, Amount, Transaction};
10use serde::{self, Deserialize, Serialize};
11use serde::de::Error as SerdeError;
12
13use crate::{BlockHeight, BlockRef, TxStatus, DEEPLY_CONFIRMED};
14
15const RPC_VERIFY_ALREADY_IN_UTXO_SET: i32 = -27;
17
18const RPC_INVALID_ADDRESS_OR_KEY: i32 = -5;
20
21#[derive(Debug)]
23pub struct BitcoinRpcClient {
24 client: Client,
25 url: String,
26 auth: Auth,
27}
28
29impl BitcoinRpcClient {
30 pub fn new(url: &str, auth: Auth) -> Result<Self, Error> {
31 Ok(BitcoinRpcClient {
32 client: Client::new(url, auth.clone())?,
33 url: url.to_owned(),
34 auth: auth,
35 })
36 }
37}
38
39impl RpcApi for BitcoinRpcClient {
40 fn call<T: for<'a> serde::de::Deserialize<'a>>(
41 &self, cmd: &str, args: &[serde_json::Value],
42 ) -> Result<T, Error> {
43 self.client.call(cmd, args)
44 }
45}
46
47impl Clone for BitcoinRpcClient {
48 fn clone(&self) -> Self {
49 Self::new(&self.url, self.auth.clone()).unwrap()
50 }
51}
52
53mod serde_hex {
57 use bitcoin::hex::{DisplayHex, FromHex};
58 use serde::de::Error;
59 use serde::{Deserializer, Serializer};
60
61 pub fn serialize<S: Serializer>(b: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
62 s.serialize_str(&b.to_lower_hex_string())
63 }
64
65 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
66 let hex_str: String = ::serde::Deserialize::deserialize(d)?;
67 Ok(FromHex::from_hex(&hex_str).map_err(D::Error::custom)?)
68 }
69
70 pub mod opt {
71 use bitcoin::hex::{DisplayHex, FromHex};
72 use serde::de::Error;
73 use serde::{Deserializer, Serializer};
74
75 pub fn serialize<S: Serializer>(b: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error> {
76 match *b {
77 None => s.serialize_none(),
78 Some(ref b) => s.serialize_str(&b.to_lower_hex_string()),
79 }
80 }
81
82 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Vec<u8>>, D::Error> {
83 let hex_str: String = ::serde::Deserialize::deserialize(d)?;
84 Ok(Some(FromHex::from_hex(&hex_str).map_err(D::Error::custom)?))
85 }
86 }
87}
88
89fn deserialize_hex_array_opt<'de, D>(deserializer: D) -> Result<Option<Vec<Vec<u8>>>, D::Error>
91where
92 D: serde::Deserializer<'de>,
93{
94 let v: Vec<String> = Vec::deserialize(deserializer)?;
98 let mut res = Vec::new();
99 for h in v.into_iter() {
100 res.push(FromHex::from_hex(&h).map_err(D::Error::custom)?);
101 }
102 Ok(Some(res))
103}
104
105#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
106#[serde(rename_all = "camelCase")]
107pub struct GetRawTransactionResultVinScriptSig {
108 pub asm: String,
109 #[serde(with = "serde_hex")]
110 pub hex: Vec<u8>,
111}
112
113#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
114#[serde(rename_all = "camelCase")]
115pub struct GetRawTransactionResultVin {
116 pub sequence: u32,
117 #[serde(default, with = "serde_hex::opt")]
119 pub coinbase: Option<Vec<u8>>,
120 pub txid: Option<bitcoin::Txid>,
122 pub vout: Option<u32>,
124 pub script_sig: Option<GetRawTransactionResultVinScriptSig>,
126 #[serde(default, deserialize_with = "deserialize_hex_array_opt")]
128 pub txinwitness: Option<Vec<Vec<u8>>>,
129}
130
131#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
132#[serde(rename_all = "camelCase")]
133pub struct GetRawTransactionResultVout {
134 #[serde(with = "bitcoin::amount::serde::as_btc")]
135 pub value: Amount,
136 pub n: u32,
137 pub script_pub_key: GetRawTransactionResultVoutScriptPubKey,
138}
139
140#[allow(non_camel_case_types)]
141#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
142#[serde(rename_all = "lowercase")]
143pub enum ScriptPubkeyType {
144 Nonstandard,
145 Anchor,
146 Pubkey,
147 PubkeyHash,
148 ScriptHash,
149 MultiSig,
150 NullData,
151 Witness_v0_KeyHash,
152 Witness_v0_ScriptHash,
153 Witness_v1_Taproot,
154 Witness_Unknown,
155}
156
157#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
158#[serde(rename_all = "camelCase")]
159pub struct GetRawTransactionResultVoutScriptPubKey {
160 pub asm: String,
161 #[serde(with = "serde_hex")]
162 pub hex: Vec<u8>,
163 pub req_sigs: Option<usize>,
164 #[serde(rename = "type")]
165 pub type_: Option<ScriptPubkeyType>,
166 #[serde(default)]
168 pub addresses: Vec<Address<NetworkUnchecked>>,
169 #[serde(default)]
171 pub address: Option<Address<NetworkUnchecked>>,
172}
173
174#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
175#[serde(rename_all = "camelCase")]
176pub struct GetRawTransactionResult {
177 #[serde(rename = "in_active_chain")]
178 pub in_active_chain: Option<bool>,
179 #[serde(with = "serde_hex")]
180 pub hex: Vec<u8>,
181 pub txid: bitcoin::Txid,
182 pub hash: bitcoin::Wtxid,
183 pub size: usize,
184 pub vsize: usize,
185 pub version: u32,
186 pub locktime: u32,
187 pub vin: Vec<GetRawTransactionResultVin>,
188 pub vout: Vec<GetRawTransactionResultVout>,
189 pub blockhash: Option<bitcoin::BlockHash>,
190 pub confirmations: Option<u32>,
191 pub time: Option<usize>,
192 pub blocktime: Option<usize>,
193}
194
195fn into_json<T>(val: T) -> RpcResult<serde_json::Value>
197where
198 T: serde::ser::Serialize,
199{
200 Ok(serde_json::to_value(val)?)
201}
202
203fn opt_into_json<T>(opt: Option<T>) -> RpcResult<serde_json::Value>
205where
206 T: serde::ser::Serialize,
207{
208 match opt {
209 Some(val) => Ok(into_json(val)?),
210 None => Ok(serde_json::Value::Null),
211 }
212}
213
214fn handle_defaults<'a, 'b>(
230 args: &'a mut [serde_json::Value],
231 defaults: &'b [serde_json::Value],
232) -> &'a [serde_json::Value] {
233 assert!(args.len() >= defaults.len());
234
235 let mut first_non_null_optional_idx = None;
238 for i in 0..defaults.len() {
239 let args_i = args.len() - 1 - i;
240 let defaults_i = defaults.len() - 1 - i;
241 if args[args_i] == serde_json::Value::Null {
242 if first_non_null_optional_idx.is_some() {
243 if defaults[defaults_i] == serde_json::Value::Null {
244 panic!("Missing `default` for argument idx {}", args_i);
245 }
246 args[args_i] = defaults[defaults_i].clone();
247 }
248 } else if first_non_null_optional_idx.is_none() {
249 first_non_null_optional_idx = Some(args_i);
250 }
251 }
252
253 let required_num = args.len() - defaults.len();
254
255 if let Some(i) = first_non_null_optional_idx {
256 &args[..i + 1]
257 } else {
258 &args[..required_num]
259 }
260}
261
262fn null() -> serde_json::Value {
264 serde_json::Value::Null
265}
266
267pub trait BitcoinRpcErrorExt: Borrow<Error> {
268 fn is_not_found(&self) -> bool {
270 if let Error::JsonRpc(jsonrpc::Error::Rpc(e)) = self.borrow() {
271 e.code == RPC_INVALID_ADDRESS_OR_KEY
272 } else {
273 false
274 }
275 }
276
277 fn is_in_utxo_set(&self) -> bool {
279 if let Error::JsonRpc(jsonrpc::Error::Rpc(e)) = self.borrow() {
280 e.code == RPC_VERIFY_ALREADY_IN_UTXO_SET
281 } else {
282 false
283 }
284 }
285
286 fn is_already_in_mempool(&self) -> bool {
287 if let Error::JsonRpc(jsonrpc::Error::Rpc(e)) = self.borrow() {
288 e.message.contains("txn-already-in-mempool")
289 } else {
290 false
291 }
292 }
293}
294impl BitcoinRpcErrorExt for Error {}
295
296pub trait BitcoinRpcExt: RpcApi {
297 fn custom_get_raw_transaction_info(
298 &self,
299 txid: &bitcoin::Txid,
300 block_hash: Option<&bitcoin::BlockHash>,
301 ) -> RpcResult<Option<GetRawTransactionResult>> {
302 let mut args = [into_json(txid)?, into_json(true)?, opt_into_json(block_hash)?];
303 match self.call("getrawtransaction", handle_defaults(&mut args, &[null()])) {
304 Ok(ret) => Ok(Some(ret)),
305 Err(e) if e.is_not_found() => Ok(None),
306 Err(e) => Err(e),
307 }
308 }
309
310 fn broadcast_tx(&self, tx: &Transaction) -> Result<(), Error> {
311 match self.send_raw_transaction(tx) {
312 Ok(_) => Ok(()),
313 Err(e) if e.is_in_utxo_set() => Ok(()),
314 Err(e) => Err(e),
315 }
316 }
317
318 fn tip(&self) -> Result<BlockRef, Error> {
319 let height = self.get_block_count()?;
320 let hash = self.get_block_hash(height)?;
321 Ok(BlockRef { height: height as BlockHeight, hash })
322 }
323
324 fn deep_tip(&self) -> Result<BlockRef, Error> {
325 let tip = self.get_block_count()?;
326 let height = tip.saturating_sub(DEEPLY_CONFIRMED as u64);
327 let hash = self.get_block_hash(height)?;
328 Ok(BlockRef { height: height as BlockHeight, hash })
329 }
330
331 fn tx_status(&self, txid: &bitcoin::Txid) -> Result<TxStatus, Error> {
332 match self.custom_get_raw_transaction_info(txid, None)? {
333 Some(tx) => match tx.blockhash {
334 Some(hash) => {
335 let block = self.get_block_header_info(&hash)?;
336 if block.confirmations > 0 {
337 Ok(TxStatus::Confirmed(BlockRef { height: block.height as BlockHeight, hash: block.hash }))
338 } else {
339 Ok(TxStatus::Mempool)
340 }
341 },
342 None => Ok(TxStatus::Mempool),
343 },
344 None => Ok(TxStatus::NotFound)
345 }
346 }
347}
348
349impl <T: RpcApi> BitcoinRpcExt for T {}