1use std::collections::HashMap;
12use std::fs::File;
13use std::iter::FromIterator;
14use std::path::PathBuf;
15use std::{fmt, result};
16
17use crate::{bitcoin, deserialize_hex};
18use bitcoin_private::hex::exts::DisplayHex;
19use jsonrpc;
20use serde;
21use serde_json;
22
23use crate::bitcoin::address::{NetworkUnchecked, NetworkChecked};
24use crate::bitcoin::hashes::hex::FromHex;
25use crate::bitcoin::secp256k1::ecdsa::Signature;
26use crate::bitcoin::{
27 Address, Amount, Block, OutPoint, PrivateKey, PublicKey, Script, Transaction,
28};
29use log::Level::{Debug, Trace, Warn};
30
31use crate::error::*;
32use crate::json;
33use crate::queryable;
34
35pub type Result<T> = result::Result<T, Error>;
38
39#[derive(Clone, Debug, Serialize, Deserialize)]
42pub struct JsonOutPoint {
43 pub txid: bitcoin::Txid,
44 pub vout: u32,
45}
46
47impl From<OutPoint> for JsonOutPoint {
48 fn from(o: OutPoint) -> JsonOutPoint {
49 JsonOutPoint {
50 txid: o.txid,
51 vout: o.vout,
52 }
53 }
54}
55
56impl Into<OutPoint> for JsonOutPoint {
57 fn into(self) -> OutPoint {
58 OutPoint {
59 txid: self.txid,
60 vout: self.vout,
61 }
62 }
63}
64
65fn into_json<T>(val: T) -> Result<serde_json::Value>
67where
68 T: serde::ser::Serialize,
69{
70 Ok(serde_json::to_value(val)?)
71}
72
73fn opt_into_json<T>(opt: Option<T>) -> Result<serde_json::Value>
75where
76 T: serde::ser::Serialize,
77{
78 match opt {
79 Some(val) => Ok(into_json(val)?),
80 None => Ok(serde_json::Value::Null),
81 }
82}
83
84fn null() -> serde_json::Value {
86 serde_json::Value::Null
87}
88
89fn empty_arr() -> serde_json::Value {
91 serde_json::Value::Array(vec![])
92}
93
94fn empty_obj() -> serde_json::Value {
96 serde_json::Value::Object(Default::default())
97}
98
99fn handle_defaults<'a, 'b>(
115 args: &'a mut [serde_json::Value],
116 defaults: &'b [serde_json::Value],
117) -> &'a [serde_json::Value] {
118 assert!(args.len() >= defaults.len());
119
120 let mut first_non_null_optional_idx = None;
123 for i in 0..defaults.len() {
124 let args_i = args.len() - 1 - i;
125 let defaults_i = defaults.len() - 1 - i;
126 if args[args_i] == serde_json::Value::Null {
127 if first_non_null_optional_idx.is_some() {
128 if defaults[defaults_i] == serde_json::Value::Null {
129 panic!("Missing `default` for argument idx {}", args_i);
130 }
131 args[args_i] = defaults[defaults_i].clone();
132 }
133 } else if first_non_null_optional_idx.is_none() {
134 first_non_null_optional_idx = Some(args_i);
135 }
136 }
137
138 let required_num = args.len() - defaults.len();
139
140 if let Some(i) = first_non_null_optional_idx {
141 &args[..i + 1]
142 } else {
143 &args[..required_num]
144 }
145}
146
147fn opt_result<T: for<'a> serde::de::Deserialize<'a>>(
149 result: serde_json::Value,
150) -> Result<Option<T>> {
151 if result == serde_json::Value::Null {
152 Ok(None)
153 } else {
154 Ok(serde_json::from_value(result)?)
155 }
156}
157
158pub trait RawTx: Sized + Clone {
160 fn raw_hex(self) -> String;
161}
162
163impl<'a> RawTx for &'a Transaction {
164 fn raw_hex(self) -> String {
165 bitcoin::consensus::encode::serialize_hex(self)
166 }
167}
168
169impl<'a> RawTx for &'a [u8] {
170 fn raw_hex(self) -> String {
171 self.to_lower_hex_string()
172 }
173}
174
175impl<'a> RawTx for &'a Vec<u8> {
176 fn raw_hex(self) -> String {
177 self.to_lower_hex_string()
178 }
179}
180
181impl<'a> RawTx for &'a str {
182 fn raw_hex(self) -> String {
183 self.to_owned()
184 }
185}
186
187impl RawTx for String {
188 fn raw_hex(self) -> String {
189 self
190 }
191}
192
193#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
195pub enum Auth {
196 None,
197 UserPass(String, String),
198 CookieFile(PathBuf),
199}
200
201impl Auth {
202 pub fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
204 use std::io::Read;
205 match self {
206 Auth::None => Ok((None, None)),
207 Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
208 Auth::CookieFile(path) => {
209 let mut file = File::open(path)?;
210 let mut contents = String::new();
211 file.read_to_string(&mut contents)?;
212 let mut split = contents.splitn(2, ":");
213 Ok((
214 Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
215 Some(split.next().ok_or(Error::InvalidCookieFile)?.into()),
216 ))
217 }
218 }
219 }
220}
221
222pub trait RpcApi: Sized {
223 fn call<T: for<'a> serde::de::Deserialize<'a>>(
225 &self,
226 cmd: &str,
227 args: &[serde_json::Value],
228 ) -> Result<T>;
229
230 fn get_by_id<T: queryable::Queryable<Self>>(
232 &self,
233 id: &<T as queryable::Queryable<Self>>::Id,
234 ) -> Result<T> {
235 T::query(&self, &id)
236 }
237
238 fn get_network_info(&self) -> Result<json::GetNetworkInfoResult> {
239 self.call("getnetworkinfo", &[])
240 }
241
242 fn get_index_info(&self) -> Result<json::GetIndexInfoResult> {
243 self.call("getindexinfo", &[])
244 }
245
246 fn version(&self) -> Result<usize> {
247 #[derive(Deserialize)]
248 struct Response {
249 pub version: usize,
250 }
251 let res: Response = self.call("getnetworkinfo", &[])?;
252 Ok(res.version)
253 }
254
255 fn add_multisig_address(
256 &self,
257 nrequired: usize,
258 keys: &[json::PubKeyOrAddress],
259 label: Option<&str>,
260 address_type: Option<json::AddressType>,
261 ) -> Result<json::AddMultiSigAddressResult> {
262 let mut args = [
263 into_json(nrequired)?,
264 into_json(keys)?,
265 opt_into_json(label)?,
266 opt_into_json(address_type)?,
267 ];
268 self.call("addmultisigaddress", handle_defaults(&mut args, &[into_json("")?, null()]))
269 }
270
271 fn load_wallet(&self, wallet: &str) -> Result<json::LoadWalletResult> {
272 self.call("loadwallet", &[wallet.into()])
273 }
274
275 fn unload_wallet(&self, wallet: Option<&str>) -> Result<Option<json::UnloadWalletResult>> {
276 let mut args = [opt_into_json(wallet)?];
277 self.call("unloadwallet", handle_defaults(&mut args, &[null()]))
278 }
279
280 fn create_wallet(
281 &self,
282 wallet: &str,
283 disable_private_keys: Option<bool>,
284 blank: Option<bool>,
285 passphrase: Option<&str>,
286 avoid_reuse: Option<bool>,
287 ) -> Result<json::LoadWalletResult> {
288 let mut args = [
289 wallet.into(),
290 opt_into_json(disable_private_keys)?,
291 opt_into_json(blank)?,
292 opt_into_json(passphrase)?,
293 opt_into_json(avoid_reuse)?,
294 ];
295 self.call(
296 "createwallet",
297 handle_defaults(&mut args, &[false.into(), false.into(), into_json("")?, false.into()]),
298 )
299 }
300
301 fn list_wallets(&self) -> Result<Vec<String>> {
302 self.call("listwallets", &[])
303 }
304
305 fn list_wallet_dir(&self) -> Result<Vec<String>> {
306 let result: json::ListWalletDirResult = self.call("listwalletdir", &[])?;
307 let names = result.wallets.into_iter().map(|x| x.name).collect();
308 Ok(names)
309 }
310
311 fn get_wallet_info(&self) -> Result<json::GetWalletInfoResult> {
312 self.call("getwalletinfo", &[])
313 }
314
315 fn backup_wallet(&self, destination: Option<&str>) -> Result<()> {
316 let mut args = [opt_into_json(destination)?];
317 self.call("backupwallet", handle_defaults(&mut args, &[null()]))
318 }
319
320 fn dump_private_key(&self, address: &Address) -> Result<PrivateKey> {
321 self.call("dumpprivkey", &[address.to_string().into()])
322 }
323
324 fn encrypt_wallet(&self, passphrase: &str) -> Result<()> {
325 self.call("encryptwallet", &[into_json(passphrase)?])
326 }
327
328 fn get_difficulty(&self) -> Result<f64> {
329 self.call("getdifficulty", &[])
330 }
331
332 fn get_connection_count(&self) -> Result<usize> {
333 self.call("getconnectioncount", &[])
334 }
335
336 fn get_block(&self, hash: &bitcoin::BlockHash) -> Result<Block> {
337 let hex: String = self.call("getblock", &[into_json(hash)?, 0.into()])?;
338 deserialize_hex(&hex)
339 }
340
341 fn get_block_hex(&self, hash: &bitcoin::BlockHash) -> Result<String> {
342 self.call("getblock", &[into_json(hash)?, 0.into()])
343 }
344
345 fn get_block_info(&self, hash: &bitcoin::BlockHash) -> Result<json::GetBlockResult> {
346 self.call("getblock", &[into_json(hash)?, 1.into()])
347 }
348 fn get_block_header(&self, hash: &bitcoin::BlockHash) -> Result<bitcoin::block::Header> {
351 let hex: String = self.call("getblockheader", &[into_json(hash)?, false.into()])?;
352 deserialize_hex(&hex)
353 }
354
355 fn get_block_header_info(
356 &self,
357 hash: &bitcoin::BlockHash,
358 ) -> Result<json::GetBlockHeaderResult> {
359 self.call("getblockheader", &[into_json(hash)?, true.into()])
360 }
361
362 fn get_mining_info(&self) -> Result<json::GetMiningInfoResult> {
363 self.call("getmininginfo", &[])
364 }
365
366 fn get_block_template(
367 &self,
368 mode: json::GetBlockTemplateModes,
369 rules: &[json::GetBlockTemplateRules],
370 capabilities: &[json::GetBlockTemplateCapabilities],
371 ) -> Result<json::GetBlockTemplateResult> {
372 #[derive(Serialize)]
373 struct Argument<'a> {
374 mode: json::GetBlockTemplateModes,
375 rules: &'a [json::GetBlockTemplateRules],
376 capabilities: &'a [json::GetBlockTemplateCapabilities],
377 }
378
379 self.call(
380 "getblocktemplate",
381 &[into_json(Argument {
382 mode: mode,
383 rules: rules,
384 capabilities: capabilities,
385 })?],
386 )
387 }
388
389 fn get_blockchain_info(&self) -> Result<json::GetBlockchainInfoResult> {
392 let mut raw: serde_json::Value = self.call("getblockchaininfo", &[])?;
393 Ok(if self.version()? < 190000 {
397 use crate::Error::UnexpectedStructure as err;
398
399 let (bip9_softforks, old_softforks) = {
402 let map = raw.as_object_mut().ok_or(err)?;
403 let bip9_softforks = map.remove("bip9_softforks").ok_or(err)?;
404 let old_softforks = map.remove("softforks").ok_or(err)?;
405 map.insert("softforks".into(), serde_json::Map::new().into());
407 (bip9_softforks, old_softforks)
408 };
409 let mut ret: json::GetBlockchainInfoResult = serde_json::from_value(raw)?;
410
411 for sf in old_softforks.as_array().ok_or(err)?.iter() {
413 let json = sf.as_object().ok_or(err)?;
414 let id = json.get("id").ok_or(err)?.as_str().ok_or(err)?;
415 let reject = json.get("reject").ok_or(err)?.as_object().ok_or(err)?;
416 let active = reject.get("status").ok_or(err)?.as_bool().ok_or(err)?;
417 ret.softforks.insert(
418 id.into(),
419 json::Softfork {
420 type_: json::SoftforkType::Buried,
421 bip9: None,
422 height: None,
423 active: active,
424 },
425 );
426 }
427 for (id, sf) in bip9_softforks.as_object().ok_or(err)?.iter() {
428 #[derive(Deserialize)]
429 struct OldBip9SoftFork {
430 pub status: json::Bip9SoftforkStatus,
431 pub bit: Option<u8>,
432 #[serde(rename = "startTime")]
433 pub start_time: i64,
434 pub timeout: u64,
435 pub since: u32,
436 pub statistics: Option<json::Bip9SoftforkStatistics>,
437 }
438 let sf: OldBip9SoftFork = serde_json::from_value(sf.clone())?;
439 ret.softforks.insert(
440 id.clone(),
441 json::Softfork {
442 type_: json::SoftforkType::Bip9,
443 bip9: Some(json::Bip9SoftforkInfo {
444 status: sf.status,
445 bit: sf.bit,
446 start_time: sf.start_time,
447 timeout: sf.timeout,
448 since: sf.since,
449 statistics: sf.statistics,
450 }),
451 height: None,
452 active: sf.status == json::Bip9SoftforkStatus::Active,
453 },
454 );
455 }
456 ret
457 } else {
458 serde_json::from_value(raw)?
459 })
460 }
461
462 fn get_block_count(&self) -> Result<u64> {
464 self.call("getblockcount", &[])
465 }
466
467 fn get_best_block_hash(&self) -> Result<bitcoin::BlockHash> {
469 self.call("getbestblockhash", &[])
470 }
471
472 fn get_block_hash(&self, height: u64) -> Result<bitcoin::BlockHash> {
474 self.call("getblockhash", &[height.into()])
475 }
476
477 fn get_block_stats(&self, height: u64) -> Result<json::GetBlockStatsResult> {
478 self.call("getblockstats", &[height.into()])
479 }
480
481 fn get_block_stats_fields(
482 &self,
483 height: u64,
484 fields: &[json::BlockStatsFields],
485 ) -> Result<json::GetBlockStatsResultPartial> {
486 self.call("getblockstats", &[height.into(), fields.into()])
487 }
488
489 fn get_raw_transaction(
490 &self,
491 txid: &bitcoin::Txid,
492 block_hash: Option<&bitcoin::BlockHash>,
493 ) -> Result<Transaction> {
494 let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
495 let hex: String = self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))?;
496 deserialize_hex(&hex)
497 }
498
499 fn get_raw_transaction_hex(
500 &self,
501 txid: &bitcoin::Txid,
502 block_hash: Option<&bitcoin::BlockHash>,
503 ) -> Result<String> {
504 let mut args = [into_json(txid)?, into_json(false)?, opt_into_json(block_hash)?];
505 self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
506 }
507
508 fn get_raw_transaction_info(
509 &self,
510 txid: &bitcoin::Txid,
511 block_hash: Option<&bitcoin::BlockHash>,
512 ) -> Result<json::GetRawTransactionResult> {
513 let mut args = [into_json(txid)?, into_json(true)?, opt_into_json(block_hash)?];
514 self.call("getrawtransaction", handle_defaults(&mut args, &[null()]))
515 }
516
517 fn get_block_filter(
518 &self,
519 block_hash: &bitcoin::BlockHash,
520 ) -> Result<json::GetBlockFilterResult> {
521 self.call("getblockfilter", &[into_json(block_hash)?])
522 }
523
524 fn get_balance(
525 &self,
526 minconf: Option<usize>,
527 include_watchonly: Option<bool>,
528 ) -> Result<Amount> {
529 let mut args = ["*".into(), opt_into_json(minconf)?, opt_into_json(include_watchonly)?];
530 Ok(Amount::from_btc(
531 self.call("getbalance", handle_defaults(&mut args, &[0.into(), null()]))?,
532 )?)
533 }
534
535 fn get_balances(&self) -> Result<json::GetBalancesResult> {
536 Ok(self.call("getbalances", &[])?)
537 }
538
539 fn get_received_by_address(&self, address: &Address, minconf: Option<u32>) -> Result<Amount> {
540 let mut args = [address.to_string().into(), opt_into_json(minconf)?];
541 Ok(Amount::from_btc(
542 self.call("getreceivedbyaddress", handle_defaults(&mut args, &[null()]))?,
543 )?)
544 }
545
546 fn get_transaction(
547 &self,
548 txid: &bitcoin::Txid,
549 include_watchonly: Option<bool>,
550 ) -> Result<json::GetTransactionResult> {
551 let mut args = [into_json(txid)?, opt_into_json(include_watchonly)?];
552 self.call("gettransaction", handle_defaults(&mut args, &[null()]))
553 }
554
555 fn list_transactions(
556 &self,
557 label: Option<&str>,
558 count: Option<usize>,
559 skip: Option<usize>,
560 include_watchonly: Option<bool>,
561 ) -> Result<Vec<json::ListTransactionResult>> {
562 let mut args = [
563 label.unwrap_or("*").into(),
564 opt_into_json(count)?,
565 opt_into_json(skip)?,
566 opt_into_json(include_watchonly)?,
567 ];
568 self.call("listtransactions", handle_defaults(&mut args, &[10.into(), 0.into(), null()]))
569 }
570
571 fn list_since_block(
572 &self,
573 blockhash: Option<&bitcoin::BlockHash>,
574 target_confirmations: Option<usize>,
575 include_watchonly: Option<bool>,
576 include_removed: Option<bool>,
577 ) -> Result<json::ListSinceBlockResult> {
578 let mut args = [
579 opt_into_json(blockhash)?,
580 opt_into_json(target_confirmations)?,
581 opt_into_json(include_watchonly)?,
582 opt_into_json(include_removed)?,
583 ];
584 self.call("listsinceblock", handle_defaults(&mut args, &[null()]))
585 }
586
587 fn get_tx_out(
588 &self,
589 txid: &bitcoin::Txid,
590 vout: u32,
591 include_mempool: Option<bool>,
592 ) -> Result<Option<json::GetTxOutResult>> {
593 let mut args = [into_json(txid)?, into_json(vout)?, opt_into_json(include_mempool)?];
594 opt_result(self.call("gettxout", handle_defaults(&mut args, &[null()]))?)
595 }
596
597 fn get_tx_out_proof(
598 &self,
599 txids: &[bitcoin::Txid],
600 block_hash: Option<&bitcoin::BlockHash>,
601 ) -> Result<Vec<u8>> {
602 let mut args = [into_json(txids)?, opt_into_json(block_hash)?];
603 let hex: String = self.call("gettxoutproof", handle_defaults(&mut args, &[null()]))?;
604 Ok(FromHex::from_hex(&hex)?)
605 }
606
607 fn import_public_key(
608 &self,
609 pubkey: &PublicKey,
610 label: Option<&str>,
611 rescan: Option<bool>,
612 ) -> Result<()> {
613 let mut args = [pubkey.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?];
614 self.call("importpubkey", handle_defaults(&mut args, &[into_json("")?, null()]))
615 }
616
617 fn import_private_key(
618 &self,
619 privkey: &PrivateKey,
620 label: Option<&str>,
621 rescan: Option<bool>,
622 ) -> Result<()> {
623 let mut args = [privkey.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?];
624 self.call("importprivkey", handle_defaults(&mut args, &[into_json("")?, null()]))
625 }
626
627 fn import_address(
628 &self,
629 address: &Address,
630 label: Option<&str>,
631 rescan: Option<bool>,
632 ) -> Result<()> {
633 let mut args = [address.to_string().into(), opt_into_json(label)?, opt_into_json(rescan)?];
634 self.call("importaddress", handle_defaults(&mut args, &[into_json("")?, null()]))
635 }
636
637 fn import_address_script(
638 &self,
639 script: &Script,
640 label: Option<&str>,
641 rescan: Option<bool>,
642 p2sh: Option<bool>,
643 ) -> Result<()> {
644 let mut args = [
645 script.to_hex_string().into(),
646 opt_into_json(label)?,
647 opt_into_json(rescan)?,
648 opt_into_json(p2sh)?,
649 ];
650 self.call(
651 "importaddress",
652 handle_defaults(&mut args, &[into_json("")?, true.into(), null()]),
653 )
654 }
655
656 fn import_multi(
657 &self,
658 requests: &[json::ImportMultiRequest],
659 options: Option<&json::ImportMultiOptions>,
660 ) -> Result<Vec<json::ImportMultiResult>> {
661 let mut json_requests = Vec::with_capacity(requests.len());
662 for req in requests {
663 json_requests.push(serde_json::to_value(req)?);
664 }
665 let mut args = [json_requests.into(), opt_into_json(options)?];
666 self.call("importmulti", handle_defaults(&mut args, &[null()]))
667 }
668
669 fn import_descriptors(
670 &self,
671 req: Vec<json::ImportDescriptors>,
672 ) -> Result<Vec<json::ImportMultiResult>> {
673 let json_request = serde_json::to_value(req)?;
674 self.call("importdescriptors", handle_defaults(&mut [json_request.into()], &[null()]))
675 }
676
677 fn list_descriptors(&self, private: Option<bool>) -> Result<json::ListDescriptorsResult> {
678 let mut args = [opt_into_json(private)?];
679 self.call("listdescriptors", handle_defaults(&mut args, &[null()]))
680 }
681
682 fn set_label(&self, address: &Address, label: &str) -> Result<()> {
683 self.call("setlabel", &[address.to_string().into(), label.into()])
684 }
685
686 fn key_pool_refill(&self, new_size: Option<usize>) -> Result<()> {
687 let mut args = [opt_into_json(new_size)?];
688 self.call("keypoolrefill", handle_defaults(&mut args, &[null()]))
689 }
690
691 fn list_unspent(
692 &self,
693 minconf: Option<usize>,
694 maxconf: Option<usize>,
695 addresses: Option<&[&Address<NetworkChecked>]>,
696 include_unsafe: Option<bool>,
697 query_options: Option<json::ListUnspentQueryOptions>,
698 ) -> Result<Vec<json::ListUnspentResultEntry>> {
699 let mut args = [
700 opt_into_json(minconf)?,
701 opt_into_json(maxconf)?,
702 opt_into_json(addresses)?,
703 opt_into_json(include_unsafe)?,
704 opt_into_json(query_options)?,
705 ];
706 let defaults = [into_json(0)?, into_json(9999999)?, empty_arr(), into_json(true)?, null()];
707 self.call("listunspent", handle_defaults(&mut args, &defaults))
708 }
709
710 fn lock_unspent(&self, outputs: &[OutPoint]) -> Result<bool> {
712 let outputs: Vec<_> = outputs
713 .into_iter()
714 .map(|o| serde_json::to_value(JsonOutPoint::from(*o)).unwrap())
715 .collect();
716 self.call("lockunspent", &[false.into(), outputs.into()])
717 }
718
719 fn unlock_unspent(&self, outputs: &[OutPoint]) -> Result<bool> {
720 let outputs: Vec<_> = outputs
721 .into_iter()
722 .map(|o| serde_json::to_value(JsonOutPoint::from(*o)).unwrap())
723 .collect();
724 self.call("lockunspent", &[true.into(), outputs.into()])
725 }
726
727 fn unlock_unspent_all(&self) -> Result<bool> {
729 self.call("lockunspent", &[true.into()])
730 }
731
732 fn list_received_by_address(
733 &self,
734 address_filter: Option<&Address>,
735 minconf: Option<u32>,
736 include_empty: Option<bool>,
737 include_watchonly: Option<bool>,
738 ) -> Result<Vec<json::ListReceivedByAddressResult>> {
739 let mut args = [
740 opt_into_json(minconf)?,
741 opt_into_json(include_empty)?,
742 opt_into_json(include_watchonly)?,
743 opt_into_json(address_filter)?,
744 ];
745 let defaults = [1.into(), false.into(), false.into(), null()];
746 self.call("listreceivedbyaddress", handle_defaults(&mut args, &defaults))
747 }
748
749 fn create_psbt(
750 &self,
751 inputs: &[json::CreateRawTransactionInput],
752 outputs: &HashMap<String, Amount>,
753 locktime: Option<i64>,
754 replaceable: Option<bool>,
755 ) -> Result<String> {
756 let outs_converted = serde_json::Map::from_iter(
757 outputs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.to_btc()))),
758 );
759 self.call(
760 "createpsbt",
761 &[
762 into_json(inputs)?,
763 into_json(outs_converted)?,
764 into_json(locktime)?,
765 into_json(replaceable)?,
766 ],
767 )
768 }
769
770 fn create_raw_transaction_hex(
771 &self,
772 utxos: &[json::CreateRawTransactionInput],
773 outs: &HashMap<String, Amount>,
774 locktime: Option<i64>,
775 replaceable: Option<bool>,
776 ) -> Result<String> {
777 let outs_converted = serde_json::Map::from_iter(
778 outs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.to_btc()))),
779 );
780 let mut args = [
781 into_json(utxos)?,
782 into_json(outs_converted)?,
783 opt_into_json(locktime)?,
784 opt_into_json(replaceable)?,
785 ];
786 let defaults = [into_json(0i64)?, null()];
787 self.call("createrawtransaction", handle_defaults(&mut args, &defaults))
788 }
789
790 fn create_raw_transaction(
791 &self,
792 utxos: &[json::CreateRawTransactionInput],
793 outs: &HashMap<String, Amount>,
794 locktime: Option<i64>,
795 replaceable: Option<bool>,
796 ) -> Result<Transaction> {
797 let hex: String = self.create_raw_transaction_hex(utxos, outs, locktime, replaceable)?;
798 deserialize_hex(&hex)
799 }
800
801 fn decode_raw_transaction<R: RawTx>(
802 &self,
803 tx: R,
804 is_witness: Option<bool>,
805 ) -> Result<json::DecodeRawTransactionResult> {
806 let mut args = [tx.raw_hex().into(), opt_into_json(is_witness)?];
807 let defaults = [null()];
808 self.call("decoderawtransaction", handle_defaults(&mut args, &defaults))
809 }
810
811 fn fund_raw_transaction<R: RawTx>(
812 &self,
813 tx: R,
814 options: Option<&json::FundRawTransactionOptions>,
815 is_witness: Option<bool>,
816 ) -> Result<json::FundRawTransactionResult> {
817 let mut args = [tx.raw_hex().into(), opt_into_json(options)?, opt_into_json(is_witness)?];
818 let defaults = [empty_obj(), null()];
819 self.call("fundrawtransaction", handle_defaults(&mut args, &defaults))
820 }
821
822 #[deprecated]
823 fn sign_raw_transaction<R: RawTx>(
824 &self,
825 tx: R,
826 utxos: Option<&[json::SignRawTransactionInput]>,
827 private_keys: Option<&[PrivateKey]>,
828 sighash_type: Option<json::SigHashType>,
829 ) -> Result<json::SignRawTransactionResult> {
830 let mut args = [
831 tx.raw_hex().into(),
832 opt_into_json(utxos)?,
833 opt_into_json(private_keys)?,
834 opt_into_json(sighash_type)?,
835 ];
836 let defaults = [empty_arr(), empty_arr(), null()];
837 self.call("signrawtransaction", handle_defaults(&mut args, &defaults))
838 }
839
840 fn sign_raw_transaction_with_wallet<R: RawTx>(
841 &self,
842 tx: R,
843 utxos: Option<&[json::SignRawTransactionInput]>,
844 sighash_type: Option<json::SigHashType>,
845 ) -> Result<json::SignRawTransactionResult> {
846 let mut args = [tx.raw_hex().into(), opt_into_json(utxos)?, opt_into_json(sighash_type)?];
847 let defaults = [empty_arr(), null()];
848 self.call("signrawtransactionwithwallet", handle_defaults(&mut args, &defaults))
849 }
850
851 fn sign_raw_transaction_with_key<R: RawTx>(
852 &self,
853 tx: R,
854 privkeys: &[PrivateKey],
855 prevtxs: Option<&[json::SignRawTransactionInput]>,
856 sighash_type: Option<json::SigHashType>,
857 ) -> Result<json::SignRawTransactionResult> {
858 let mut args = [
859 tx.raw_hex().into(),
860 into_json(privkeys)?,
861 opt_into_json(prevtxs)?,
862 opt_into_json(sighash_type)?,
863 ];
864 let defaults = [empty_arr(), null()];
865 self.call("signrawtransactionwithkey", handle_defaults(&mut args, &defaults))
866 }
867
868 fn test_mempool_accept<R: RawTx>(
869 &self,
870 rawtxs: &[R],
871 ) -> Result<Vec<json::TestMempoolAcceptResult>> {
872 let hexes: Vec<serde_json::Value> =
873 rawtxs.to_vec().into_iter().map(|r| r.raw_hex().into()).collect();
874 self.call("testmempoolaccept", &[hexes.into()])
875 }
876
877 fn stop(&self) -> Result<String> {
878 self.call("stop", &[])
879 }
880
881 fn verify_message(
882 &self,
883 address: &Address,
884 signature: &Signature,
885 message: &str,
886 ) -> Result<bool> {
887 let args = [address.to_string().into(), signature.to_string().into(), into_json(message)?];
888 self.call("verifymessage", &args)
889 }
890
891 fn get_new_address(
893 &self,
894 label: Option<&str>,
895 address_type: Option<json::AddressType>,
896 ) -> Result<Address<NetworkUnchecked>> {
897 self.call("getnewaddress", &[opt_into_json(label)?, opt_into_json(address_type)?])
898 }
899
900 fn get_raw_change_address(&self, address_type: Option<json::AddressType>) -> Result<Address<NetworkUnchecked>> {
902 self.call("getrawchangeaddress", &[opt_into_json(address_type)?])
903 }
904
905 fn get_address_info(&self, address: &Address) -> Result<json::GetAddressInfoResult> {
906 self.call("getaddressinfo", &[address.to_string().into()])
907 }
908
909 fn generate_to_address(
913 &self,
914 block_num: u64,
915 address: &Address<NetworkChecked>,
916 ) -> Result<Vec<bitcoin::BlockHash>> {
917 self.call("generatetoaddress", &[block_num.into(), address.to_string().into()])
918 }
919
920 fn generate(&self, block_num: u64, maxtries: Option<u64>) -> Result<Vec<bitcoin::BlockHash>> {
923 self.call("generate", &[block_num.into(), opt_into_json(maxtries)?])
924 }
925
926 fn invalidate_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> {
928 self.call("invalidateblock", &[into_json(block_hash)?])
929 }
930
931 fn reconsider_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> {
933 self.call("reconsiderblock", &[into_json(block_hash)?])
934 }
935
936 fn get_mempool_info(&self) -> Result<json::GetMempoolInfoResult> {
938 self.call("getmempoolinfo", &[])
939 }
940
941 fn get_raw_mempool(&self) -> Result<Vec<bitcoin::Txid>> {
943 self.call("getrawmempool", &[])
944 }
945
946 fn get_raw_mempool_verbose(
948 &self,
949 ) -> Result<HashMap<bitcoin::Txid, json::GetMempoolEntryResult>> {
950 self.call("getrawmempool", &[into_json(true)?])
951 }
952
953 fn get_mempool_entry(&self, txid: &bitcoin::Txid) -> Result<json::GetMempoolEntryResult> {
955 self.call("getmempoolentry", &[into_json(txid)?])
956 }
957
958 fn get_chain_tips(&self) -> Result<json::GetChainTipsResult> {
961 self.call("getchaintips", &[])
962 }
963
964 fn send_to_address(
965 &self,
966 address: &Address<NetworkChecked>,
967 amount: Amount,
968 comment: Option<&str>,
969 comment_to: Option<&str>,
970 subtract_fee: Option<bool>,
971 replaceable: Option<bool>,
972 confirmation_target: Option<u32>,
973 estimate_mode: Option<json::EstimateMode>,
974 ) -> Result<bitcoin::Txid> {
975 let mut args = [
976 address.to_string().into(),
977 into_json(amount.to_btc())?,
978 opt_into_json(comment)?,
979 opt_into_json(comment_to)?,
980 opt_into_json(subtract_fee)?,
981 opt_into_json(replaceable)?,
982 opt_into_json(confirmation_target)?,
983 opt_into_json(estimate_mode)?,
984 ];
985 self.call(
986 "sendtoaddress",
987 handle_defaults(
988 &mut args,
989 &["".into(), "".into(), false.into(), false.into(), 6.into(), null()],
990 ),
991 )
992 }
993
994 fn add_node(&self, addr: &str) -> Result<()> {
997 self.call("addnode", &[into_json(&addr)?, into_json("add")?])
998 }
999
1000 fn remove_node(&self, addr: &str) -> Result<()> {
1002 self.call("addnode", &[into_json(&addr)?, into_json("remove")?])
1003 }
1004
1005 fn onetry_node(&self, addr: &str) -> Result<()> {
1007 self.call("addnode", &[into_json(&addr)?, into_json("onetry")?])
1008 }
1009
1010 fn disconnect_node(&self, addr: &str) -> Result<()> {
1012 self.call("disconnectnode", &[into_json(&addr)?])
1013 }
1014
1015 fn disconnect_node_by_id(&self, node_id: u32) -> Result<()> {
1016 self.call("disconnectnode", &[into_json("")?, into_json(node_id)?])
1017 }
1018
1019 fn get_added_node_info(&self, node: Option<&str>) -> Result<Vec<json::GetAddedNodeInfoResult>> {
1021 if let Some(addr) = node {
1022 self.call("getaddednodeinfo", &[into_json(&addr)?])
1023 } else {
1024 self.call("getaddednodeinfo", &[])
1025 }
1026 }
1027
1028 fn get_node_addresses(
1030 &self,
1031 count: Option<usize>,
1032 ) -> Result<Vec<json::GetNodeAddressesResult>> {
1033 let cnt = count.unwrap_or(1);
1034 self.call("getnodeaddresses", &[into_json(&cnt)?])
1035 }
1036
1037 fn list_banned(&self) -> Result<Vec<json::ListBannedResult>> {
1039 self.call("listbanned", &[])
1040 }
1041
1042 fn clear_banned(&self) -> Result<()> {
1044 self.call("clearbanned", &[])
1045 }
1046
1047 fn add_ban(&self, subnet: &str, bantime: u64, absolute: bool) -> Result<()> {
1049 self.call(
1050 "setban",
1051 &[into_json(&subnet)?, into_json("add")?, into_json(&bantime)?, into_json(&absolute)?],
1052 )
1053 }
1054
1055 fn remove_ban(&self, subnet: &str) -> Result<()> {
1057 self.call("setban", &[into_json(&subnet)?, into_json("remove")?])
1058 }
1059
1060 fn set_network_active(&self, state: bool) -> Result<bool> {
1062 self.call("setnetworkactive", &[into_json(&state)?])
1063 }
1064
1065 fn get_peer_info(&self) -> Result<Vec<json::GetPeerInfoResult>> {
1070 self.call("getpeerinfo", &[])
1071 }
1072
1073 fn ping(&self) -> Result<()> {
1082 self.call("ping", &[])
1083 }
1084
1085 fn send_raw_transaction<R: RawTx>(&self, tx: R) -> Result<bitcoin::Txid> {
1086 self.call("sendrawtransaction", &[tx.raw_hex().into()])
1087 }
1088
1089 fn estimate_smart_fee(
1090 &self,
1091 conf_target: u16,
1092 estimate_mode: Option<json::EstimateMode>,
1093 ) -> Result<json::EstimateSmartFeeResult> {
1094 let mut args = [into_json(conf_target)?, opt_into_json(estimate_mode)?];
1095 self.call("estimatesmartfee", handle_defaults(&mut args, &[null()]))
1096 }
1097
1098 fn wait_for_new_block(&self, timeout: u64) -> Result<json::BlockRef> {
1106 self.call("waitfornewblock", &[into_json(timeout)?])
1107 }
1108
1109 fn wait_for_block(
1118 &self,
1119 blockhash: &bitcoin::BlockHash,
1120 timeout: u64,
1121 ) -> Result<json::BlockRef> {
1122 let args = [into_json(blockhash)?, into_json(timeout)?];
1123 self.call("waitforblock", &args)
1124 }
1125
1126 fn wallet_create_funded_psbt(
1127 &self,
1128 inputs: &[json::CreateRawTransactionInput],
1129 outputs: &HashMap<String, Amount>,
1130 locktime: Option<i64>,
1131 options: Option<json::WalletCreateFundedPsbtOptions>,
1132 bip32derivs: Option<bool>,
1133 ) -> Result<json::WalletCreateFundedPsbtResult> {
1134 let outputs_converted = serde_json::Map::from_iter(
1135 outputs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.to_btc()))),
1136 );
1137 let mut args = [
1138 into_json(inputs)?,
1139 into_json(outputs_converted)?,
1140 opt_into_json(locktime)?,
1141 opt_into_json(options)?,
1142 opt_into_json(bip32derivs)?,
1143 ];
1144 self.call(
1145 "walletcreatefundedpsbt",
1146 handle_defaults(&mut args, &[0.into(), serde_json::Map::new().into(), false.into()]),
1147 )
1148 }
1149
1150 fn wallet_process_psbt(
1151 &self,
1152 psbt: &str,
1153 sign: Option<bool>,
1154 sighash_type: Option<json::SigHashType>,
1155 bip32derivs: Option<bool>,
1156 ) -> Result<json::WalletProcessPsbtResult> {
1157 let mut args = [
1158 into_json(psbt)?,
1159 opt_into_json(sign)?,
1160 opt_into_json(sighash_type)?,
1161 opt_into_json(bip32derivs)?,
1162 ];
1163 let defaults = [
1164 true.into(),
1165 into_json(json::SigHashType::from(bitcoin::sighash::EcdsaSighashType::All))?,
1166 true.into(),
1167 ];
1168 self.call("walletprocesspsbt", handle_defaults(&mut args, &defaults))
1169 }
1170
1171 fn get_descriptor_info(&self, desc: &str) -> Result<json::GetDescriptorInfoResult> {
1172 self.call("getdescriptorinfo", &[desc.to_string().into()])
1173 }
1174
1175 fn join_psbt(&self, psbts: &[String]) -> Result<String> {
1176 self.call("joinpsbts", &[into_json(psbts)?])
1177 }
1178
1179 fn combine_psbt(&self, psbts: &[String]) -> Result<String> {
1180 self.call("combinepsbt", &[into_json(psbts)?])
1181 }
1182
1183 fn combine_raw_transaction(&self, hex_strings: &[String]) -> Result<String> {
1184 self.call("combinerawtransaction", &[into_json(hex_strings)?])
1185 }
1186
1187 fn finalize_psbt(&self, psbt: &str, extract: Option<bool>) -> Result<json::FinalizePsbtResult> {
1188 let mut args = [into_json(psbt)?, opt_into_json(extract)?];
1189 self.call("finalizepsbt", handle_defaults(&mut args, &[true.into()]))
1190 }
1191
1192 fn derive_addresses(&self, descriptor: &str, range: Option<[u32; 2]>) -> Result<Vec<Address<NetworkUnchecked>>> {
1193 let mut args = [into_json(descriptor)?, opt_into_json(range)?];
1194 self.call("deriveaddresses", handle_defaults(&mut args, &[null()]))
1195 }
1196
1197 fn rescan_blockchain(
1198 &self,
1199 start_from: Option<usize>,
1200 stop_height: Option<usize>,
1201 ) -> Result<(usize, Option<usize>)> {
1202 let mut args = [opt_into_json(start_from)?, opt_into_json(stop_height)?];
1203
1204 #[derive(Deserialize)]
1205 struct Response {
1206 pub start_height: usize,
1207 pub stop_height: Option<usize>,
1208 }
1209 let res: Response =
1210 self.call("rescanblockchain", handle_defaults(&mut args, &[0.into(), null()]))?;
1211 Ok((res.start_height, res.stop_height))
1212 }
1213
1214 fn get_tx_out_set_info(
1217 &self,
1218 hash_type: Option<json::TxOutSetHashType>,
1219 hash_or_height: Option<json::HashOrHeight>,
1220 use_index: Option<bool>,
1221 ) -> Result<json::GetTxOutSetInfoResult> {
1222 let mut args =
1223 [opt_into_json(hash_type)?, opt_into_json(hash_or_height)?, opt_into_json(use_index)?];
1224 self.call("gettxoutsetinfo", handle_defaults(&mut args, &[null(), null(), null()]))
1225 }
1226
1227 fn get_net_totals(&self) -> Result<json::GetNetTotalsResult> {
1230 self.call("getnettotals", &[])
1231 }
1232
1233 fn get_network_hash_ps(&self, nblocks: Option<u64>, height: Option<u64>) -> Result<f64> {
1235 let mut args = [opt_into_json(nblocks)?, opt_into_json(height)?];
1236 self.call("getnetworkhashps", handle_defaults(&mut args, &[null(), null()]))
1237 }
1238
1239 fn uptime(&self) -> Result<u64> {
1241 self.call("uptime", &[])
1242 }
1243
1244 fn submit_block(&self, block: &bitcoin::Block) -> Result<()> {
1246 let block_hex: String = bitcoin::consensus::encode::serialize_hex(block);
1247 self.submit_block_hex(&block_hex)
1248 }
1249
1250 fn submit_block_bytes(&self, block_bytes: &[u8]) -> Result<()> {
1252 let block_hex: String = block_bytes.to_lower_hex_string();
1253 self.submit_block_hex(&block_hex)
1254 }
1255
1256 fn submit_block_hex(&self, block_hex: &str) -> Result<()> {
1258 match self.call("submitblock", &[into_json(&block_hex)?]) {
1259 Ok(serde_json::Value::Null) => Ok(()),
1260 Ok(res) => Err(Error::ReturnedError(res.to_string())),
1261 Err(err) => Err(err.into()),
1262 }
1263 }
1264
1265 fn scan_tx_out_set_blocking(
1266 &self,
1267 descriptors: &[json::ScanTxOutRequest],
1268 ) -> Result<json::ScanTxOutResult> {
1269 self.call("scantxoutset", &["start".into(), into_json(descriptors)?])
1270 }
1271}
1272
1273pub struct Client {
1275 client: jsonrpc::client::Client,
1276}
1277
1278impl fmt::Debug for Client {
1279 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1280 write!(f, "bitcoincore_rpc::Client({:?})", self.client)
1281 }
1282}
1283
1284impl Client {
1285 pub fn new(url: &str, auth: Auth) -> Result<Self> {
1289 let (user, pass) = auth.get_user_pass()?;
1290 jsonrpc::client::Client::simple_http(url, user, pass)
1291 .map(|client| Client {
1292 client,
1293 })
1294 .map_err(|e| super::error::Error::JsonRpc(e.into()))
1295 }
1296
1297 pub fn from_jsonrpc(client: jsonrpc::client::Client) -> Client {
1299 Client {
1300 client,
1301 }
1302 }
1303
1304 pub fn get_jsonrpc_client(&self) -> &jsonrpc::client::Client {
1306 &self.client
1307 }
1308}
1309
1310impl RpcApi for Client {
1311 fn call<T: for<'a> serde::de::Deserialize<'a>>(
1313 &self,
1314 cmd: &str,
1315 args: &[serde_json::Value],
1316 ) -> Result<T> {
1317 let raw_args: Vec<_> = args
1318 .iter()
1319 .map(|a| {
1320 let json_string = serde_json::to_string(a)?;
1321 serde_json::value::RawValue::from_string(json_string) })
1323 .map(|a| a.map_err(|e| Error::Json(e)))
1324 .collect::<Result<Vec<_>>>()?;
1325 let req = self.client.build_request(&cmd, &raw_args);
1326 if log_enabled!(Debug) {
1327 debug!(target: "bitcoincore_rpc", "JSON-RPC request: {} {}", cmd, serde_json::Value::from(args));
1328 }
1329
1330 let resp = self.client.send_request(req).map_err(Error::from);
1331 log_response(cmd, &resp);
1332 Ok(resp?.result()?)
1333 }
1334}
1335
1336fn log_response(cmd: &str, resp: &Result<jsonrpc::Response>) {
1337 if log_enabled!(Warn) || log_enabled!(Debug) || log_enabled!(Trace) {
1338 match resp {
1339 Err(ref e) => {
1340 if log_enabled!(Debug) {
1341 debug!(target: "bitcoincore_rpc", "JSON-RPC failed parsing reply of {}: {:?}", cmd, e);
1342 }
1343 }
1344 Ok(ref resp) => {
1345 if let Some(ref e) = resp.error {
1346 if log_enabled!(Debug) {
1347 debug!(target: "bitcoincore_rpc", "JSON-RPC error for {}: {:?}", cmd, e);
1348 }
1349 } else if log_enabled!(Trace) {
1350 let def = serde_json::value::RawValue::from_string(
1352 serde_json::Value::Null.to_string(),
1353 )
1354 .unwrap();
1355 let result = resp.result.as_ref().unwrap_or(&def);
1356 trace!(target: "bitcoincore_rpc", "JSON-RPC response for {}: {}", cmd, result);
1357 }
1358 }
1359 }
1360 }
1361}
1362
1363#[cfg(test)]
1364mod tests {
1365 use super::*;
1366 use crate::bitcoin;
1367 use serde_json;
1368
1369 #[test]
1370 fn test_raw_tx() {
1371 use crate::bitcoin::consensus::encode;
1372 let client = Client::new("http://localhost/".into(), Auth::None).unwrap();
1373 let tx: bitcoin::Transaction = encode::deserialize(&Vec::<u8>::from_hex("0200000001586bd02815cf5faabfec986a4e50d25dbee089bd2758621e61c5fab06c334af0000000006b483045022100e85425f6d7c589972ee061413bcf08dc8c8e589ce37b217535a42af924f0e4d602205c9ba9cb14ef15513c9d946fa1c4b797883e748e8c32171bdf6166583946e35c012103dae30a4d7870cd87b45dd53e6012f71318fdd059c1c2623b8cc73f8af287bb2dfeffffff021dc4260c010000001976a914f602e88b2b5901d8aab15ebe4a97cf92ec6e03b388ac00e1f505000000001976a914687ffeffe8cf4e4c038da46a9b1d37db385a472d88acfd211500").unwrap()).unwrap();
1374
1375 assert!(client.send_raw_transaction(&tx).is_err());
1376 assert!(client.send_raw_transaction(&encode::serialize(&tx)).is_err());
1377 assert!(client.send_raw_transaction("deadbeef").is_err());
1378 assert!(client.send_raw_transaction("deadbeef".to_owned()).is_err());
1379 }
1380
1381 fn test_handle_defaults_inner() -> Result<()> {
1382 {
1383 let mut args = [into_json(0)?, null(), null()];
1384 let defaults = [into_json(1)?, into_json(2)?];
1385 let res = [into_json(0)?];
1386 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1387 }
1388 {
1389 let mut args = [into_json(0)?, into_json(1)?, null()];
1390 let defaults = [into_json(2)?];
1391 let res = [into_json(0)?, into_json(1)?];
1392 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1393 }
1394 {
1395 let mut args = [into_json(0)?, null(), into_json(5)?];
1396 let defaults = [into_json(2)?, into_json(3)?];
1397 let res = [into_json(0)?, into_json(2)?, into_json(5)?];
1398 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1399 }
1400 {
1401 let mut args = [into_json(0)?, null(), into_json(5)?, null()];
1402 let defaults = [into_json(2)?, into_json(3)?, into_json(4)?];
1403 let res = [into_json(0)?, into_json(2)?, into_json(5)?];
1404 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1405 }
1406 {
1407 let mut args = [null(), null()];
1408 let defaults = [into_json(2)?, into_json(3)?];
1409 let res: [serde_json::Value; 0] = [];
1410 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1411 }
1412 {
1413 let mut args = [null(), into_json(1)?];
1414 let defaults = [];
1415 let res = [null(), into_json(1)?];
1416 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1417 }
1418 {
1419 let mut args = [];
1420 let defaults = [];
1421 let res: [serde_json::Value; 0] = [];
1422 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1423 }
1424 {
1425 let mut args = [into_json(0)?];
1426 let defaults = [into_json(2)?];
1427 let res = [into_json(0)?];
1428 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1429 }
1430 Ok(())
1431 }
1432
1433 #[test]
1434 fn test_handle_defaults() {
1435 test_handle_defaults_inner().unwrap();
1436 }
1437}