1use std::collections::BTreeMap;
2
3use bitcoin::{
4 absolute::Height,
5 address::{self, NetworkUnchecked},
6 block::Header,
7 consensus::{self, encode},
8 Address, Amount, Block, BlockHash, FeeRate, Psbt, SignedAmount, Transaction, Txid, Wtxid,
9};
10use serde::{
11 de::{self, IntoDeserializer, Visitor},
12 Deserialize, Deserializer, Serialize, Serializer,
13};
14use tracing::*;
15
16use crate::error::SignRawTransactionWithWalletError;
17
18#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
28#[serde(rename_all = "lowercase")]
29pub enum TransactionCategory {
30 Send,
32 Receive,
34 Generate,
36 Immature,
38 Orphan,
40}
41
42#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
48pub struct GetBlockchainInfo {
49 pub chain: String,
51 pub blocks: u64,
53 pub headers: u64,
55 #[serde(rename = "bestblockhash")]
57 pub best_block_hash: String,
58 pub difficulty: f64,
60 #[serde(rename = "mediantime")]
62 pub median_time: u64,
63 #[serde(rename = "verificationprogress")]
65 pub verification_progress: f64,
66 #[serde(rename = "initialblockdownload")]
68 pub initial_block_download: bool,
69 #[serde(rename = "chainwork")]
71 pub chain_work: String,
72 pub size_on_disk: u64,
74 pub pruned: bool,
76 #[serde(rename = "pruneheight")]
78 pub prune_height: Option<u64>,
79 pub automatic_pruning: Option<bool>,
81 pub prune_target_size: Option<u64>,
83}
84
85#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
91pub struct GetBlockHeaderVerbosityZero(pub String);
92
93impl GetBlockHeaderVerbosityZero {
94 pub fn header(self) -> Result<Header, encode::FromHexError> {
96 let header: Header = encode::deserialize_hex(&self.0)?;
97 Ok(header)
98 }
99}
100
101#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
107pub struct GetBlockVerbosityZero(pub String);
108
109impl GetBlockVerbosityZero {
110 pub fn block(self) -> Result<Block, encode::FromHexError> {
112 let block: Block = encode::deserialize_hex(&self.0)?;
113 Ok(block)
114 }
115}
116
117#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
119pub struct GetBlockVerbosityOne {
120 pub hash: String,
122 pub confirmations: i32,
124 pub size: usize,
126 #[serde(rename = "strippedsize")]
128 pub stripped_size: Option<usize>,
129 pub weight: u64,
131 pub height: usize,
133 pub version: i32,
135 #[serde(rename = "versionHex")]
137 pub version_hex: String,
138 #[serde(rename = "merkleroot")]
140 pub merkle_root: String,
141 pub tx: Vec<String>,
143 pub time: usize,
145 #[serde(rename = "mediantime")]
147 pub median_time: Option<usize>,
148 pub nonce: u32,
150 pub bits: String,
152 pub difficulty: f64,
154 #[serde(rename = "chainwork")]
156 pub chain_work: String,
157 #[serde(rename = "nTx")]
159 pub n_tx: u32,
160 #[serde(rename = "previousblockhash")]
162 pub previous_block_hash: Option<String>,
163 #[serde(rename = "nextblockhash")]
165 pub next_block_hash: Option<String>,
166}
167
168#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
174pub struct GetRawTransactionVerbosityZero(pub String);
175
176impl GetRawTransactionVerbosityZero {
177 pub fn transaction(self) -> Result<Transaction, encode::FromHexError> {
179 let transaction: Transaction = encode::deserialize_hex(&self.0)?;
180 Ok(transaction)
181 }
182}
183
184#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
188pub struct GetMempoolInfo {
189 pub loaded: bool,
190 pub size: usize,
191 pub bytes: usize,
192 pub usage: usize,
193 pub maxmempool: usize,
194 pub mempoolminfee: f64,
195 pub minrelaytxfee: f64,
196 pub unbroadcastcount: usize,
197}
198
199pub type GetRawMempoolVerbose = BTreeMap<Txid, MempoolEntry>;
203
204#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
205pub struct MempoolEntry {
206 pub vsize: usize,
207 pub weight: usize,
208 pub time: u64,
209 pub height: usize,
210 pub descendantcount: usize,
211 pub descendantsize: usize,
212 #[serde(default)]
213 pub ancestorcount: usize,
214 pub ancestorsize: usize,
215 pub wtxid: String,
216 pub fees: Option<MempoolFeeBreakdown>,
217 pub depends: Vec<Txid>,
218 pub spentby: Vec<Txid>,
219 #[serde(rename = "bip125-replaceable")]
220 pub bip125_replaceable: bool,
221 pub unbroadcast: bool,
222}
223
224#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
225pub struct MempoolFeeBreakdown {
226 #[serde(deserialize_with = "deserialize_bitcoin")]
227 pub base: Amount,
228 #[serde(deserialize_with = "deserialize_bitcoin")]
229 pub modified: Amount,
230 #[serde(deserialize_with = "deserialize_bitcoin")]
231 pub ancestor: Amount,
232 #[serde(deserialize_with = "deserialize_bitcoin")]
233 pub descendant: Amount,
234}
235
236#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
240pub struct GetRawTransactionVerbosityOne {
241 pub in_active_chain: Option<bool>,
242 #[serde(deserialize_with = "deserialize_tx")]
243 #[serde(rename = "hex")]
244 pub transaction: Transaction,
245 pub txid: Txid,
246 pub hash: Wtxid,
247 pub size: usize,
248 pub vsize: usize,
249 pub version: u32,
250 pub locktime: u32,
251 pub blockhash: Option<BlockHash>,
252 pub confirmations: Option<u32>,
253 pub time: Option<usize>,
254 pub blocktime: Option<usize>,
255}
256
257#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
267pub struct GetTxOut {
268 #[serde(rename = "bestblock")]
270 pub best_block: String,
271 pub confirmations: u32, pub value: f64,
275 #[serde(rename = "scriptPubkey")]
277 pub script_pubkey: Option<ScriptPubkey>,
278 pub coinbase: bool,
280}
281
282#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
284pub struct ScriptPubkey {
285 pub asm: String,
287 pub hex: String,
289 #[serde(rename = "reqSigs")]
290 pub req_sigs: i64,
291 #[serde(rename = "type")]
293 pub type_: String,
294 pub address: Option<String>,
296}
297
298#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
304pub struct CreateRawTransaction {
305 pub inputs: Vec<CreateRawTransactionInput>,
306 pub outputs: Vec<CreateRawTransactionOutput>,
307}
308
309#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
311pub struct CreateRawTransactionInput {
312 pub txid: String,
313 pub vout: u32,
314}
315
316#[derive(Clone, Debug, PartialEq, Deserialize)]
322#[serde(untagged)]
323pub enum CreateRawTransactionOutput {
324 AddressAmount {
326 address: String,
328 amount: f64,
330 },
331 Data {
333 data: String,
335 },
336}
337
338impl Serialize for CreateRawTransactionOutput {
339 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
340 where
341 S: serde::Serializer,
342 {
343 match self {
344 CreateRawTransactionOutput::AddressAmount { address, amount } => {
345 let mut map = serde_json::Map::new();
346 map.insert(
347 address.clone(),
348 serde_json::Value::Number(serde_json::Number::from_f64(*amount).unwrap()),
349 );
350 map.serialize(serializer)
351 }
352 CreateRawTransactionOutput::Data { data } => {
353 let mut map = serde_json::Map::new();
354 map.insert("data".to_string(), serde_json::Value::String(data.clone()));
355 map.serialize(serializer)
356 }
357 }
358 }
359}
360
361#[allow(clippy::doc_lazy_continuation)]
383#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
384pub struct SubmitPackage {
385 pub package_msg: String,
389 #[serde(rename = "tx-results")]
391 pub tx_results: BTreeMap<String, SubmitPackageTxResult>,
392 #[serde(rename = "replaced-transactions")]
394 pub replaced_transactions: Vec<String>,
395}
396
397#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
399pub struct SubmitPackageTxResult {
400 pub txid: String,
402 #[serde(rename = "other-wtxid")]
407 pub other_wtxid: Option<String>,
408 pub vsize: i64,
410 pub fees: Option<SubmitPackageTxResultFees>,
412 pub error: Option<String>,
414}
415
416#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
418pub struct SubmitPackageTxResultFees {
419 #[serde(rename = "base")]
421 pub base_fee: f64,
422 #[serde(rename = "effective-feerate")]
427 pub effective_fee_rate: Option<f64>,
428 #[serde(rename = "effective-includes")]
431 pub effective_includes: Option<Vec<String>>,
432}
433
434#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
441pub struct GetTransactionDetail {
442 pub address: String,
443 pub category: GetTransactionDetailCategory,
444 pub amount: f64,
445 pub label: Option<String>,
446 pub vout: u32,
447 pub fee: Option<f64>,
448 pub abandoned: Option<bool>,
449}
450
451#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
453#[serde(rename_all = "lowercase")]
454pub enum GetTransactionDetailCategory {
455 Send,
456 Receive,
457 Generate,
458 Immature,
459 Orphan,
460}
461
462#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
469pub struct GetNewAddress(pub String);
470
471impl GetNewAddress {
472 pub fn address(self) -> Result<Address<NetworkUnchecked>, address::ParseError> {
474 let address = self.0.parse::<Address<_>>()?;
475 Ok(address)
476 }
477}
478
479#[derive(Clone, Debug, PartialEq, Deserialize)]
490pub struct GetTransaction {
491 #[serde(deserialize_with = "deserialize_signed_bitcoin")]
493 pub amount: SignedAmount,
494 pub confirmations: u64,
496 pub generated: Option<bool>,
497 pub trusted: Option<bool>,
498 pub blockhash: Option<String>,
499 pub blockheight: Option<u64>,
500 pub blockindex: Option<u32>,
501 pub blocktime: Option<u64>,
502 #[serde(deserialize_with = "deserialize_txid")]
504 pub txid: Txid,
505 pub wtxid: String,
506 pub walletconflicts: Vec<String>,
507 pub replaced_by_txid: Option<String>,
508 pub replaces_txid: Option<String>,
509 pub comment: Option<String>,
510 pub to: Option<String>,
511 pub time: u64,
512 pub timereceived: u64,
513 #[serde(rename = "bip125-replaceable")]
514 pub bip125_replaceable: String,
515 pub details: Vec<GetTransactionDetail>,
516 #[serde(deserialize_with = "deserialize_tx")]
518 pub hex: Transaction,
519}
520
521impl GetTransaction {
522 pub fn block_height(&self) -> u64 {
523 if self.confirmations == 0 {
524 return 0;
525 }
526 self.blockheight.unwrap_or_else(|| {
527 warn!("Txn confirmed but did not obtain blockheight. Setting height to zero");
528 0
529 })
530 }
531}
532
533#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
540pub struct ListUnspent {
541 #[serde(deserialize_with = "deserialize_txid")]
543 pub txid: Txid,
544 pub vout: u32,
546 #[serde(deserialize_with = "deserialize_address")]
548 pub address: Address<NetworkUnchecked>,
549 pub label: Option<String>,
551 #[serde(rename = "scriptPubKey")]
553 pub script_pubkey: String,
554 #[serde(deserialize_with = "deserialize_bitcoin")]
556 pub amount: Amount,
557 pub confirmations: u32,
559 pub spendable: bool,
561 pub solvable: bool,
563 pub safe: bool,
568}
569
570#[derive(Clone, Debug, PartialEq, Deserialize)]
581pub struct ListTransactions {
582 #[serde(deserialize_with = "deserialize_address")]
584 pub address: Address<NetworkUnchecked>,
585 category: TransactionCategory,
587 #[serde(deserialize_with = "deserialize_signed_bitcoin")]
589 pub amount: SignedAmount,
590 pub label: Option<String>,
592 pub confirmations: u32,
594 pub trusted: Option<bool>,
595 pub generated: Option<bool>,
596 pub blockhash: Option<String>,
597 pub blockheight: Option<u64>,
598 pub blockindex: Option<u32>,
599 pub blocktime: Option<u64>,
600 #[serde(deserialize_with = "deserialize_txid")]
602 pub txid: Txid,
603}
604
605#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
607pub struct TestMempoolAccept {
608 #[serde(deserialize_with = "deserialize_txid")]
610 pub txid: Txid,
611 pub reject_reason: Option<String>,
613}
614
615#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
622pub struct SignRawTransactionWithWallet {
623 pub hex: String,
625 pub complete: bool,
627 pub errors: Option<Vec<SignRawTransactionWithWalletError>>,
629}
630
631#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
652pub struct PreviousTransactionOutput {
653 #[serde(deserialize_with = "deserialize_txid")]
655 pub txid: Txid,
656 pub vout: u32,
658 #[serde(rename = "scriptPubKey")]
660 pub script_pubkey: String,
661 #[serde(rename = "redeemScript")]
663 pub redeem_script: Option<String>,
664 #[serde(rename = "witnessScript")]
666 pub witness_script: Option<String>,
667 pub amount: Option<f64>,
669}
670
671#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
673pub struct ListDescriptors {
674 pub descriptors: Vec<ListDescriptor>,
676}
677
678#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
680pub struct ListDescriptor {
681 pub desc: String,
683}
684
685#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
687pub struct ImportDescriptors {
688 pub descriptors: Vec<ListDescriptor>,
690}
691
692#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
694pub struct ImportDescriptor {
695 pub desc: String,
697 pub active: Option<bool>,
700 pub timestamp: String,
703}
704#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
706pub struct ImportDescriptorResult {
707 pub success: bool,
709}
710
711#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
717pub struct CreateWallet {
718 pub wallet_name: String,
720 pub load_on_startup: Option<bool>,
722}
723
724fn deserialize_bitcoin<'d, D>(deserializer: D) -> Result<Amount, D::Error>
726where
727 D: Deserializer<'d>,
728{
729 struct SatVisitor;
730
731 impl Visitor<'_> for SatVisitor {
732 type Value = Amount;
733
734 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
735 write!(formatter, "a float representation of btc values expected")
736 }
737
738 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
739 where
740 E: de::Error,
741 {
742 let amount = Amount::from_btc(v).expect("Amount deserialization failed");
743 Ok(amount)
744 }
745 }
746 deserializer.deserialize_any(SatVisitor)
747}
748
749fn serialize_option_bitcoin<S>(amount: &Option<Amount>, serializer: S) -> Result<S::Ok, S::Error>
751where
752 S: Serializer,
753{
754 match amount {
755 Some(amt) => serializer.serialize_some(&amt.to_btc()),
756 None => serializer.serialize_none(),
757 }
758}
759
760fn deserialize_feerate<'d, D>(deserializer: D) -> Result<FeeRate, D::Error>
764where
765 D: Deserializer<'d>,
766{
767 struct FeeRateVisitor;
768
769 impl Visitor<'_> for FeeRateVisitor {
770 type Value = FeeRate;
771
772 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
773 write!(
774 formatter,
775 "a numeric representation of fee rate in sat/vB expected"
776 )
777 }
778
779 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
780 where
781 E: de::Error,
782 {
783 let sat_per_vb = v.round() as u64;
785 let fee_rate = FeeRate::from_sat_per_vb(sat_per_vb)
786 .ok_or_else(|| de::Error::custom("Invalid fee rate"))?;
787 Ok(fee_rate)
788 }
789 }
790 deserializer.deserialize_any(FeeRateVisitor)
791}
792
793fn deserialize_signed_bitcoin<'d, D>(deserializer: D) -> Result<SignedAmount, D::Error>
795where
796 D: Deserializer<'d>,
797{
798 struct SatVisitor;
799
800 impl Visitor<'_> for SatVisitor {
801 type Value = SignedAmount;
802
803 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
804 write!(formatter, "a float representation of btc values expected")
805 }
806
807 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
808 where
809 E: de::Error,
810 {
811 let signed_amount = SignedAmount::from_btc(v).expect("Amount deserialization failed");
812 Ok(signed_amount)
813 }
814 }
815 deserializer.deserialize_any(SatVisitor)
816}
817
818#[expect(dead_code)]
820fn deserialize_signed_bitcoin_option<'d, D>(
821 deserializer: D,
822) -> Result<Option<SignedAmount>, D::Error>
823where
824 D: Deserializer<'d>,
825{
826 let f: Option<f64> = Option::deserialize(deserializer)?;
827 match f {
828 Some(v) => deserialize_signed_bitcoin(v.into_deserializer()).map(Some),
829 None => Ok(None),
830 }
831}
832
833fn deserialize_txid<'d, D>(deserializer: D) -> Result<Txid, D::Error>
835where
836 D: Deserializer<'d>,
837{
838 struct TxidVisitor;
839
840 impl Visitor<'_> for TxidVisitor {
841 type Value = Txid;
842
843 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
844 write!(formatter, "a transaction id string expected")
845 }
846
847 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
848 where
849 E: de::Error,
850 {
851 let txid = v.parse::<Txid>().expect("invalid txid");
852
853 Ok(txid)
854 }
855 }
856 deserializer.deserialize_any(TxidVisitor)
857}
858
859fn deserialize_tx<'d, D>(deserializer: D) -> Result<Transaction, D::Error>
861where
862 D: Deserializer<'d>,
863{
864 struct TxVisitor;
865
866 impl Visitor<'_> for TxVisitor {
867 type Value = Transaction;
868
869 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
870 write!(formatter, "a transaction hex string expected")
871 }
872
873 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
874 where
875 E: de::Error,
876 {
877 let tx = consensus::encode::deserialize_hex::<Transaction>(v)
878 .expect("failed to deserialize tx hex");
879 Ok(tx)
880 }
881 }
882 deserializer.deserialize_any(TxVisitor)
883}
884
885fn deserialize_psbt<'d, D>(deserializer: D) -> Result<Psbt, D::Error>
892where
893 D: Deserializer<'d>,
894{
895 struct PsbtVisitor;
896
897 impl Visitor<'_> for PsbtVisitor {
898 type Value = Psbt;
899
900 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
901 write!(formatter, "a base64-encoded PSBT string expected")
902 }
903
904 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
905 where
906 E: de::Error,
907 {
908 v.parse::<Psbt>()
909 .map_err(|e| E::custom(format!("failed to deserialize PSBT: {e}")))
910 }
911 }
912 deserializer.deserialize_any(PsbtVisitor)
913}
914
915fn deserialize_option_psbt<'d, D>(deserializer: D) -> Result<Option<Psbt>, D::Error>
922where
923 D: Deserializer<'d>,
924{
925 let opt: Option<String> = Option::deserialize(deserializer)?;
926 match opt {
927 Some(s) => s
928 .parse::<Psbt>()
929 .map(Some)
930 .map_err(|e| de::Error::custom(format!("failed to deserialize PSBT: {e}"))),
931 None => Ok(None),
932 }
933}
934
935fn deserialize_option_tx<'d, D>(deserializer: D) -> Result<Option<Transaction>, D::Error>
936where
937 D: Deserializer<'d>,
938{
939 let opt: Option<String> = Option::deserialize(deserializer)?;
940 match opt {
941 Some(s) => consensus::encode::deserialize_hex::<Transaction>(&s)
942 .map(Some)
943 .map_err(|e| de::Error::custom(format!("failed to deserialize transaction hex: {e}"))),
944 None => Ok(None),
945 }
946}
947
948fn deserialize_address<'d, D>(deserializer: D) -> Result<Address<NetworkUnchecked>, D::Error>
955where
956 D: Deserializer<'d>,
957{
958 struct AddressVisitor;
959 impl Visitor<'_> for AddressVisitor {
960 type Value = Address<NetworkUnchecked>;
961
962 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
963 write!(formatter, "a Bitcoin address string expected")
964 }
965
966 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
967 where
968 E: de::Error,
969 {
970 v.parse::<Address<_>>()
971 .map_err(|e| E::custom(format!("failed to deserialize address: {e}")))
972 }
973 }
974 deserializer.deserialize_any(AddressVisitor)
975}
976
977#[expect(dead_code)]
979fn deserialize_blockhash<'d, D>(deserializer: D) -> Result<BlockHash, D::Error>
980where
981 D: Deserializer<'d>,
982{
983 struct BlockHashVisitor;
984
985 impl Visitor<'_> for BlockHashVisitor {
986 type Value = BlockHash;
987
988 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
989 write!(formatter, "a blockhash string expected")
990 }
991
992 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
993 where
994 E: de::Error,
995 {
996 let blockhash = consensus::encode::deserialize_hex::<BlockHash>(v)
997 .expect("BlockHash deserialization failed");
998 Ok(blockhash)
999 }
1000 }
1001 deserializer.deserialize_any(BlockHashVisitor)
1002}
1003
1004#[expect(dead_code)]
1006fn deserialize_height<'d, D>(deserializer: D) -> Result<Height, D::Error>
1007where
1008 D: Deserializer<'d>,
1009{
1010 struct HeightVisitor;
1011
1012 impl Visitor<'_> for HeightVisitor {
1013 type Value = Height;
1014
1015 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1016 write!(formatter, "a height u32 string expected")
1017 }
1018
1019 fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
1020 where
1021 E: de::Error,
1022 {
1023 let height = Height::from_consensus(v).expect("Height deserialization failed");
1024 Ok(height)
1025 }
1026 }
1027 deserializer.deserialize_any(HeightVisitor)
1028}
1029
1030#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1041#[serde(rename_all = "UPPERCASE")]
1042pub enum SighashType {
1043 Default,
1045
1046 All,
1051
1052 None,
1057
1058 Single,
1063
1064 #[serde(rename = "ALL|ANYONECANPAY")]
1069 AllPlusAnyoneCanPay,
1070
1071 #[serde(rename = "NONE|ANYONECANPAY")]
1076 NonePlusAnyoneCanPay,
1077
1078 #[serde(rename = "SINGLE|ANYONECANPAY")]
1083 SinglePlusAnyoneCanPay,
1084}
1085
1086#[derive(Clone, Debug, PartialEq, Serialize, Default)]
1097pub struct WalletCreateFundedPsbtOptions {
1098 #[serde(default, rename = "fee_rate", skip_serializing_if = "Option::is_none")]
1103 pub fee_rate: Option<f64>,
1104
1105 #[serde(
1110 default,
1111 rename = "lockUnspents",
1112 skip_serializing_if = "Option::is_none"
1113 )]
1114 pub lock_unspents: Option<bool>,
1115
1116 #[serde(
1122 default,
1123 rename = "conf_target",
1124 skip_serializing_if = "Option::is_none"
1125 )]
1126 pub conf_target: Option<u16>,
1127
1128 #[serde(
1133 default,
1134 rename = "replaceable",
1135 skip_serializing_if = "Option::is_none"
1136 )]
1137 pub replaceable: Option<bool>,
1138}
1139
1140#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1150pub struct WalletCreateFundedPsbt {
1151 #[serde(deserialize_with = "deserialize_psbt")]
1156 pub psbt: Psbt,
1157
1158 #[serde(deserialize_with = "deserialize_bitcoin")]
1163 pub fee: Amount,
1164
1165 #[serde(rename = "changepos")]
1170 pub change_pos: i32,
1171}
1172
1173#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1185pub struct WalletProcessPsbtResult {
1186 #[serde(deserialize_with = "deserialize_option_psbt")]
1192 pub psbt: Option<Psbt>,
1193
1194 pub complete: bool,
1200
1201 #[serde(
1207 deserialize_with = "deserialize_option_tx",
1208 skip_serializing_if = "Option::is_none",
1209 default
1210 )]
1211 pub hex: Option<Transaction>,
1212}
1213
1214#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1224pub struct GetAddressInfo {
1225 #[serde(deserialize_with = "deserialize_address")]
1230 pub address: Address<NetworkUnchecked>,
1231
1232 #[serde(rename = "ismine")]
1237 pub is_mine: Option<bool>,
1238
1239 #[serde(rename = "iswatchonly")]
1245 pub is_watchonly: Option<bool>,
1246
1247 pub solvable: Option<bool>,
1253}
1254
1255#[derive(Clone, Debug, PartialEq, Serialize)]
1266#[serde(rename_all = "camelCase")]
1267pub struct ListUnspentQueryOptions {
1268 #[serde(serialize_with = "serialize_option_bitcoin")]
1273 pub minimum_amount: Option<Amount>,
1274
1275 #[serde(serialize_with = "serialize_option_bitcoin")]
1280 pub maximum_amount: Option<Amount>,
1281
1282 pub maximum_count: Option<u32>,
1287}
1288
1289#[derive(Clone, Debug, Default, PartialEq, Serialize)]
1291pub struct PsbtBumpFeeOptions {
1292 #[serde(skip_serializing_if = "Option::is_none")]
1294 pub conf_target: Option<u16>,
1295
1296 #[serde(skip_serializing_if = "Option::is_none")]
1298 pub fee_rate: Option<FeeRate>,
1299
1300 #[serde(skip_serializing_if = "Option::is_none")]
1302 pub replaceable: Option<bool>,
1303
1304 #[serde(skip_serializing_if = "Option::is_none")]
1306 pub estimate_mode: Option<String>,
1307
1308 #[serde(skip_serializing_if = "Option::is_none")]
1310 pub outputs: Option<Vec<CreateRawTransactionOutput>>,
1311
1312 #[serde(skip_serializing_if = "Option::is_none")]
1314 pub original_change_index: Option<u32>,
1315}
1316
1317#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1319pub struct PsbtBumpFee {
1320 #[serde(deserialize_with = "deserialize_psbt")]
1322 pub psbt: Psbt,
1323
1324 #[serde(deserialize_with = "deserialize_feerate")]
1326 pub origfee: FeeRate,
1327
1328 #[serde(deserialize_with = "deserialize_feerate")]
1330 pub fee: FeeRate,
1331
1332 pub errors: Option<Vec<String>>,
1334}
1335
1336#[cfg(test)]
1337mod tests {
1338 use super::*;
1339 use serde_json;
1340
1341 const TEST_PSBT: &str = "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA";
1344
1345 const TEST_TX_HEX: &str = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
1347
1348 #[test]
1349 fn test_wallet_process_psbt_result() {
1350 let valid_psbt = TEST_PSBT;
1351
1352 let test_tx_hex = TEST_TX_HEX;
1354 let json1 = format!(r#"{{"psbt":"{valid_psbt}","complete":true,"hex":"{test_tx_hex}"}}"#);
1355 let result1: WalletProcessPsbtResult = serde_json::from_str(&json1).unwrap();
1356 assert!(result1.psbt.is_some());
1357 assert!(result1.complete);
1358 assert!(result1.hex.is_some());
1359 let tx = result1.hex.unwrap();
1360 assert!(!tx.input.is_empty());
1361 assert!(!tx.output.is_empty());
1362
1363 let json2 = format!(r#"{{"psbt":"{valid_psbt}","complete":false}}"#);
1365 let result2: WalletProcessPsbtResult = serde_json::from_str(&json2).unwrap();
1366 assert!(result2.psbt.is_some());
1367 assert!(!result2.complete);
1368 }
1369
1370 #[test]
1371 fn test_sighashtype_serialize() {
1372 let sighash = SighashType::All;
1373 let serialized = serde_json::to_string(&sighash).unwrap();
1374 assert_eq!(serialized, "\"ALL\"");
1375
1376 let sighash2 = SighashType::AllPlusAnyoneCanPay;
1377 let serialized2 = serde_json::to_string(&sighash2).unwrap();
1378 assert_eq!(serialized2, "\"ALL|ANYONECANPAY\"");
1379 }
1380
1381 #[test]
1382 fn test_list_unspent_query_options_camelcase() {
1383 let options = ListUnspentQueryOptions {
1384 minimum_amount: Some(Amount::from_btc(0.5).unwrap()),
1385 maximum_amount: Some(Amount::from_btc(2.0).unwrap()),
1386 maximum_count: Some(10),
1387 };
1388 let serialized = serde_json::to_string(&options).unwrap();
1389
1390 assert!(serialized.contains("\"minimumAmount\":0.5"));
1391 assert!(serialized.contains("\"maximumAmount\":2.0"));
1392 assert!(serialized.contains("\"maximumCount\":10"));
1393 }
1394
1395 #[test]
1396 fn test_psbt_parsing() {
1397 let valid_psbt = TEST_PSBT;
1399 let json1 = format!(r#"{{"psbt":"{valid_psbt}","fee":0.001,"changepos":-1}}"#);
1400 let result1: WalletCreateFundedPsbt = serde_json::from_str(&json1).unwrap();
1401 assert!(!result1.psbt.inputs.is_empty()); let invalid_psbt = "invalid_base64";
1405 let json2 = format!(r#"{{"psbt":"{invalid_psbt}","fee":0.001,"changepos":-1}}"#);
1406 let result2 = serde_json::from_str::<WalletCreateFundedPsbt>(&json2);
1407 assert!(result2.is_err());
1408 }
1409}