bitcoind_async_client/
types.rs

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/// The category of a transaction.
19///
20/// This is one of the results of `listtransactions` RPC method.
21///
22/// # Note
23///
24/// This is a subset of the categories available in Bitcoin Core.
25/// It also assumes that the transactions are present in the underlying Bitcoin
26/// client's wallet.
27#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
28#[serde(rename_all = "lowercase")]
29pub enum TransactionCategory {
30    /// Transactions sent.
31    Send,
32    /// Non-coinbase transactions received.
33    Receive,
34    /// Coinbase transactions received with more than 100 confirmations.
35    Generate,
36    /// Coinbase transactions received with 100 or less confirmations.
37    Immature,
38    /// Orphaned coinbase transactions received.
39    Orphan,
40}
41
42/// Result of JSON-RPC method `getblockchaininfo`.
43///
44/// Method call: `getblockchaininfo`
45///
46/// > Returns an object containing various state info regarding blockchain processing.
47#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
48pub struct GetBlockchainInfo {
49    /// Current network name as defined in BIP70 (main, test, signet, regtest).
50    pub chain: String,
51    /// The current number of blocks processed in the server.
52    pub blocks: u64,
53    /// The current number of headers we have validated.
54    pub headers: u64,
55    /// The hash of the currently best block.
56    #[serde(rename = "bestblockhash")]
57    pub best_block_hash: String,
58    /// The current difficulty.
59    pub difficulty: f64,
60    /// Median time for the current best block.
61    #[serde(rename = "mediantime")]
62    pub median_time: u64,
63    /// Estimate of verification progress (between 0 and 1).
64    #[serde(rename = "verificationprogress")]
65    pub verification_progress: f64,
66    /// Estimate of whether this node is in Initial Block Download (IBD) mode.
67    #[serde(rename = "initialblockdownload")]
68    pub initial_block_download: bool,
69    /// Total amount of work in active chain, in hexadecimal.
70    #[serde(rename = "chainwork")]
71    pub chain_work: String,
72    /// The estimated size of the block and undo files on disk.
73    pub size_on_disk: u64,
74    /// If the blocks are subject to pruning.
75    pub pruned: bool,
76    /// Lowest-height complete block stored (only present if pruning is enabled).
77    #[serde(rename = "pruneheight")]
78    pub prune_height: Option<u64>,
79    /// Whether automatic pruning is enabled (only present if pruning is enabled).
80    pub automatic_pruning: Option<bool>,
81    /// The target size used by pruning (only present if automatic pruning is enabled).
82    pub prune_target_size: Option<u64>,
83}
84
85/// Result of JSON-RPC method `getblockheader` with verbosity set to 0.
86///
87/// A string that is serialized, hex-encoded data for block 'hash'.
88///
89/// Method call: `getblockheader "blockhash" ( verbosity )`
90#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
91pub struct GetBlockHeaderVerbosityZero(pub String);
92
93impl GetBlockHeaderVerbosityZero {
94    /// Converts json straight to a [`Header`].
95    pub fn header(self) -> Result<Header, encode::FromHexError> {
96        let header: Header = encode::deserialize_hex(&self.0)?;
97        Ok(header)
98    }
99}
100
101/// Result of JSON-RPC method `getblock` with verbosity set to 0.
102///
103/// A string that is serialized, hex-encoded data for block 'hash'.
104///
105/// Method call: `getblock "blockhash" ( verbosity )`
106#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
107pub struct GetBlockVerbosityZero(pub String);
108
109impl GetBlockVerbosityZero {
110    /// Converts json straight to a [`Block`].
111    pub fn block(self) -> Result<Block, encode::FromHexError> {
112        let block: Block = encode::deserialize_hex(&self.0)?;
113        Ok(block)
114    }
115}
116
117/// Result of JSON-RPC method `getblock` with verbosity set to 1.
118#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
119pub struct GetBlockVerbosityOne {
120    /// The block hash (same as provided) in RPC call.
121    pub hash: String,
122    /// The number of confirmations, or -1 if the block is not on the main chain.
123    pub confirmations: i32,
124    /// The block size.
125    pub size: usize,
126    /// The block size excluding witness data.
127    #[serde(rename = "strippedsize")]
128    pub stripped_size: Option<usize>,
129    /// The block weight as defined in BIP-141.
130    pub weight: u64,
131    /// The block height or index.
132    pub height: usize,
133    /// The block version.
134    pub version: i32,
135    /// The block version formatted in hexadecimal.
136    #[serde(rename = "versionHex")]
137    pub version_hex: String,
138    /// The merkle root
139    #[serde(rename = "merkleroot")]
140    pub merkle_root: String,
141    /// The transaction ids
142    pub tx: Vec<String>,
143    /// The block time expressed in UNIX epoch time.
144    pub time: usize,
145    /// The median block time expressed in UNIX epoch time.
146    #[serde(rename = "mediantime")]
147    pub median_time: Option<usize>,
148    /// The nonce
149    pub nonce: u32,
150    /// The bits.
151    pub bits: String,
152    /// The difficulty.
153    pub difficulty: f64,
154    /// Expected number of hashes required to produce the chain up to this block (in hex).
155    #[serde(rename = "chainwork")]
156    pub chain_work: String,
157    /// The number of transactions in the block.
158    #[serde(rename = "nTx")]
159    pub n_tx: u32,
160    /// The hash of the previous block (if available).
161    #[serde(rename = "previousblockhash")]
162    pub previous_block_hash: Option<String>,
163    /// The hash of the next block (if available).
164    #[serde(rename = "nextblockhash")]
165    pub next_block_hash: Option<String>,
166}
167
168/// Result of JSON-RPC method `getrawtransaction` with verbosity set to 0.
169///
170/// A string that is serialized, hex-encoded data for transaction.
171///
172/// Method call: `getrawtransaction "txid" ( verbosity )`
173#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
174pub struct GetRawTransactionVerbosityZero(pub String);
175
176impl GetRawTransactionVerbosityZero {
177    /// Converts json straight to a [`Transaction`].
178    pub fn transaction(self) -> Result<Transaction, encode::FromHexError> {
179        let transaction: Transaction = encode::deserialize_hex(&self.0)?;
180        Ok(transaction)
181    }
182}
183
184/// Result of JSON-RPC method `getmempoolinfo`.
185///
186/// Method call: `getmempoolinfo`
187#[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
199/// Result of JSON-RPC method `getrawtransaction` with verbosity set to 1.
200///
201/// Method call: `getrawtransaction "txid" ( verbosity )`
202#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
203pub struct GetRawTransactionVerbosityOne {
204    pub in_active_chain: Option<bool>,
205    #[serde(deserialize_with = "deserialize_tx")]
206    #[serde(rename = "hex")]
207    pub transaction: Transaction,
208    pub txid: Txid,
209    pub hash: Wtxid,
210    pub size: usize,
211    pub vsize: usize,
212    pub version: u32,
213    pub locktime: u32,
214    pub blockhash: Option<BlockHash>,
215    pub confirmations: Option<u32>,
216    pub time: Option<usize>,
217    pub blocktime: Option<usize>,
218}
219
220/// Result of JSON-RPC method `gettxout`.
221///
222/// > gettxout "txid" n ( include_mempool )
223/// >
224/// > Returns details about an unspent transaction output.
225/// >
226/// > Arguments:
227/// > 1. txid               (string, required) The transaction id
228/// > 2. n                  (numeric, required) vout number
229#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
230pub struct GetTxOut {
231    /// The hash of the block at the tip of the chain.
232    #[serde(rename = "bestblock")]
233    pub best_block: String,
234    /// The number of confirmations.
235    pub confirmations: u32, // TODO: Change this to an i64.
236    /// The transaction value in BTC.
237    pub value: f64,
238    /// The script pubkey.
239    #[serde(rename = "scriptPubkey")]
240    pub script_pubkey: Option<ScriptPubkey>,
241    /// Coinbase or not.
242    pub coinbase: bool,
243}
244
245/// A script pubkey.
246#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
247pub struct ScriptPubkey {
248    /// Script assembly.
249    pub asm: String,
250    /// Script hex.
251    pub hex: String,
252    #[serde(rename = "reqSigs")]
253    pub req_sigs: i64,
254    /// The type, eg pubkeyhash.
255    #[serde(rename = "type")]
256    pub type_: String,
257    /// Bitcoin address.
258    pub address: Option<String>,
259}
260
261/// Models the arguments of JSON-RPC method `createrawtransaction`.
262///
263/// # Note
264///
265/// Assumes that the transaction is always "replaceable" by default and has a locktime of 0.
266#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
267pub struct CreateRawTransaction {
268    pub inputs: Vec<CreateRawTransactionInput>,
269    pub outputs: Vec<CreateRawTransactionOutput>,
270}
271
272/// Models the input of JSON-RPC method `createrawtransaction`.
273#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
274pub struct CreateRawTransactionInput {
275    pub txid: String,
276    pub vout: u32,
277}
278
279/// Models transaction outputs for Bitcoin RPC methods.
280///
281/// Used by various RPC methods such as `createrawtransaction`, `psbtbumpfee`,
282/// and `walletcreatefundedpsbt`. The outputs are specified as key-value pairs,
283/// where the keys are addresses and the values are amounts to send.
284#[derive(Clone, Debug, PartialEq, Deserialize)]
285#[serde(untagged)]
286pub enum CreateRawTransactionOutput {
287    /// A pair of an [`Address`] string and an [`Amount`] in BTC.
288    AddressAmount {
289        /// An [`Address`] string.
290        address: String,
291        /// An [`Amount`] in BTC.
292        amount: f64,
293    },
294    /// A payload such as in `OP_RETURN` transactions.
295    Data {
296        /// The payload.
297        data: String,
298    },
299}
300
301impl Serialize for CreateRawTransactionOutput {
302    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303    where
304        S: serde::Serializer,
305    {
306        match self {
307            CreateRawTransactionOutput::AddressAmount { address, amount } => {
308                let mut map = serde_json::Map::new();
309                map.insert(
310                    address.clone(),
311                    serde_json::Value::Number(serde_json::Number::from_f64(*amount).unwrap()),
312                );
313                map.serialize(serializer)
314            }
315            CreateRawTransactionOutput::Data { data } => {
316                let mut map = serde_json::Map::new();
317                map.insert("data".to_string(), serde_json::Value::String(data.clone()));
318                map.serialize(serializer)
319            }
320        }
321    }
322}
323
324/// Result of JSON-RPC method `submitpackage`.
325///
326/// > submitpackage ["rawtx",...] ( maxfeerate maxburnamount )
327/// >
328/// > Submit a package of raw transactions (serialized, hex-encoded) to local node.
329/// > The package will be validated according to consensus and mempool policy rules. If any
330/// > transaction passes, it will be accepted to mempool.
331/// > This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md
332/// > for documentation on package policies.
333/// > Warning: successful submission does not mean the transactions will propagate throughout the
334/// > network.
335/// >
336/// > Arguments:
337/// > 1. package          (json array, required) An array of raw transactions.
338/// > The package must solely consist of a child and its parents. None of the parents may depend on
339/// > each other.
340/// > The package must be topologically sorted, with the child being the last element in the array.
341/// > [
342/// > "rawtx",     (string)
343/// > ...
344/// > ]
345#[allow(clippy::doc_lazy_continuation)]
346#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
347pub struct SubmitPackage {
348    /// The transaction package result message.
349    ///
350    /// "success" indicates all transactions were accepted into or are already in the mempool.
351    pub package_msg: String,
352    /// Transaction results keyed by wtxid.
353    #[serde(rename = "tx-results")]
354    pub tx_results: BTreeMap<String, SubmitPackageTxResult>,
355    /// List of txids of replaced transactions.
356    #[serde(rename = "replaced-transactions")]
357    pub replaced_transactions: Vec<String>,
358}
359
360/// Models the per-transaction result included in the JSON-RPC method `submitpackage`.
361#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
362pub struct SubmitPackageTxResult {
363    /// The transaction id.
364    pub txid: String,
365    /// The wtxid of a different transaction with the same txid but different witness found in the
366    /// mempool.
367    ///
368    /// If set, this means the submitted transaction was ignored.
369    #[serde(rename = "other-wtxid")]
370    pub other_wtxid: Option<String>,
371    /// Sigops-adjusted virtual transaction size.
372    pub vsize: i64,
373    /// Transaction fees.
374    pub fees: Option<SubmitPackageTxResultFees>,
375    /// The transaction error string, if it was rejected by the mempool
376    pub error: Option<String>,
377}
378
379/// Models the fees included in the per-transaction result of the JSON-RPC method `submitpackage`.
380#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
381pub struct SubmitPackageTxResultFees {
382    /// Transaction fee.
383    #[serde(rename = "base")]
384    pub base_fee: f64,
385    /// The effective feerate.
386    ///
387    /// Will be `None` if the transaction was already in the mempool. For example, the package
388    /// feerate and/or feerate with modified fees from the `prioritisetransaction` JSON-RPC method.
389    #[serde(rename = "effective-feerate")]
390    pub effective_fee_rate: Option<f64>,
391    /// If [`Self::effective_fee_rate`] is provided, this holds the wtxid's of the transactions
392    /// whose fees and vsizes are included in effective-feerate.
393    #[serde(rename = "effective-includes")]
394    pub effective_includes: Option<Vec<String>>,
395}
396
397/// Result of JSON-RPC method `gettxout`.
398///
399/// # Note
400///
401/// This assumes that the UTXOs are present in the underlying Bitcoin
402/// client's wallet.
403#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
404pub struct GetTransactionDetail {
405    pub address: String,
406    pub category: GetTransactionDetailCategory,
407    pub amount: f64,
408    pub label: Option<String>,
409    pub vout: u32,
410    pub fee: Option<f64>,
411    pub abandoned: Option<bool>,
412}
413
414/// Enum to represent the category of a transaction.
415#[derive(Copy, Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
416#[serde(rename_all = "lowercase")]
417pub enum GetTransactionDetailCategory {
418    Send,
419    Receive,
420    Generate,
421    Immature,
422    Orphan,
423}
424
425/// Result of the JSON-RPC method `getnewaddress`.
426///
427/// # Note
428///
429/// This assumes that the UTXOs are present in the underlying Bitcoin
430/// client's wallet.
431#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
432pub struct GetNewAddress(pub String);
433
434impl GetNewAddress {
435    /// Converts json straight to a [`Address`].
436    pub fn address(self) -> Result<Address<NetworkUnchecked>, address::ParseError> {
437        let address = self.0.parse::<Address<_>>()?;
438        Ok(address)
439    }
440}
441
442/// Models the result of JSON-RPC method `listunspent`.
443///
444/// # Note
445///
446/// This assumes that the UTXOs are present in the underlying Bitcoin
447/// client's wallet.
448///
449/// Careful with the amount field. It is a [`SignedAmount`], hence can be negative.
450/// Negative amounts for the [`TransactionCategory::Send`], and is positive
451/// for all other categories.
452#[derive(Clone, Debug, PartialEq, Deserialize)]
453pub struct GetTransaction {
454    /// The signed amount in BTC.
455    #[serde(deserialize_with = "deserialize_signed_bitcoin")]
456    pub amount: SignedAmount,
457    /// The signed fee in BTC.
458    pub confirmations: u64,
459    pub generated: Option<bool>,
460    pub trusted: Option<bool>,
461    pub blockhash: Option<String>,
462    pub blockheight: Option<u64>,
463    pub blockindex: Option<u32>,
464    pub blocktime: Option<u64>,
465    /// The transaction id.
466    #[serde(deserialize_with = "deserialize_txid")]
467    pub txid: Txid,
468    pub wtxid: String,
469    pub walletconflicts: Vec<String>,
470    pub replaced_by_txid: Option<String>,
471    pub replaces_txid: Option<String>,
472    pub comment: Option<String>,
473    pub to: Option<String>,
474    pub time: u64,
475    pub timereceived: u64,
476    #[serde(rename = "bip125-replaceable")]
477    pub bip125_replaceable: String,
478    pub details: Vec<GetTransactionDetail>,
479    /// The transaction itself.
480    #[serde(deserialize_with = "deserialize_tx")]
481    pub hex: Transaction,
482}
483
484impl GetTransaction {
485    pub fn block_height(&self) -> u64 {
486        if self.confirmations == 0 {
487            return 0;
488        }
489        self.blockheight.unwrap_or_else(|| {
490            warn!("Txn confirmed but did not obtain blockheight. Setting height to zero");
491            0
492        })
493    }
494}
495
496/// Models the result of JSON-RPC method `listunspent`.
497///
498/// # Note
499///
500/// This assumes that the UTXOs are present in the underlying Bitcoin
501/// client's wallet.
502#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
503pub struct ListUnspent {
504    /// The transaction id.
505    #[serde(deserialize_with = "deserialize_txid")]
506    pub txid: Txid,
507    /// The vout value.
508    pub vout: u32,
509    /// The Bitcoin address.
510    #[serde(deserialize_with = "deserialize_address")]
511    pub address: Address<NetworkUnchecked>,
512    // The associated label, if any.
513    pub label: Option<String>,
514    /// The script pubkey.
515    #[serde(rename = "scriptPubKey")]
516    pub script_pubkey: String,
517    /// The transaction output amount in BTC.
518    #[serde(deserialize_with = "deserialize_bitcoin")]
519    pub amount: Amount,
520    /// The number of confirmations.
521    pub confirmations: u32,
522    /// Whether we have the private keys to spend this output.
523    pub spendable: bool,
524    /// Whether we know how to spend this output, ignoring the lack of keys.
525    pub solvable: bool,
526    /// Whether this output is considered safe to spend.
527    /// Unconfirmed transactions from outside keys and unconfirmed replacement
528    /// transactions are considered unsafe and are not eligible for spending by
529    /// `fundrawtransaction` and `sendtoaddress`.
530    pub safe: bool,
531}
532
533/// Models the result of JSON-RPC method `listtransactions`.
534///
535/// # Note
536///
537/// This assumes that the transactions are present in the underlying Bitcoin
538/// client's wallet.
539///
540/// Careful with the amount field. It is a [`SignedAmount`], hence can be negative.
541/// Negative amounts for the [`TransactionCategory::Send`], and is positive
542/// for all other categories.
543#[derive(Clone, Debug, PartialEq, Deserialize)]
544pub struct ListTransactions {
545    /// The Bitcoin address.
546    #[serde(deserialize_with = "deserialize_address")]
547    pub address: Address<NetworkUnchecked>,
548    /// Category of the transaction.
549    category: TransactionCategory,
550    /// The signed amount in BTC.
551    #[serde(deserialize_with = "deserialize_signed_bitcoin")]
552    pub amount: SignedAmount,
553    /// The label associated with the address, if any.
554    pub label: Option<String>,
555    /// The number of confirmations.
556    pub confirmations: u32,
557    pub trusted: Option<bool>,
558    pub generated: Option<bool>,
559    pub blockhash: Option<String>,
560    pub blockheight: Option<u64>,
561    pub blockindex: Option<u32>,
562    pub blocktime: Option<u64>,
563    /// The transaction id.
564    #[serde(deserialize_with = "deserialize_txid")]
565    pub txid: Txid,
566}
567
568/// Models the result of JSON-RPC method `testmempoolaccept`.
569#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
570pub struct TestMempoolAccept {
571    /// The transaction id.
572    #[serde(deserialize_with = "deserialize_txid")]
573    pub txid: Txid,
574    /// Rejection reason, if any.
575    pub reject_reason: Option<String>,
576}
577
578/// Models the result of JSON-RPC method `signrawtransactionwithwallet`.
579///
580/// # Note
581///
582/// This assumes that the transactions are present in the underlying Bitcoin
583/// client's wallet.
584#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
585pub struct SignRawTransactionWithWallet {
586    /// The Transaction ID.
587    pub hex: String,
588    /// If the transaction has a complete set of signatures.
589    pub complete: bool,
590    /// Errors, if any.
591    pub errors: Option<Vec<SignRawTransactionWithWalletError>>,
592}
593
594/// Models the optional previous transaction outputs argument for the method
595/// `signrawtransactionwithwallet`.
596///
597/// These are the outputs that this transaction depends on but may not yet be in the block chain.
598/// Widely used for One Parent One Child (1P1C) Relay in Bitcoin >28.0.
599///
600/// > transaction outputs
601/// > [
602/// > {                            (json object)
603/// > "txid": "hex",             (string, required) The transaction id
604/// > "vout": n,                 (numeric, required) The output number
605/// > "scriptPubKey": "hex",     (string, required) The output script
606/// > "redeemScript": "hex",     (string, optional) (required for P2SH) redeem script
607/// > "witnessScript": "hex",    (string, optional) (required for P2WSH or P2SH-P2WSH) witness
608/// > script
609/// > "amount": amount,          (numeric or string, optional) (required for Segwit inputs) the
610/// > amount spent
611/// > },
612/// > ...
613/// > ]
614#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
615pub struct PreviousTransactionOutput {
616    /// The transaction id.
617    #[serde(deserialize_with = "deserialize_txid")]
618    pub txid: Txid,
619    /// The output number.
620    pub vout: u32,
621    /// The output script.
622    #[serde(rename = "scriptPubKey")]
623    pub script_pubkey: String,
624    /// The redeem script.
625    #[serde(rename = "redeemScript")]
626    pub redeem_script: Option<String>,
627    /// The witness script.
628    #[serde(rename = "witnessScript")]
629    pub witness_script: Option<String>,
630    /// The amount spent.
631    pub amount: Option<f64>,
632}
633
634/// Models the result of the JSON-RPC method `listdescriptors`.
635#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
636pub struct ListDescriptors {
637    /// The descriptors
638    pub descriptors: Vec<ListDescriptor>,
639}
640
641/// Models the Descriptor in the result of the JSON-RPC method `listdescriptors`.
642#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
643pub struct ListDescriptor {
644    /// The descriptor.
645    pub desc: String,
646}
647
648/// Models the result of the JSON-RPC method `importdescriptors`.
649#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
650pub struct ImportDescriptors {
651    /// The descriptors
652    pub descriptors: Vec<ListDescriptor>,
653}
654
655/// Models the Descriptor in the result of the JSON-RPC method `importdescriptors`.
656#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
657pub struct ImportDescriptor {
658    /// The descriptor.
659    pub desc: String,
660    /// Set this descriptor to be the active descriptor
661    /// for the corresponding output type/externality.
662    pub active: Option<bool>,
663    /// Time from which to start rescanning the blockchain for this descriptor,
664    /// in UNIX epoch time. Can also be a string "now"
665    pub timestamp: String,
666}
667/// Models the Descriptor in the result of the JSON-RPC method `importdescriptors`.
668#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
669pub struct ImportDescriptorResult {
670    /// Result.
671    pub success: bool,
672}
673
674/// Models the `createwallet` JSON-RPC method.
675///
676/// # Note
677///
678/// This can also be used for the `loadwallet` JSON-RPC method.
679#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
680pub struct CreateWallet {
681    /// Wallet name
682    pub wallet_name: String,
683    /// Load on startup
684    pub load_on_startup: Option<bool>,
685}
686
687/// Deserializes the amount in BTC into proper [`Amount`]s.
688fn deserialize_bitcoin<'d, D>(deserializer: D) -> Result<Amount, D::Error>
689where
690    D: Deserializer<'d>,
691{
692    struct SatVisitor;
693
694    impl Visitor<'_> for SatVisitor {
695        type Value = Amount;
696
697        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
698            write!(formatter, "a float representation of btc values expected")
699        }
700
701        fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
702        where
703            E: de::Error,
704        {
705            let amount = Amount::from_btc(v).expect("Amount deserialization failed");
706            Ok(amount)
707        }
708    }
709    deserializer.deserialize_any(SatVisitor)
710}
711
712/// Serializes the optional [`Amount`] into BTC.
713fn serialize_option_bitcoin<S>(amount: &Option<Amount>, serializer: S) -> Result<S::Ok, S::Error>
714where
715    S: Serializer,
716{
717    match amount {
718        Some(amt) => serializer.serialize_some(&amt.to_btc()),
719        None => serializer.serialize_none(),
720    }
721}
722
723/// Deserializes the fee rate from sat/vB into proper [`FeeRate`].
724///
725/// Note: Bitcoin Core 0.21+ uses sat/vB for fee rates for most RPC methods/results.
726fn deserialize_feerate<'d, D>(deserializer: D) -> Result<FeeRate, D::Error>
727where
728    D: Deserializer<'d>,
729{
730    struct FeeRateVisitor;
731
732    impl Visitor<'_> for FeeRateVisitor {
733        type Value = FeeRate;
734
735        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
736            write!(
737                formatter,
738                "a numeric representation of fee rate in sat/vB expected"
739            )
740        }
741
742        fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
743        where
744            E: de::Error,
745        {
746            // The value is already in sat/vB (Bitcoin Core 0.21+)
747            let sat_per_vb = v.round() as u64;
748            let fee_rate = FeeRate::from_sat_per_vb(sat_per_vb)
749                .ok_or_else(|| de::Error::custom("Invalid fee rate"))?;
750            Ok(fee_rate)
751        }
752    }
753    deserializer.deserialize_any(FeeRateVisitor)
754}
755
756/// Deserializes the *signed* amount in BTC into proper [`SignedAmount`]s.
757fn deserialize_signed_bitcoin<'d, D>(deserializer: D) -> Result<SignedAmount, D::Error>
758where
759    D: Deserializer<'d>,
760{
761    struct SatVisitor;
762
763    impl Visitor<'_> for SatVisitor {
764        type Value = SignedAmount;
765
766        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
767            write!(formatter, "a float representation of btc values expected")
768        }
769
770        fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
771        where
772            E: de::Error,
773        {
774            let signed_amount = SignedAmount::from_btc(v).expect("Amount deserialization failed");
775            Ok(signed_amount)
776        }
777    }
778    deserializer.deserialize_any(SatVisitor)
779}
780
781/// Deserializes the *signed* amount in BTC into proper [`SignedAmount`]s.
782#[expect(dead_code)]
783fn deserialize_signed_bitcoin_option<'d, D>(
784    deserializer: D,
785) -> Result<Option<SignedAmount>, D::Error>
786where
787    D: Deserializer<'d>,
788{
789    let f: Option<f64> = Option::deserialize(deserializer)?;
790    match f {
791        Some(v) => deserialize_signed_bitcoin(v.into_deserializer()).map(Some),
792        None => Ok(None),
793    }
794}
795
796/// Deserializes the transaction id string into proper [`Txid`]s.
797fn deserialize_txid<'d, D>(deserializer: D) -> Result<Txid, D::Error>
798where
799    D: Deserializer<'d>,
800{
801    struct TxidVisitor;
802
803    impl Visitor<'_> for TxidVisitor {
804        type Value = Txid;
805
806        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
807            write!(formatter, "a transaction id string expected")
808        }
809
810        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
811        where
812            E: de::Error,
813        {
814            let txid = v.parse::<Txid>().expect("invalid txid");
815
816            Ok(txid)
817        }
818    }
819    deserializer.deserialize_any(TxidVisitor)
820}
821
822/// Deserializes the transaction hex string into proper [`Transaction`]s.
823fn deserialize_tx<'d, D>(deserializer: D) -> Result<Transaction, D::Error>
824where
825    D: Deserializer<'d>,
826{
827    struct TxVisitor;
828
829    impl Visitor<'_> for TxVisitor {
830        type Value = Transaction;
831
832        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
833            write!(formatter, "a transaction hex string expected")
834        }
835
836        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
837        where
838            E: de::Error,
839        {
840            let tx = consensus::encode::deserialize_hex::<Transaction>(v)
841                .expect("failed to deserialize tx hex");
842            Ok(tx)
843        }
844    }
845    deserializer.deserialize_any(TxVisitor)
846}
847
848/// Deserializes a base64-encoded PSBT string into proper [`Psbt`]s.
849///
850/// # Note
851///
852/// Expects a valid base64-encoded PSBT as defined in BIP 174. The PSBT
853/// string must contain valid transaction data and metadata for successful parsing.
854fn deserialize_psbt<'d, D>(deserializer: D) -> Result<Psbt, D::Error>
855where
856    D: Deserializer<'d>,
857{
858    struct PsbtVisitor;
859
860    impl Visitor<'_> for PsbtVisitor {
861        type Value = Psbt;
862
863        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
864            write!(formatter, "a base64-encoded PSBT string expected")
865        }
866
867        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
868        where
869            E: de::Error,
870        {
871            v.parse::<Psbt>()
872                .map_err(|e| E::custom(format!("failed to deserialize PSBT: {e}")))
873        }
874    }
875    deserializer.deserialize_any(PsbtVisitor)
876}
877
878/// Deserializes an optional base64-encoded PSBT string into `Option<Psbt>`.
879///
880/// # Note
881///
882/// When the JSON field is `null` or missing, returns `None`. When present,
883/// deserializes the base64 PSBT string using the same validation as [`deserialize_psbt`].
884fn deserialize_option_psbt<'d, D>(deserializer: D) -> Result<Option<Psbt>, D::Error>
885where
886    D: Deserializer<'d>,
887{
888    let opt: Option<String> = Option::deserialize(deserializer)?;
889    match opt {
890        Some(s) => s
891            .parse::<Psbt>()
892            .map(Some)
893            .map_err(|e| de::Error::custom(format!("failed to deserialize PSBT: {e}"))),
894        None => Ok(None),
895    }
896}
897
898fn deserialize_option_tx<'d, D>(deserializer: D) -> Result<Option<Transaction>, D::Error>
899where
900    D: Deserializer<'d>,
901{
902    let opt: Option<String> = Option::deserialize(deserializer)?;
903    match opt {
904        Some(s) => consensus::encode::deserialize_hex::<Transaction>(&s)
905            .map(Some)
906            .map_err(|e| de::Error::custom(format!("failed to deserialize transaction hex: {e}"))),
907        None => Ok(None),
908    }
909}
910
911/// Deserializes the address string into proper [`Address`]s.
912///
913/// # Note
914///
915/// The user is responsible for ensuring that the address is valid,
916/// since this functions returns an [`Address<NetworkUnchecked>`].
917fn deserialize_address<'d, D>(deserializer: D) -> Result<Address<NetworkUnchecked>, D::Error>
918where
919    D: Deserializer<'d>,
920{
921    struct AddressVisitor;
922    impl Visitor<'_> for AddressVisitor {
923        type Value = Address<NetworkUnchecked>;
924
925        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
926            write!(formatter, "a Bitcoin address string expected")
927        }
928
929        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
930        where
931            E: de::Error,
932        {
933            v.parse::<Address<_>>()
934                .map_err(|e| E::custom(format!("failed to deserialize address: {e}")))
935        }
936    }
937    deserializer.deserialize_any(AddressVisitor)
938}
939
940/// Deserializes the blockhash string into proper [`BlockHash`]s.
941#[expect(dead_code)]
942fn deserialize_blockhash<'d, D>(deserializer: D) -> Result<BlockHash, D::Error>
943where
944    D: Deserializer<'d>,
945{
946    struct BlockHashVisitor;
947
948    impl Visitor<'_> for BlockHashVisitor {
949        type Value = BlockHash;
950
951        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
952            write!(formatter, "a blockhash string expected")
953        }
954
955        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
956        where
957            E: de::Error,
958        {
959            let blockhash = consensus::encode::deserialize_hex::<BlockHash>(v)
960                .expect("BlockHash deserialization failed");
961            Ok(blockhash)
962        }
963    }
964    deserializer.deserialize_any(BlockHashVisitor)
965}
966
967/// Deserializes the height string into proper [`Height`]s.
968#[expect(dead_code)]
969fn deserialize_height<'d, D>(deserializer: D) -> Result<Height, D::Error>
970where
971    D: Deserializer<'d>,
972{
973    struct HeightVisitor;
974
975    impl Visitor<'_> for HeightVisitor {
976        type Value = Height;
977
978        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
979            write!(formatter, "a height u32 string expected")
980        }
981
982        fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
983        where
984            E: de::Error,
985        {
986            let height = Height::from_consensus(v).expect("Height deserialization failed");
987            Ok(height)
988        }
989    }
990    deserializer.deserialize_any(HeightVisitor)
991}
992
993/// Signature hash types for Bitcoin transactions.
994///
995/// These types specify which parts of a transaction are included in the signature
996/// hash calculation when signing transaction inputs. Used with wallet signing
997/// operations like `wallet_process_psbt`.
998///
999/// # Note
1000///
1001/// These correspond to the SIGHASH flags defined in Bitcoin's script system
1002/// and BIP 143 (witness transaction digest).
1003#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1004#[serde(rename_all = "UPPERCASE")]
1005pub enum SighashType {
1006    /// Use the default signature hash type (equivalent to SIGHASH_ALL).
1007    Default,
1008
1009    /// Sign all inputs and all outputs of the transaction.
1010    ///
1011    /// This is the most common and secure signature type, ensuring the entire
1012    /// transaction structure cannot be modified after signing.
1013    All,
1014
1015    /// Sign all inputs but no outputs.
1016    ///
1017    /// Allows outputs to be modified after signing, useful for donation scenarios
1018    /// where the exact destination amounts can be adjusted.
1019    None,
1020
1021    /// Sign all inputs and the output with the same index as this input.
1022    ///
1023    /// Used in scenarios where multiple parties contribute inputs and want to
1024    /// ensure their corresponding output is protected.
1025    Single,
1026
1027    /// Combination of SIGHASH_ALL with ANYONECANPAY flag.
1028    ///
1029    /// Signs all outputs but only this specific input, allowing other inputs
1030    /// to be added or removed. Useful for crowdfunding transactions.
1031    #[serde(rename = "ALL|ANYONECANPAY")]
1032    AllPlusAnyoneCanPay,
1033
1034    /// Combination of SIGHASH_NONE with ANYONECANPAY flag.
1035    ///
1036    /// Signs only this specific input with no outputs committed, providing
1037    /// maximum flexibility for transaction modification.
1038    #[serde(rename = "NONE|ANYONECANPAY")]
1039    NonePlusAnyoneCanPay,
1040
1041    /// Combination of SIGHASH_SINGLE with ANYONECANPAY flag.
1042    ///
1043    /// Signs only this input and its corresponding output, allowing other
1044    /// inputs and outputs to be modified independently.
1045    #[serde(rename = "SINGLE|ANYONECANPAY")]
1046    SinglePlusAnyoneCanPay,
1047}
1048
1049/// Options for creating a funded PSBT with wallet inputs.
1050///
1051/// Used with `wallet_create_funded_psbt` to control funding behavior,
1052/// fee estimation, and transaction policies when the wallet automatically
1053/// selects inputs to fund the specified outputs.
1054///
1055/// # Note
1056///
1057/// All fields are optional and will use Bitcoin Core defaults if not specified.
1058/// Fee rate takes precedence over confirmation target if both are provided.
1059#[derive(Clone, Debug, PartialEq, Serialize, Default)]
1060pub struct WalletCreateFundedPsbtOptions {
1061    /// Fee rate in sat/vB (satoshis per virtual byte) for the transaction.
1062    ///
1063    /// If specified, this overrides the `conf_target` parameter for fee estimation.
1064    /// Must be a positive value representing the desired fee density.
1065    #[serde(default, rename = "fee_rate", skip_serializing_if = "Option::is_none")]
1066    pub fee_rate: Option<f64>,
1067
1068    /// Whether to lock the selected UTXOs to prevent them from being spent by other transactions.
1069    ///
1070    /// When `true`, the wallet will temporarily lock the selected unspent outputs
1071    /// until the transaction is broadcast or manually unlocked. Default is `false`.
1072    #[serde(
1073        default,
1074        rename = "lockUnspents",
1075        skip_serializing_if = "Option::is_none"
1076    )]
1077    pub lock_unspents: Option<bool>,
1078
1079    /// Target number of confirmations for automatic fee estimation.
1080    ///
1081    /// Represents the desired number of blocks within which the transaction should
1082    /// be confirmed. Higher values result in lower fees but longer confirmation times.
1083    /// Ignored if `fee_rate` is specified.
1084    #[serde(
1085        default,
1086        rename = "conf_target",
1087        skip_serializing_if = "Option::is_none"
1088    )]
1089    pub conf_target: Option<u16>,
1090
1091    /// Whether the transaction should be BIP-125 opt-in Replace-By-Fee (RBF) enabled.
1092    ///
1093    /// When `true`, allows the transaction to be replaced with a higher-fee version
1094    /// before confirmation. Useful for fee bumping if the initial fee proves insufficient.
1095    #[serde(
1096        default,
1097        rename = "replaceable",
1098        skip_serializing_if = "Option::is_none"
1099    )]
1100    pub replaceable: Option<bool>,
1101}
1102
1103/// Result of the `walletcreatefundedpsbt` RPC method.
1104///
1105/// Contains a funded PSBT created by the wallet with automatically selected inputs
1106/// to cover the specified outputs, along with fee information and change output details.
1107///
1108/// # Note
1109///
1110/// The PSBT returned is not signed and requires further processing with
1111/// `wallet_process_psbt` or `finalize_psbt` before broadcasting.
1112#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1113pub struct WalletCreateFundedPsbt {
1114    /// The funded PSBT with inputs selected by the wallet.
1115    ///
1116    /// Contains the unsigned transaction structure with all necessary
1117    /// input and output information for subsequent signing operations.
1118    #[serde(deserialize_with = "deserialize_psbt")]
1119    pub psbt: Psbt,
1120
1121    /// The fee amount in BTC paid by this transaction.
1122    ///
1123    /// Represents the total fee calculated based on the selected inputs,
1124    /// outputs, and the specified fee rate or confirmation target.
1125    #[serde(deserialize_with = "deserialize_bitcoin")]
1126    pub fee: Amount,
1127
1128    /// The position of the change output in the transaction outputs array.
1129    ///
1130    /// If no change output was created (exact amount match), this will be -1.
1131    /// Otherwise, indicates the zero-based index of the change output.
1132    #[serde(rename = "changepos")]
1133    pub change_pos: i32,
1134}
1135
1136/// Result of the `walletprocesspsbt` and `finalizepsbt` RPC methods.
1137///
1138/// Contains the processed PSBT state, completion status, and optionally the
1139/// extracted final transaction. This struct handles the Bitcoin Core's PSBT
1140/// workflow where PSBTs can be incrementally signed and eventually finalized.
1141///
1142/// # Note
1143///
1144/// The `psbt` field contains the updated PSBT after processing, while `hex`
1145/// contains the final transaction only when `complete` is `true` and extraction
1146/// is requested. Both fields may be `None` depending on the operation context.
1147#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1148pub struct WalletProcessPsbtResult {
1149    /// The processed Partially Signed Bitcoin Transaction.
1150    ///
1151    /// Contains the PSBT after wallet processing with any signatures or input data
1152    /// that could be added. Will be `None` if the transaction was fully extracted
1153    /// and the PSBT is no longer needed.
1154    #[serde(deserialize_with = "deserialize_option_psbt")]
1155    pub psbt: Option<Psbt>,
1156
1157    /// Whether the transaction is complete and ready for broadcast.
1158    ///
1159    /// `true` indicates all required signatures have been collected and the
1160    /// transaction can be finalized. `false` means more signatures are needed
1161    /// before the transaction can be broadcast to the network.
1162    pub complete: bool,
1163
1164    /// The final transaction ready for broadcast (when complete).
1165    ///
1166    /// Contains the fully signed and finalized transaction when `complete` is `true`
1167    /// and extraction was requested. Will be `None` for incomplete transactions or
1168    /// when extraction is not performed.
1169    #[serde(
1170        deserialize_with = "deserialize_option_tx",
1171        skip_serializing_if = "Option::is_none",
1172        default
1173    )]
1174    pub hex: Option<Transaction>,
1175}
1176
1177/// Result of the `getaddressinfo` RPC method.
1178///
1179/// Provides detailed information about a Bitcoin address, including ownership
1180/// status, watching capabilities, and spending permissions within the wallet.
1181///
1182/// # Note
1183///
1184/// Optional fields may be `None` if the wallet doesn't have specific information
1185/// about the address or if the address is not related to the wallet.
1186#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1187pub struct GetAddressInfo {
1188    /// The Bitcoin address that was queried.
1189    ///
1190    /// Returns the same address that was provided as input to `getaddressinfo`,
1191    /// validated and parsed into the proper Address type.
1192    #[serde(deserialize_with = "deserialize_address")]
1193    pub address: Address<NetworkUnchecked>,
1194
1195    /// Whether the address belongs to the wallet (can receive payments to it).
1196    ///
1197    /// `true` if the wallet owns the private key or can generate signatures for this address.
1198    /// `false` if the address is not owned by the wallet. `None` if ownership status is unknown.
1199    #[serde(rename = "ismine")]
1200    pub is_mine: Option<bool>,
1201
1202    /// Whether the address is watch-only (monitored but not spendable).
1203    ///
1204    /// `true` if the wallet watches this address for incoming transactions but cannot
1205    /// spend from it (no private key). `false` if the address is fully controlled.
1206    /// `None` if watch status is not applicable.
1207    #[serde(rename = "iswatchonly")]
1208    pub is_watchonly: Option<bool>,
1209
1210    /// Whether the wallet knows how to spend coins sent to this address.
1211    ///
1212    /// `true` if the wallet has enough information (private keys, scripts) to create
1213    /// valid spending transactions from this address. `false` if the address cannot
1214    /// be spent by this wallet. `None` if spendability cannot be determined.
1215    pub solvable: Option<bool>,
1216}
1217
1218/// Query options for filtering unspent transaction outputs.
1219///
1220/// Used with `list_unspent` to apply additional filtering criteria
1221/// beyond confirmation counts and addresses, allowing precise UTXO selection
1222/// based on amount ranges and result limits.
1223///
1224/// # Note
1225///
1226/// All fields are optional and can be combined. UTXOs must satisfy all
1227/// specified criteria to be included in the results.
1228#[derive(Clone, Debug, PartialEq, Serialize)]
1229#[serde(rename_all = "camelCase")]
1230pub struct ListUnspentQueryOptions {
1231    /// Minimum amount that UTXOs must have to be included.
1232    ///
1233    /// Only unspent outputs with a value greater than or equal to this amount
1234    /// will be returned. Useful for filtering out dust or very small UTXOs.
1235    #[serde(serialize_with = "serialize_option_bitcoin")]
1236    pub minimum_amount: Option<Amount>,
1237
1238    /// Maximum amount that UTXOs can have to be included.
1239    ///
1240    /// Only unspent outputs with a value less than or equal to this amount
1241    /// will be returned. Useful for finding smaller UTXOs or avoiding large ones.
1242    #[serde(serialize_with = "serialize_option_bitcoin")]
1243    pub maximum_amount: Option<Amount>,
1244
1245    /// Maximum number of UTXOs to return in the result set.
1246    ///
1247    /// Limits the total number of unspent outputs returned, regardless of how many
1248    /// match the other criteria. Useful for pagination or limiting response size.
1249    pub maximum_count: Option<u32>,
1250}
1251
1252/// Options for psbtbumpfee RPC method.
1253#[derive(Clone, Debug, Default, PartialEq, Serialize)]
1254pub struct PsbtBumpFeeOptions {
1255    /// Confirmation target in blocks.
1256    #[serde(skip_serializing_if = "Option::is_none")]
1257    pub conf_target: Option<u16>,
1258
1259    /// Fee rate in sat/vB.
1260    #[serde(skip_serializing_if = "Option::is_none")]
1261    pub fee_rate: Option<FeeRate>,
1262
1263    /// Whether the new transaction should be BIP-125 replaceable.
1264    #[serde(skip_serializing_if = "Option::is_none")]
1265    pub replaceable: Option<bool>,
1266
1267    /// Fee estimate mode ("unset", "economical", "conservative").
1268    #[serde(skip_serializing_if = "Option::is_none")]
1269    pub estimate_mode: Option<String>,
1270
1271    /// New transaction outputs to replace the existing ones.
1272    #[serde(skip_serializing_if = "Option::is_none")]
1273    pub outputs: Option<Vec<CreateRawTransactionOutput>>,
1274
1275    /// Index of the change output to recycle from the original transaction.
1276    #[serde(skip_serializing_if = "Option::is_none")]
1277    pub original_change_index: Option<u32>,
1278}
1279
1280/// Result of the psbtbumpfee RPC method.
1281#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1282pub struct PsbtBumpFee {
1283    /// The base64-encoded unsigned PSBT of the new transaction.
1284    #[serde(deserialize_with = "deserialize_psbt")]
1285    pub psbt: Psbt,
1286
1287    /// The fee of the replaced transaction.
1288    #[serde(deserialize_with = "deserialize_feerate")]
1289    pub origfee: FeeRate,
1290
1291    /// The fee of the new transaction.
1292    #[serde(deserialize_with = "deserialize_feerate")]
1293    pub fee: FeeRate,
1294
1295    /// Errors encountered during processing (if any).
1296    pub errors: Option<Vec<String>>,
1297}
1298
1299#[cfg(test)]
1300mod tests {
1301    use super::*;
1302    use serde_json;
1303
1304    // Taken from https://docs.rs/bitcoin/0.32.6/src/bitcoin/psbt/mod.rs.html#1515-1520
1305    // BIP 174 test vector with inputs and outputs (more realistic than empty transaction)
1306    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";
1307
1308    // Valid Bitcoin transaction hex (Genesis block coinbase transaction)
1309    const TEST_TX_HEX: &str = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
1310
1311    #[test]
1312    fn test_wallet_process_psbt_result() {
1313        let valid_psbt = TEST_PSBT;
1314
1315        // Test complete with hex
1316        let test_tx_hex = TEST_TX_HEX;
1317        let json1 = format!(r#"{{"psbt":"{valid_psbt}","complete":true,"hex":"{test_tx_hex}"}}"#);
1318        let result1: WalletProcessPsbtResult = serde_json::from_str(&json1).unwrap();
1319        assert!(result1.psbt.is_some());
1320        assert!(result1.complete);
1321        assert!(result1.hex.is_some());
1322        let tx = result1.hex.unwrap();
1323        assert!(!tx.input.is_empty());
1324        assert!(!tx.output.is_empty());
1325
1326        // Test incomplete without hex
1327        let json2 = format!(r#"{{"psbt":"{valid_psbt}","complete":false}}"#);
1328        let result2: WalletProcessPsbtResult = serde_json::from_str(&json2).unwrap();
1329        assert!(result2.psbt.is_some());
1330        assert!(!result2.complete);
1331    }
1332
1333    #[test]
1334    fn test_sighashtype_serialize() {
1335        let sighash = SighashType::All;
1336        let serialized = serde_json::to_string(&sighash).unwrap();
1337        assert_eq!(serialized, "\"ALL\"");
1338
1339        let sighash2 = SighashType::AllPlusAnyoneCanPay;
1340        let serialized2 = serde_json::to_string(&sighash2).unwrap();
1341        assert_eq!(serialized2, "\"ALL|ANYONECANPAY\"");
1342    }
1343
1344    #[test]
1345    fn test_list_unspent_query_options_camelcase() {
1346        let options = ListUnspentQueryOptions {
1347            minimum_amount: Some(Amount::from_btc(0.5).unwrap()),
1348            maximum_amount: Some(Amount::from_btc(2.0).unwrap()),
1349            maximum_count: Some(10),
1350        };
1351        let serialized = serde_json::to_string(&options).unwrap();
1352
1353        assert!(serialized.contains("\"minimumAmount\":0.5"));
1354        assert!(serialized.contains("\"maximumAmount\":2.0"));
1355        assert!(serialized.contains("\"maximumCount\":10"));
1356    }
1357
1358    #[test]
1359    fn test_psbt_parsing() {
1360        // Test valid PSBT parsing
1361        let valid_psbt = TEST_PSBT;
1362        let json1 = format!(r#"{{"psbt":"{valid_psbt}","fee":0.001,"changepos":-1}}"#);
1363        let result1: WalletCreateFundedPsbt = serde_json::from_str(&json1).unwrap();
1364        assert!(!result1.psbt.inputs.is_empty()); // BIP 174 test vector has inputs
1365
1366        // Test invalid PSBT parsing fails
1367        let invalid_psbt = "invalid_base64";
1368        let json2 = format!(r#"{{"psbt":"{invalid_psbt}","fee":0.001,"changepos":-1}}"#);
1369        let result2 = serde_json::from_str::<WalletCreateFundedPsbt>(&json2);
1370        assert!(result2.is_err());
1371    }
1372}