1use std::collections::HashMap;
12use std::fs::File;
13use std::io::{BufRead, BufReader};
14use std::iter::FromIterator;
15use std::path::PathBuf;
16use std::time::Duration;
17use std::{fmt, result};
18
19use crate::{bitcoin, deserialize_hex};
20use bitcoin::hex::DisplayHex;
21use jsonrpc;
22use jsonrpc::minreq_http::Builder;
23use serde;
24use serde_json;
25
26use crate::bitcoin::address::{NetworkChecked, NetworkUnchecked};
27use crate::bitcoin::hashes::hex::FromHex;
28use crate::bitcoin::secp256k1::ecdsa::Signature;
29use crate::bitcoin::{
30 Address, Amount, Block, OutPoint, PrivateKey, PublicKey, Script, Transaction,
31};
32use log::Level::{Debug, Trace, Warn};
33
34use crate::error::*;
35use crate::json;
36use crate::queryable;
37
38pub type Result<T> = result::Result<T, Error>;
41
42#[derive(Clone, Debug, Serialize, Deserialize)]
45pub struct JsonOutPoint {
46 pub txid: bitcoin::Txid,
47 pub vout: u32,
48}
49
50impl From<OutPoint> for JsonOutPoint {
51 fn from(o: OutPoint) -> JsonOutPoint {
52 JsonOutPoint {
53 txid: o.txid,
54 vout: o.vout,
55 }
56 }
57}
58
59impl Into<OutPoint> for JsonOutPoint {
60 fn into(self) -> OutPoint {
61 OutPoint {
62 txid: self.txid,
63 vout: self.vout,
64 }
65 }
66}
67
68fn into_json<T>(val: T) -> Result<serde_json::Value>
70where
71 T: serde::ser::Serialize,
72{
73 Ok(serde_json::to_value(val)?)
74}
75
76fn opt_into_json<T>(opt: Option<T>) -> Result<serde_json::Value>
78where
79 T: serde::ser::Serialize,
80{
81 match opt {
82 Some(val) => Ok(into_json(val)?),
83 None => Ok(serde_json::Value::Null),
84 }
85}
86
87fn null() -> serde_json::Value {
89 serde_json::Value::Null
90}
91
92fn empty_arr() -> serde_json::Value {
94 serde_json::Value::Array(vec![])
95}
96
97fn empty_obj() -> serde_json::Value {
99 serde_json::Value::Object(Default::default())
100}
101
102fn handle_defaults<'a, 'b>(
118 args: &'a mut [serde_json::Value],
119 defaults: &'b [serde_json::Value],
120) -> &'a [serde_json::Value] {
121 assert!(args.len() >= defaults.len());
122
123 let mut first_non_null_optional_idx = None;
126 for i in 0..defaults.len() {
127 let args_i = args.len() - 1 - i;
128 let defaults_i = defaults.len() - 1 - i;
129 if args[args_i] == serde_json::Value::Null {
130 if first_non_null_optional_idx.is_some() {
131 if defaults[defaults_i] == serde_json::Value::Null {
132 panic!("Missing `default` for argument idx {}", args_i);
133 }
134 args[args_i] = defaults[defaults_i].clone();
135 }
136 } else if first_non_null_optional_idx.is_none() {
137 first_non_null_optional_idx = Some(args_i);
138 }
139 }
140
141 let required_num = args.len() - defaults.len();
142
143 if let Some(i) = first_non_null_optional_idx {
144 &args[..i + 1]
145 } else {
146 &args[..required_num]
147 }
148}
149
150fn opt_result<T: for<'a> serde::de::Deserialize<'a>>(
152 result: serde_json::Value,
153) -> Result<Option<T>> {
154 if result == serde_json::Value::Null {
155 Ok(None)
156 } else {
157 Ok(serde_json::from_value(result)?)
158 }
159}
160
161pub trait RawTx: Sized + Clone {
163 fn raw_hex(self) -> String;
164}
165
166impl<'a> RawTx for &'a Transaction {
167 fn raw_hex(self) -> String {
168 bitcoin::consensus::encode::serialize_hex(self)
169 }
170}
171
172impl<'a> RawTx for &'a [u8] {
173 fn raw_hex(self) -> String {
174 self.to_lower_hex_string()
175 }
176}
177
178impl<'a> RawTx for &'a Vec<u8> {
179 fn raw_hex(self) -> String {
180 self.to_lower_hex_string()
181 }
182}
183
184impl<'a> RawTx for &'a str {
185 fn raw_hex(self) -> String {
186 self.to_owned()
187 }
188}
189
190impl RawTx for String {
191 fn raw_hex(self) -> String {
192 self
193 }
194}
195
196#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
198pub enum Auth {
199 None,
200 UserPass(String, String),
201 CookieFile(PathBuf),
202}
203
204impl Auth {
205 pub fn get_user_pass(self) -> Result<(Option<String>, Option<String>)> {
207 match self {
208 Auth::None => Ok((None, None)),
209 Auth::UserPass(u, p) => Ok((Some(u), Some(p))),
210 Auth::CookieFile(path) => {
211 let line = BufReader::new(File::open(path)?)
212 .lines()
213 .next()
214 .ok_or(Error::InvalidCookieFile)??;
215 let colon = line.find(':').ok_or(Error::InvalidCookieFile)?;
216 Ok((Some(line[..colon].into()), Some(line[colon + 1..].into())))
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(
902 &self,
903 address_type: Option<json::AddressType>,
904 ) -> Result<Address<NetworkUnchecked>> {
905 self.call("getrawchangeaddress", &[opt_into_json(address_type)?])
906 }
907
908 fn get_address_info(&self, address: &Address) -> Result<json::GetAddressInfoResult> {
909 self.call("getaddressinfo", &[address.to_string().into()])
910 }
911
912 fn generate_to_address(
916 &self,
917 block_num: u64,
918 address: &Address<NetworkChecked>,
919 ) -> Result<Vec<bitcoin::BlockHash>> {
920 self.call("generatetoaddress", &[block_num.into(), address.to_string().into()])
921 }
922
923 fn generate(&self, block_num: u64, maxtries: Option<u64>) -> Result<Vec<bitcoin::BlockHash>> {
926 self.call("generate", &[block_num.into(), opt_into_json(maxtries)?])
927 }
928
929 fn invalidate_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> {
931 self.call("invalidateblock", &[into_json(block_hash)?])
932 }
933
934 fn reconsider_block(&self, block_hash: &bitcoin::BlockHash) -> Result<()> {
936 self.call("reconsiderblock", &[into_json(block_hash)?])
937 }
938
939 fn get_mempool_info(&self) -> Result<json::GetMempoolInfoResult> {
941 self.call("getmempoolinfo", &[])
942 }
943
944 fn get_raw_mempool(&self) -> Result<Vec<bitcoin::Txid>> {
946 self.call("getrawmempool", &[])
947 }
948
949 fn get_raw_mempool_verbose(
951 &self,
952 ) -> Result<HashMap<bitcoin::Txid, json::GetMempoolEntryResult>> {
953 self.call("getrawmempool", &[into_json(true)?])
954 }
955
956 fn get_mempool_entry(&self, txid: &bitcoin::Txid) -> Result<json::GetMempoolEntryResult> {
958 self.call("getmempoolentry", &[into_json(txid)?])
959 }
960
961 fn get_chain_tips(&self) -> Result<json::GetChainTipsResult> {
964 self.call("getchaintips", &[])
965 }
966
967 fn send_to_address(
968 &self,
969 address: &Address<NetworkChecked>,
970 amount: Amount,
971 comment: Option<&str>,
972 comment_to: Option<&str>,
973 subtract_fee: Option<bool>,
974 replaceable: Option<bool>,
975 confirmation_target: Option<u32>,
976 estimate_mode: Option<json::EstimateMode>,
977 ) -> Result<bitcoin::Txid> {
978 let mut args = [
979 address.to_string().into(),
980 into_json(amount.to_btc())?,
981 opt_into_json(comment)?,
982 opt_into_json(comment_to)?,
983 opt_into_json(subtract_fee)?,
984 opt_into_json(replaceable)?,
985 opt_into_json(confirmation_target)?,
986 opt_into_json(estimate_mode)?,
987 ];
988 self.call(
989 "sendtoaddress",
990 handle_defaults(
991 &mut args,
992 &["".into(), "".into(), false.into(), false.into(), 6.into(), null()],
993 ),
994 )
995 }
996
997 fn add_node(&self, addr: &str) -> Result<()> {
1000 self.call("addnode", &[into_json(&addr)?, into_json("add")?])
1001 }
1002
1003 fn remove_node(&self, addr: &str) -> Result<()> {
1005 self.call("addnode", &[into_json(&addr)?, into_json("remove")?])
1006 }
1007
1008 fn onetry_node(&self, addr: &str) -> Result<()> {
1010 self.call("addnode", &[into_json(&addr)?, into_json("onetry")?])
1011 }
1012
1013 fn disconnect_node(&self, addr: &str) -> Result<()> {
1015 self.call("disconnectnode", &[into_json(&addr)?])
1016 }
1017
1018 fn disconnect_node_by_id(&self, node_id: u32) -> Result<()> {
1019 self.call("disconnectnode", &[into_json("")?, into_json(node_id)?])
1020 }
1021
1022 fn get_added_node_info(&self, node: Option<&str>) -> Result<Vec<json::GetAddedNodeInfoResult>> {
1024 if let Some(addr) = node {
1025 self.call("getaddednodeinfo", &[into_json(&addr)?])
1026 } else {
1027 self.call("getaddednodeinfo", &[])
1028 }
1029 }
1030
1031 fn get_node_addresses(
1033 &self,
1034 count: Option<usize>,
1035 ) -> Result<Vec<json::GetNodeAddressesResult>> {
1036 let cnt = count.unwrap_or(1);
1037 self.call("getnodeaddresses", &[into_json(&cnt)?])
1038 }
1039
1040 fn list_banned(&self) -> Result<Vec<json::ListBannedResult>> {
1042 self.call("listbanned", &[])
1043 }
1044
1045 fn clear_banned(&self) -> Result<()> {
1047 self.call("clearbanned", &[])
1048 }
1049
1050 fn add_ban(&self, subnet: &str, bantime: u64, absolute: bool) -> Result<()> {
1052 self.call(
1053 "setban",
1054 &[into_json(&subnet)?, into_json("add")?, into_json(&bantime)?, into_json(&absolute)?],
1055 )
1056 }
1057
1058 fn remove_ban(&self, subnet: &str) -> Result<()> {
1060 self.call("setban", &[into_json(&subnet)?, into_json("remove")?])
1061 }
1062
1063 fn set_network_active(&self, state: bool) -> Result<bool> {
1065 self.call("setnetworkactive", &[into_json(&state)?])
1066 }
1067
1068 fn get_peer_info(&self) -> Result<Vec<json::GetPeerInfoResult>> {
1073 self.call("getpeerinfo", &[])
1074 }
1075
1076 fn ping(&self) -> Result<()> {
1085 self.call("ping", &[])
1086 }
1087
1088 fn send_raw_transaction<R: RawTx>(&self, tx: R) -> Result<bitcoin::Txid> {
1089 self.call("sendrawtransaction", &[tx.raw_hex().into()])
1090 }
1091
1092 fn estimate_smart_fee(
1093 &self,
1094 conf_target: u16,
1095 estimate_mode: Option<json::EstimateMode>,
1096 ) -> Result<json::EstimateSmartFeeResult> {
1097 let mut args = [into_json(conf_target)?, opt_into_json(estimate_mode)?];
1098 self.call("estimatesmartfee", handle_defaults(&mut args, &[null()]))
1099 }
1100
1101 fn wait_for_new_block(&self, timeout: u64) -> Result<json::BlockRef> {
1109 self.call("waitfornewblock", &[into_json(timeout)?])
1110 }
1111
1112 fn wait_for_block(
1121 &self,
1122 blockhash: &bitcoin::BlockHash,
1123 timeout: u64,
1124 ) -> Result<json::BlockRef> {
1125 let args = [into_json(blockhash)?, into_json(timeout)?];
1126 self.call("waitforblock", &args)
1127 }
1128
1129 fn wallet_create_funded_psbt(
1130 &self,
1131 inputs: &[json::CreateRawTransactionInput],
1132 outputs: &HashMap<String, Amount>,
1133 locktime: Option<i64>,
1134 options: Option<json::WalletCreateFundedPsbtOptions>,
1135 bip32derivs: Option<bool>,
1136 ) -> Result<json::WalletCreateFundedPsbtResult> {
1137 let outputs_converted = serde_json::Map::from_iter(
1138 outputs.iter().map(|(k, v)| (k.clone(), serde_json::Value::from(v.to_btc()))),
1139 );
1140 let mut args = [
1141 into_json(inputs)?,
1142 into_json(outputs_converted)?,
1143 opt_into_json(locktime)?,
1144 opt_into_json(options)?,
1145 opt_into_json(bip32derivs)?,
1146 ];
1147 self.call(
1148 "walletcreatefundedpsbt",
1149 handle_defaults(&mut args, &[0.into(), serde_json::Map::new().into(), false.into()]),
1150 )
1151 }
1152
1153 fn wallet_process_psbt(
1154 &self,
1155 psbt: &str,
1156 sign: Option<bool>,
1157 sighash_type: Option<json::SigHashType>,
1158 bip32derivs: Option<bool>,
1159 ) -> Result<json::WalletProcessPsbtResult> {
1160 let mut args = [
1161 into_json(psbt)?,
1162 opt_into_json(sign)?,
1163 opt_into_json(sighash_type)?,
1164 opt_into_json(bip32derivs)?,
1165 ];
1166 let defaults = [
1167 true.into(),
1168 into_json(json::SigHashType::from(bitcoin::sighash::EcdsaSighashType::All))?,
1169 true.into(),
1170 ];
1171 self.call("walletprocesspsbt", handle_defaults(&mut args, &defaults))
1172 }
1173
1174 fn get_descriptor_info(&self, desc: &str) -> Result<json::GetDescriptorInfoResult> {
1175 self.call("getdescriptorinfo", &[desc.to_string().into()])
1176 }
1177
1178 fn join_psbt(&self, psbts: &[String]) -> Result<String> {
1179 self.call("joinpsbts", &[into_json(psbts)?])
1180 }
1181
1182 fn combine_psbt(&self, psbts: &[String]) -> Result<String> {
1183 self.call("combinepsbt", &[into_json(psbts)?])
1184 }
1185
1186 fn combine_raw_transaction(&self, hex_strings: &[String]) -> Result<String> {
1187 self.call("combinerawtransaction", &[into_json(hex_strings)?])
1188 }
1189
1190 fn finalize_psbt(&self, psbt: &str, extract: Option<bool>) -> Result<json::FinalizePsbtResult> {
1191 let mut args = [into_json(psbt)?, opt_into_json(extract)?];
1192 self.call("finalizepsbt", handle_defaults(&mut args, &[true.into()]))
1193 }
1194
1195 fn derive_addresses(
1196 &self,
1197 descriptor: &str,
1198 range: Option<[u32; 2]>,
1199 ) -> Result<Vec<Address<NetworkUnchecked>>> {
1200 let mut args = [into_json(descriptor)?, opt_into_json(range)?];
1201 self.call("deriveaddresses", handle_defaults(&mut args, &[null()]))
1202 }
1203
1204 fn rescan_blockchain(
1205 &self,
1206 start_from: Option<usize>,
1207 stop_height: Option<usize>,
1208 ) -> Result<(usize, Option<usize>)> {
1209 let mut args = [opt_into_json(start_from)?, opt_into_json(stop_height)?];
1210
1211 #[derive(Deserialize)]
1212 struct Response {
1213 pub start_height: usize,
1214 pub stop_height: Option<usize>,
1215 }
1216 let res: Response =
1217 self.call("rescanblockchain", handle_defaults(&mut args, &[0.into(), null()]))?;
1218 Ok((res.start_height, res.stop_height))
1219 }
1220
1221 fn get_tx_out_set_info(
1224 &self,
1225 hash_type: Option<json::TxOutSetHashType>,
1226 hash_or_height: Option<json::HashOrHeight>,
1227 use_index: Option<bool>,
1228 ) -> Result<json::GetTxOutSetInfoResult> {
1229 let mut args =
1230 [opt_into_json(hash_type)?, opt_into_json(hash_or_height)?, opt_into_json(use_index)?];
1231 self.call("gettxoutsetinfo", handle_defaults(&mut args, &[null(), null(), null()]))
1232 }
1233
1234 fn get_net_totals(&self) -> Result<json::GetNetTotalsResult> {
1237 self.call("getnettotals", &[])
1238 }
1239
1240 fn get_network_hash_ps(&self, nblocks: Option<u64>, height: Option<u64>) -> Result<f64> {
1242 let mut args = [opt_into_json(nblocks)?, opt_into_json(height)?];
1243 self.call("getnetworkhashps", handle_defaults(&mut args, &[null(), null()]))
1244 }
1245
1246 fn uptime(&self) -> Result<u64> {
1248 self.call("uptime", &[])
1249 }
1250
1251 fn submit_block(&self, block: &bitcoin::Block) -> Result<()> {
1253 let block_hex: String = bitcoin::consensus::encode::serialize_hex(block);
1254 self.submit_block_hex(&block_hex)
1255 }
1256
1257 fn submit_block_bytes(&self, block_bytes: &[u8]) -> Result<()> {
1259 let block_hex: String = block_bytes.to_lower_hex_string();
1260 self.submit_block_hex(&block_hex)
1261 }
1262
1263 fn submit_block_hex(&self, block_hex: &str) -> Result<()> {
1265 match self.call("submitblock", &[into_json(&block_hex)?]) {
1266 Ok(serde_json::Value::Null) => Ok(()),
1267 Ok(res) => Err(Error::ReturnedError(res.to_string())),
1268 Err(err) => Err(err.into()),
1269 }
1270 }
1271
1272 fn scan_tx_out_set_blocking(
1273 &self,
1274 descriptors: &[json::ScanTxOutRequest],
1275 ) -> Result<json::ScanTxOutResult> {
1276 self.call("scantxoutset", &["start".into(), into_json(descriptors)?])
1277 }
1278}
1279
1280pub struct Client {
1282 client: jsonrpc::client::Client,
1283}
1284
1285impl fmt::Debug for Client {
1286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1287 write!(f, "bitcoincore_rpc::Client({:?})", self.client)
1288 }
1289}
1290
1291impl Client {
1292 pub fn new(url: &str, auth: Auth) -> Result<Self> {
1296 let (user, pass) = auth.get_user_pass()?;
1297 jsonrpc::client::Client::simple_http(url, user, pass)
1298 .map(|client| Client {
1299 client,
1300 })
1301 .map_err(|e| super::error::Error::JsonRpc(e.into()))
1302 }
1303
1304 pub fn new_minreq_http(url: &str, auth: Auth) -> Result<Self> {
1305 let (user, pass) = auth.get_user_pass()?;
1306 let mut tp_builder = Builder::new().timeout(Duration::from_millis(10000)).url(url).unwrap();
1307 if let Some(user) = user {
1308 tp_builder = tp_builder.basic_auth(user, pass);
1309 }
1310 let tp = tp_builder.build();
1311
1312 let client = jsonrpc::client::Client::with_transport(tp);
1313 Ok(Client {
1314 client,
1315 })
1316 }
1317
1318 pub fn from_jsonrpc(client: jsonrpc::client::Client) -> Client {
1320 Client {
1321 client,
1322 }
1323 }
1324
1325 pub fn get_jsonrpc_client(&self) -> &jsonrpc::client::Client {
1327 &self.client
1328 }
1329}
1330
1331impl RpcApi for Client {
1332 fn call<T: for<'a> serde::de::Deserialize<'a>>(
1334 &self,
1335 cmd: &str,
1336 args: &[serde_json::Value],
1337 ) -> Result<T> {
1338 let raw_args: Vec<_> = args
1339 .iter()
1340 .map(|a| {
1341 let json_string = serde_json::to_string(a)?;
1342 serde_json::value::RawValue::from_string(json_string) })
1344 .map(|a| a.map_err(|e| Error::Json(e)))
1345 .collect::<Result<Vec<_>>>()?;
1346 let req = self.client.build_request(&cmd, &raw_args);
1347 if log_enabled!(Debug) {
1348 debug!(target: "bitcoincore_rpc", "JSON-RPC request: {} {}", cmd, serde_json::Value::from(args));
1349 }
1350
1351 let resp = self.client.send_request(req).map_err(Error::from);
1352 log_response(cmd, &resp);
1353 Ok(resp?.result()?)
1354 }
1355}
1356
1357fn log_response(cmd: &str, resp: &Result<jsonrpc::Response>) {
1358 if log_enabled!(Warn) || log_enabled!(Debug) || log_enabled!(Trace) {
1359 match resp {
1360 Err(ref e) => {
1361 if log_enabled!(Debug) {
1362 debug!(target: "bitcoincore_rpc", "JSON-RPC failed parsing reply of {}: {:?}", cmd, e);
1363 }
1364 }
1365 Ok(ref resp) => {
1366 if let Some(ref e) = resp.error {
1367 if log_enabled!(Debug) {
1368 debug!(target: "bitcoincore_rpc", "JSON-RPC error for {}: {:?}", cmd, e);
1369 }
1370 } else if log_enabled!(Trace) {
1371 let def = serde_json::value::RawValue::from_string(
1373 serde_json::Value::Null.to_string(),
1374 )
1375 .unwrap();
1376 let result = resp.result.as_ref().unwrap_or(&def);
1377 trace!(target: "bitcoincore_rpc", "JSON-RPC response for {}: {}", cmd, result);
1378 }
1379 }
1380 }
1381 }
1382}
1383
1384#[cfg(test)]
1385mod tests {
1386 use super::*;
1387 use crate::bitcoin;
1388 use serde_json;
1389
1390 #[test]
1391 fn test_raw_tx() {
1392 use crate::bitcoin::consensus::encode;
1393 let client = Client::new("http://localhost/".into(), Auth::None).unwrap();
1394 let tx: bitcoin::Transaction = encode::deserialize(&Vec::<u8>::from_hex("0200000001586bd02815cf5faabfec986a4e50d25dbee089bd2758621e61c5fab06c334af0000000006b483045022100e85425f6d7c589972ee061413bcf08dc8c8e589ce37b217535a42af924f0e4d602205c9ba9cb14ef15513c9d946fa1c4b797883e748e8c32171bdf6166583946e35c012103dae30a4d7870cd87b45dd53e6012f71318fdd059c1c2623b8cc73f8af287bb2dfeffffff021dc4260c010000001976a914f602e88b2b5901d8aab15ebe4a97cf92ec6e03b388ac00e1f505000000001976a914687ffeffe8cf4e4c038da46a9b1d37db385a472d88acfd211500").unwrap()).unwrap();
1395
1396 assert!(client.send_raw_transaction(&tx).is_err());
1397 assert!(client.send_raw_transaction(&encode::serialize(&tx)).is_err());
1398 assert!(client.send_raw_transaction("deadbeef").is_err());
1399 assert!(client.send_raw_transaction("deadbeef".to_owned()).is_err());
1400 }
1401
1402 fn test_handle_defaults_inner() -> Result<()> {
1403 {
1404 let mut args = [into_json(0)?, null(), null()];
1405 let defaults = [into_json(1)?, into_json(2)?];
1406 let res = [into_json(0)?];
1407 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1408 }
1409 {
1410 let mut args = [into_json(0)?, into_json(1)?, null()];
1411 let defaults = [into_json(2)?];
1412 let res = [into_json(0)?, into_json(1)?];
1413 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1414 }
1415 {
1416 let mut args = [into_json(0)?, null(), into_json(5)?];
1417 let defaults = [into_json(2)?, into_json(3)?];
1418 let res = [into_json(0)?, into_json(2)?, into_json(5)?];
1419 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1420 }
1421 {
1422 let mut args = [into_json(0)?, null(), into_json(5)?, null()];
1423 let defaults = [into_json(2)?, into_json(3)?, into_json(4)?];
1424 let res = [into_json(0)?, into_json(2)?, into_json(5)?];
1425 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1426 }
1427 {
1428 let mut args = [null(), null()];
1429 let defaults = [into_json(2)?, into_json(3)?];
1430 let res: [serde_json::Value; 0] = [];
1431 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1432 }
1433 {
1434 let mut args = [null(), into_json(1)?];
1435 let defaults = [];
1436 let res = [null(), into_json(1)?];
1437 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1438 }
1439 {
1440 let mut args = [];
1441 let defaults = [];
1442 let res: [serde_json::Value; 0] = [];
1443 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1444 }
1445 {
1446 let mut args = [into_json(0)?];
1447 let defaults = [into_json(2)?];
1448 let res = [into_json(0)?];
1449 assert_eq!(handle_defaults(&mut args, &defaults), &res);
1450 }
1451 Ok(())
1452 }
1453
1454 #[test]
1455 fn test_handle_defaults() {
1456 test_handle_defaults_inner().unwrap();
1457 }
1458
1459 #[test]
1460 fn auth_cookie_file_ignores_newline() {
1461 let tempdir = tempfile::tempdir().unwrap();
1462 let path = tempdir.path().join("cookie");
1463 std::fs::write(&path, "foo:bar\n").unwrap();
1464 assert_eq!(
1465 Auth::CookieFile(path).get_user_pass().unwrap(),
1466 (Some("foo".into()), Some("bar".into())),
1467 );
1468 }
1469
1470 #[test]
1471 fn auth_cookie_file_ignores_additional_lines() {
1472 let tempdir = tempfile::tempdir().unwrap();
1473 let path = tempdir.path().join("cookie");
1474 std::fs::write(&path, "foo:bar\nbaz").unwrap();
1475 assert_eq!(
1476 Auth::CookieFile(path).get_user_pass().unwrap(),
1477 (Some("foo".into()), Some("bar".into())),
1478 );
1479 }
1480
1481 #[test]
1482 fn auth_cookie_file_fails_if_colon_isnt_present() {
1483 let tempdir = tempfile::tempdir().unwrap();
1484 let path = tempdir.path().join("cookie");
1485 std::fs::write(&path, "foobar").unwrap();
1486 assert!(matches!(Auth::CookieFile(path).get_user_pass(), Err(Error::InvalidCookieFile)));
1487 }
1488}