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