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/// Response from `getrawmempool` with `verbose=true`.
200///
201/// The top-level map key is the txid, and the value contains detailed mempool info per tx.
202pub 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/// Result of JSON-RPC method `getrawtransaction` with verbosity set to 1.
237///
238/// Method call: `getrawtransaction "txid" ( verbosity )`
239#[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/// Result of JSON-RPC method `gettxout`.
258///
259/// > gettxout "txid" n ( include_mempool )
260/// >
261/// > Returns details about an unspent transaction output.
262/// >
263/// > Arguments:
264/// > 1. txid               (string, required) The transaction id
265/// > 2. n                  (numeric, required) vout number
266#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
267pub struct GetTxOut {
268    /// The hash of the block at the tip of the chain.
269    #[serde(rename = "bestblock")]
270    pub best_block: String,
271    /// The number of confirmations.
272    pub confirmations: u32, // TODO: Change this to an i64.
273    /// The transaction value in BTC.
274    pub value: f64,
275    /// The script pubkey.
276    #[serde(rename = "scriptPubkey")]
277    pub script_pubkey: Option<ScriptPubkey>,
278    /// Coinbase or not.
279    pub coinbase: bool,
280}
281
282/// A script pubkey.
283#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
284pub struct ScriptPubkey {
285    /// Script assembly.
286    pub asm: String,
287    /// Script hex.
288    pub hex: String,
289    #[serde(rename = "reqSigs")]
290    pub req_sigs: i64,
291    /// The type, eg pubkeyhash.
292    #[serde(rename = "type")]
293    pub type_: String,
294    /// Bitcoin address.
295    pub address: Option<String>,
296}
297
298/// Models the arguments of JSON-RPC method `createrawtransaction`.
299///
300/// # Note
301///
302/// Assumes that the transaction is always "replaceable" by default and has a locktime of 0.
303#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
304pub struct CreateRawTransaction {
305    pub inputs: Vec<CreateRawTransactionInput>,
306    pub outputs: Vec<CreateRawTransactionOutput>,
307}
308
309/// Models the input of JSON-RPC method `createrawtransaction`.
310#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
311pub struct CreateRawTransactionInput {
312    pub txid: String,
313    pub vout: u32,
314}
315
316/// Models transaction outputs for Bitcoin RPC methods.
317///
318/// Used by various RPC methods such as `createrawtransaction`, `psbtbumpfee`,
319/// and `walletcreatefundedpsbt`. The outputs are specified as key-value pairs,
320/// where the keys are addresses and the values are amounts to send.
321#[derive(Clone, Debug, PartialEq, Deserialize)]
322#[serde(untagged)]
323pub enum CreateRawTransactionOutput {
324    /// A pair of an [`Address`] string and an [`Amount`] in BTC.
325    AddressAmount {
326        /// An [`Address`] string.
327        address: String,
328        /// An [`Amount`] in BTC.
329        amount: f64,
330    },
331    /// A payload such as in `OP_RETURN` transactions.
332    Data {
333        /// The payload.
334        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/// Result of JSON-RPC method `submitpackage`.
362///
363/// > submitpackage ["rawtx",...] ( maxfeerate maxburnamount )
364/// >
365/// > Submit a package of raw transactions (serialized, hex-encoded) to local node.
366/// > The package will be validated according to consensus and mempool policy rules. If any
367/// > transaction passes, it will be accepted to mempool.
368/// > This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md
369/// > for documentation on package policies.
370/// > Warning: successful submission does not mean the transactions will propagate throughout the
371/// > network.
372/// >
373/// > Arguments:
374/// > 1. package          (json array, required) An array of raw transactions.
375/// > The package must solely consist of a child and its parents. None of the parents may depend on
376/// > each other.
377/// > The package must be topologically sorted, with the child being the last element in the array.
378/// > [
379/// > "rawtx",     (string)
380/// > ...
381/// > ]
382#[allow(clippy::doc_lazy_continuation)]
383#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
384pub struct SubmitPackage {
385    /// The transaction package result message.
386    ///
387    /// "success" indicates all transactions were accepted into or are already in the mempool.
388    pub package_msg: String,
389    /// Transaction results keyed by wtxid.
390    #[serde(rename = "tx-results")]
391    pub tx_results: BTreeMap<String, SubmitPackageTxResult>,
392    /// List of txids of replaced transactions.
393    #[serde(rename = "replaced-transactions")]
394    pub replaced_transactions: Vec<String>,
395}
396
397/// Models the per-transaction result included in the JSON-RPC method `submitpackage`.
398#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
399pub struct SubmitPackageTxResult {
400    /// The transaction id.
401    pub txid: String,
402    /// The wtxid of a different transaction with the same txid but different witness found in the
403    /// mempool.
404    ///
405    /// If set, this means the submitted transaction was ignored.
406    #[serde(rename = "other-wtxid")]
407    pub other_wtxid: Option<String>,
408    /// Sigops-adjusted virtual transaction size.
409    pub vsize: i64,
410    /// Transaction fees.
411    pub fees: Option<SubmitPackageTxResultFees>,
412    /// The transaction error string, if it was rejected by the mempool
413    pub error: Option<String>,
414}
415
416/// Models the fees included in the per-transaction result of the JSON-RPC method `submitpackage`.
417#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
418pub struct SubmitPackageTxResultFees {
419    /// Transaction fee.
420    #[serde(rename = "base")]
421    pub base_fee: f64,
422    /// The effective feerate.
423    ///
424    /// Will be `None` if the transaction was already in the mempool. For example, the package
425    /// feerate and/or feerate with modified fees from the `prioritisetransaction` JSON-RPC method.
426    #[serde(rename = "effective-feerate")]
427    pub effective_fee_rate: Option<f64>,
428    /// If [`Self::effective_fee_rate`] is provided, this holds the wtxid's of the transactions
429    /// whose fees and vsizes are included in effective-feerate.
430    #[serde(rename = "effective-includes")]
431    pub effective_includes: Option<Vec<String>>,
432}
433
434/// Result of JSON-RPC method `gettxout`.
435///
436/// # Note
437///
438/// This assumes that the UTXOs are present in the underlying Bitcoin
439/// client's wallet.
440#[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/// Enum to represent the category of a transaction.
452#[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/// Result of the JSON-RPC method `getnewaddress`.
463///
464/// # Note
465///
466/// This assumes that the UTXOs are present in the underlying Bitcoin
467/// client's wallet.
468#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
469pub struct GetNewAddress(pub String);
470
471impl GetNewAddress {
472    /// Converts json straight to a [`Address`].
473    pub fn address(self) -> Result<Address<NetworkUnchecked>, address::ParseError> {
474        let address = self.0.parse::<Address<_>>()?;
475        Ok(address)
476    }
477}
478
479/// Models the result of JSON-RPC method `listunspent`.
480///
481/// # Note
482///
483/// This assumes that the UTXOs are present in the underlying Bitcoin
484/// client's wallet.
485///
486/// Careful with the amount field. It is a [`SignedAmount`], hence can be negative.
487/// Negative amounts for the [`TransactionCategory::Send`], and is positive
488/// for all other categories.
489#[derive(Clone, Debug, PartialEq, Deserialize)]
490pub struct GetTransaction {
491    /// The signed amount in BTC.
492    #[serde(deserialize_with = "deserialize_signed_bitcoin")]
493    pub amount: SignedAmount,
494    /// The signed fee in BTC.
495    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    /// The transaction id.
503    #[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    /// The transaction itself.
517    #[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/// Models the result of JSON-RPC method `listunspent`.
534///
535/// # Note
536///
537/// This assumes that the UTXOs are present in the underlying Bitcoin
538/// client's wallet.
539#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
540pub struct ListUnspent {
541    /// The transaction id.
542    #[serde(deserialize_with = "deserialize_txid")]
543    pub txid: Txid,
544    /// The vout value.
545    pub vout: u32,
546    /// The Bitcoin address.
547    #[serde(deserialize_with = "deserialize_address")]
548    pub address: Address<NetworkUnchecked>,
549    // The associated label, if any.
550    pub label: Option<String>,
551    /// The script pubkey.
552    #[serde(rename = "scriptPubKey")]
553    pub script_pubkey: String,
554    /// The transaction output amount in BTC.
555    #[serde(deserialize_with = "deserialize_bitcoin")]
556    pub amount: Amount,
557    /// The number of confirmations.
558    pub confirmations: u32,
559    /// Whether we have the private keys to spend this output.
560    pub spendable: bool,
561    /// Whether we know how to spend this output, ignoring the lack of keys.
562    pub solvable: bool,
563    /// Whether this output is considered safe to spend.
564    /// Unconfirmed transactions from outside keys and unconfirmed replacement
565    /// transactions are considered unsafe and are not eligible for spending by
566    /// `fundrawtransaction` and `sendtoaddress`.
567    pub safe: bool,
568}
569
570/// Models the result of JSON-RPC method `listtransactions`.
571///
572/// # Note
573///
574/// This assumes that the transactions are present in the underlying Bitcoin
575/// client's wallet.
576///
577/// Careful with the amount field. It is a [`SignedAmount`], hence can be negative.
578/// Negative amounts for the [`TransactionCategory::Send`], and is positive
579/// for all other categories.
580#[derive(Clone, Debug, PartialEq, Deserialize)]
581pub struct ListTransactions {
582    /// The Bitcoin address.
583    #[serde(deserialize_with = "deserialize_address")]
584    pub address: Address<NetworkUnchecked>,
585    /// Category of the transaction.
586    category: TransactionCategory,
587    /// The signed amount in BTC.
588    #[serde(deserialize_with = "deserialize_signed_bitcoin")]
589    pub amount: SignedAmount,
590    /// The label associated with the address, if any.
591    pub label: Option<String>,
592    /// The number of confirmations.
593    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    /// The transaction id.
601    #[serde(deserialize_with = "deserialize_txid")]
602    pub txid: Txid,
603}
604
605/// Models the result of JSON-RPC method `testmempoolaccept`.
606#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
607pub struct TestMempoolAccept {
608    /// The transaction id.
609    #[serde(deserialize_with = "deserialize_txid")]
610    pub txid: Txid,
611    /// Rejection reason, if any.
612    pub reject_reason: Option<String>,
613}
614
615/// Models the result of JSON-RPC method `signrawtransactionwithwallet`.
616///
617/// # Note
618///
619/// This assumes that the transactions are present in the underlying Bitcoin
620/// client's wallet.
621#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
622pub struct SignRawTransactionWithWallet {
623    /// The Transaction ID.
624    pub hex: String,
625    /// If the transaction has a complete set of signatures.
626    pub complete: bool,
627    /// Errors, if any.
628    pub errors: Option<Vec<SignRawTransactionWithWalletError>>,
629}
630
631/// Models the optional previous transaction outputs argument for the method
632/// `signrawtransactionwithwallet`.
633///
634/// These are the outputs that this transaction depends on but may not yet be in the block chain.
635/// Widely used for One Parent One Child (1P1C) Relay in Bitcoin >28.0.
636///
637/// > transaction outputs
638/// > [
639/// > {                            (json object)
640/// > "txid": "hex",             (string, required) The transaction id
641/// > "vout": n,                 (numeric, required) The output number
642/// > "scriptPubKey": "hex",     (string, required) The output script
643/// > "redeemScript": "hex",     (string, optional) (required for P2SH) redeem script
644/// > "witnessScript": "hex",    (string, optional) (required for P2WSH or P2SH-P2WSH) witness
645/// > script
646/// > "amount": amount,          (numeric or string, optional) (required for Segwit inputs) the
647/// > amount spent
648/// > },
649/// > ...
650/// > ]
651#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
652pub struct PreviousTransactionOutput {
653    /// The transaction id.
654    #[serde(deserialize_with = "deserialize_txid")]
655    pub txid: Txid,
656    /// The output number.
657    pub vout: u32,
658    /// The output script.
659    #[serde(rename = "scriptPubKey")]
660    pub script_pubkey: String,
661    /// The redeem script.
662    #[serde(rename = "redeemScript")]
663    pub redeem_script: Option<String>,
664    /// The witness script.
665    #[serde(rename = "witnessScript")]
666    pub witness_script: Option<String>,
667    /// The amount spent.
668    pub amount: Option<f64>,
669}
670
671/// Models the result of the JSON-RPC method `listdescriptors`.
672#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
673pub struct ListDescriptors {
674    /// The descriptors
675    pub descriptors: Vec<ListDescriptor>,
676}
677
678/// Models the Descriptor in the result of the JSON-RPC method `listdescriptors`.
679#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
680pub struct ListDescriptor {
681    /// The descriptor.
682    pub desc: String,
683}
684
685/// Models the result of the JSON-RPC method `importdescriptors`.
686#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
687pub struct ImportDescriptors {
688    /// The descriptors
689    pub descriptors: Vec<ListDescriptor>,
690}
691
692/// Models the Descriptor in the result of the JSON-RPC method `importdescriptors`.
693#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
694pub struct ImportDescriptor {
695    /// The descriptor.
696    pub desc: String,
697    /// Set this descriptor to be the active descriptor
698    /// for the corresponding output type/externality.
699    pub active: Option<bool>,
700    /// Time from which to start rescanning the blockchain for this descriptor,
701    /// in UNIX epoch time. Can also be a string "now"
702    pub timestamp: String,
703}
704/// Models the Descriptor in the result of the JSON-RPC method `importdescriptors`.
705#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
706pub struct ImportDescriptorResult {
707    /// Result.
708    pub success: bool,
709}
710
711/// Models the `createwallet` JSON-RPC method.
712///
713/// # Note
714///
715/// This can also be used for the `loadwallet` JSON-RPC method.
716#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
717pub struct CreateWallet {
718    /// Wallet name
719    pub wallet_name: String,
720    /// Load on startup
721    pub load_on_startup: Option<bool>,
722}
723
724/// Deserializes the amount in BTC into proper [`Amount`]s.
725fn 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
749/// Serializes the optional [`Amount`] into BTC.
750fn 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
760/// Deserializes the fee rate from sat/vB into proper [`FeeRate`].
761///
762/// Note: Bitcoin Core 0.21+ uses sat/vB for fee rates for most RPC methods/results.
763fn 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            // The value is already in sat/vB (Bitcoin Core 0.21+)
784            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
793/// Deserializes the *signed* amount in BTC into proper [`SignedAmount`]s.
794fn 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/// Deserializes the *signed* amount in BTC into proper [`SignedAmount`]s.
819#[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
833/// Deserializes the transaction id string into proper [`Txid`]s.
834fn 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
859/// Deserializes the transaction hex string into proper [`Transaction`]s.
860fn 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
885/// Deserializes a base64-encoded PSBT string into proper [`Psbt`]s.
886///
887/// # Note
888///
889/// Expects a valid base64-encoded PSBT as defined in BIP 174. The PSBT
890/// string must contain valid transaction data and metadata for successful parsing.
891fn 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
915/// Deserializes an optional base64-encoded PSBT string into `Option<Psbt>`.
916///
917/// # Note
918///
919/// When the JSON field is `null` or missing, returns `None`. When present,
920/// deserializes the base64 PSBT string using the same validation as [`deserialize_psbt`].
921fn 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
948/// Deserializes the address string into proper [`Address`]s.
949///
950/// # Note
951///
952/// The user is responsible for ensuring that the address is valid,
953/// since this functions returns an [`Address<NetworkUnchecked>`].
954fn 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/// Deserializes the blockhash string into proper [`BlockHash`]s.
978#[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/// Deserializes the height string into proper [`Height`]s.
1005#[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/// Signature hash types for Bitcoin transactions.
1031///
1032/// These types specify which parts of a transaction are included in the signature
1033/// hash calculation when signing transaction inputs. Used with wallet signing
1034/// operations like `wallet_process_psbt`.
1035///
1036/// # Note
1037///
1038/// These correspond to the SIGHASH flags defined in Bitcoin's script system
1039/// and BIP 143 (witness transaction digest).
1040#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1041#[serde(rename_all = "UPPERCASE")]
1042pub enum SighashType {
1043    /// Use the default signature hash type (equivalent to SIGHASH_ALL).
1044    Default,
1045
1046    /// Sign all inputs and all outputs of the transaction.
1047    ///
1048    /// This is the most common and secure signature type, ensuring the entire
1049    /// transaction structure cannot be modified after signing.
1050    All,
1051
1052    /// Sign all inputs but no outputs.
1053    ///
1054    /// Allows outputs to be modified after signing, useful for donation scenarios
1055    /// where the exact destination amounts can be adjusted.
1056    None,
1057
1058    /// Sign all inputs and the output with the same index as this input.
1059    ///
1060    /// Used in scenarios where multiple parties contribute inputs and want to
1061    /// ensure their corresponding output is protected.
1062    Single,
1063
1064    /// Combination of SIGHASH_ALL with ANYONECANPAY flag.
1065    ///
1066    /// Signs all outputs but only this specific input, allowing other inputs
1067    /// to be added or removed. Useful for crowdfunding transactions.
1068    #[serde(rename = "ALL|ANYONECANPAY")]
1069    AllPlusAnyoneCanPay,
1070
1071    /// Combination of SIGHASH_NONE with ANYONECANPAY flag.
1072    ///
1073    /// Signs only this specific input with no outputs committed, providing
1074    /// maximum flexibility for transaction modification.
1075    #[serde(rename = "NONE|ANYONECANPAY")]
1076    NonePlusAnyoneCanPay,
1077
1078    /// Combination of SIGHASH_SINGLE with ANYONECANPAY flag.
1079    ///
1080    /// Signs only this input and its corresponding output, allowing other
1081    /// inputs and outputs to be modified independently.
1082    #[serde(rename = "SINGLE|ANYONECANPAY")]
1083    SinglePlusAnyoneCanPay,
1084}
1085
1086/// Options for creating a funded PSBT with wallet inputs.
1087///
1088/// Used with `wallet_create_funded_psbt` to control funding behavior,
1089/// fee estimation, and transaction policies when the wallet automatically
1090/// selects inputs to fund the specified outputs.
1091///
1092/// # Note
1093///
1094/// All fields are optional and will use Bitcoin Core defaults if not specified.
1095/// Fee rate takes precedence over confirmation target if both are provided.
1096#[derive(Clone, Debug, PartialEq, Serialize, Default)]
1097pub struct WalletCreateFundedPsbtOptions {
1098    /// Fee rate in sat/vB (satoshis per virtual byte) for the transaction.
1099    ///
1100    /// If specified, this overrides the `conf_target` parameter for fee estimation.
1101    /// Must be a positive value representing the desired fee density.
1102    #[serde(default, rename = "fee_rate", skip_serializing_if = "Option::is_none")]
1103    pub fee_rate: Option<f64>,
1104
1105    /// Whether to lock the selected UTXOs to prevent them from being spent by other transactions.
1106    ///
1107    /// When `true`, the wallet will temporarily lock the selected unspent outputs
1108    /// until the transaction is broadcast or manually unlocked. Default is `false`.
1109    #[serde(
1110        default,
1111        rename = "lockUnspents",
1112        skip_serializing_if = "Option::is_none"
1113    )]
1114    pub lock_unspents: Option<bool>,
1115
1116    /// Target number of confirmations for automatic fee estimation.
1117    ///
1118    /// Represents the desired number of blocks within which the transaction should
1119    /// be confirmed. Higher values result in lower fees but longer confirmation times.
1120    /// Ignored if `fee_rate` is specified.
1121    #[serde(
1122        default,
1123        rename = "conf_target",
1124        skip_serializing_if = "Option::is_none"
1125    )]
1126    pub conf_target: Option<u16>,
1127
1128    /// Whether the transaction should be BIP-125 opt-in Replace-By-Fee (RBF) enabled.
1129    ///
1130    /// When `true`, allows the transaction to be replaced with a higher-fee version
1131    /// before confirmation. Useful for fee bumping if the initial fee proves insufficient.
1132    #[serde(
1133        default,
1134        rename = "replaceable",
1135        skip_serializing_if = "Option::is_none"
1136    )]
1137    pub replaceable: Option<bool>,
1138}
1139
1140/// Result of the `walletcreatefundedpsbt` RPC method.
1141///
1142/// Contains a funded PSBT created by the wallet with automatically selected inputs
1143/// to cover the specified outputs, along with fee information and change output details.
1144///
1145/// # Note
1146///
1147/// The PSBT returned is not signed and requires further processing with
1148/// `wallet_process_psbt` or `finalize_psbt` before broadcasting.
1149#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1150pub struct WalletCreateFundedPsbt {
1151    /// The funded PSBT with inputs selected by the wallet.
1152    ///
1153    /// Contains the unsigned transaction structure with all necessary
1154    /// input and output information for subsequent signing operations.
1155    #[serde(deserialize_with = "deserialize_psbt")]
1156    pub psbt: Psbt,
1157
1158    /// The fee amount in BTC paid by this transaction.
1159    ///
1160    /// Represents the total fee calculated based on the selected inputs,
1161    /// outputs, and the specified fee rate or confirmation target.
1162    #[serde(deserialize_with = "deserialize_bitcoin")]
1163    pub fee: Amount,
1164
1165    /// The position of the change output in the transaction outputs array.
1166    ///
1167    /// If no change output was created (exact amount match), this will be -1.
1168    /// Otherwise, indicates the zero-based index of the change output.
1169    #[serde(rename = "changepos")]
1170    pub change_pos: i32,
1171}
1172
1173/// Result of the `walletprocesspsbt` and `finalizepsbt` RPC methods.
1174///
1175/// Contains the processed PSBT state, completion status, and optionally the
1176/// extracted final transaction. This struct handles the Bitcoin Core's PSBT
1177/// workflow where PSBTs can be incrementally signed and eventually finalized.
1178///
1179/// # Note
1180///
1181/// The `psbt` field contains the updated PSBT after processing, while `hex`
1182/// contains the final transaction only when `complete` is `true` and extraction
1183/// is requested. Both fields may be `None` depending on the operation context.
1184#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1185pub struct WalletProcessPsbtResult {
1186    /// The processed Partially Signed Bitcoin Transaction.
1187    ///
1188    /// Contains the PSBT after wallet processing with any signatures or input data
1189    /// that could be added. Will be `None` if the transaction was fully extracted
1190    /// and the PSBT is no longer needed.
1191    #[serde(deserialize_with = "deserialize_option_psbt")]
1192    pub psbt: Option<Psbt>,
1193
1194    /// Whether the transaction is complete and ready for broadcast.
1195    ///
1196    /// `true` indicates all required signatures have been collected and the
1197    /// transaction can be finalized. `false` means more signatures are needed
1198    /// before the transaction can be broadcast to the network.
1199    pub complete: bool,
1200
1201    /// The final transaction ready for broadcast (when complete).
1202    ///
1203    /// Contains the fully signed and finalized transaction when `complete` is `true`
1204    /// and extraction was requested. Will be `None` for incomplete transactions or
1205    /// when extraction is not performed.
1206    #[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/// Result of the `getaddressinfo` RPC method.
1215///
1216/// Provides detailed information about a Bitcoin address, including ownership
1217/// status, watching capabilities, and spending permissions within the wallet.
1218///
1219/// # Note
1220///
1221/// Optional fields may be `None` if the wallet doesn't have specific information
1222/// about the address or if the address is not related to the wallet.
1223#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1224pub struct GetAddressInfo {
1225    /// The Bitcoin address that was queried.
1226    ///
1227    /// Returns the same address that was provided as input to `getaddressinfo`,
1228    /// validated and parsed into the proper Address type.
1229    #[serde(deserialize_with = "deserialize_address")]
1230    pub address: Address<NetworkUnchecked>,
1231
1232    /// Whether the address belongs to the wallet (can receive payments to it).
1233    ///
1234    /// `true` if the wallet owns the private key or can generate signatures for this address.
1235    /// `false` if the address is not owned by the wallet. `None` if ownership status is unknown.
1236    #[serde(rename = "ismine")]
1237    pub is_mine: Option<bool>,
1238
1239    /// Whether the address is watch-only (monitored but not spendable).
1240    ///
1241    /// `true` if the wallet watches this address for incoming transactions but cannot
1242    /// spend from it (no private key). `false` if the address is fully controlled.
1243    /// `None` if watch status is not applicable.
1244    #[serde(rename = "iswatchonly")]
1245    pub is_watchonly: Option<bool>,
1246
1247    /// Whether the wallet knows how to spend coins sent to this address.
1248    ///
1249    /// `true` if the wallet has enough information (private keys, scripts) to create
1250    /// valid spending transactions from this address. `false` if the address cannot
1251    /// be spent by this wallet. `None` if spendability cannot be determined.
1252    pub solvable: Option<bool>,
1253}
1254
1255/// Query options for filtering unspent transaction outputs.
1256///
1257/// Used with `list_unspent` to apply additional filtering criteria
1258/// beyond confirmation counts and addresses, allowing precise UTXO selection
1259/// based on amount ranges and result limits.
1260///
1261/// # Note
1262///
1263/// All fields are optional and can be combined. UTXOs must satisfy all
1264/// specified criteria to be included in the results.
1265#[derive(Clone, Debug, PartialEq, Serialize)]
1266#[serde(rename_all = "camelCase")]
1267pub struct ListUnspentQueryOptions {
1268    /// Minimum amount that UTXOs must have to be included.
1269    ///
1270    /// Only unspent outputs with a value greater than or equal to this amount
1271    /// will be returned. Useful for filtering out dust or very small UTXOs.
1272    #[serde(serialize_with = "serialize_option_bitcoin")]
1273    pub minimum_amount: Option<Amount>,
1274
1275    /// Maximum amount that UTXOs can have to be included.
1276    ///
1277    /// Only unspent outputs with a value less than or equal to this amount
1278    /// will be returned. Useful for finding smaller UTXOs or avoiding large ones.
1279    #[serde(serialize_with = "serialize_option_bitcoin")]
1280    pub maximum_amount: Option<Amount>,
1281
1282    /// Maximum number of UTXOs to return in the result set.
1283    ///
1284    /// Limits the total number of unspent outputs returned, regardless of how many
1285    /// match the other criteria. Useful for pagination or limiting response size.
1286    pub maximum_count: Option<u32>,
1287}
1288
1289/// Options for psbtbumpfee RPC method.
1290#[derive(Clone, Debug, Default, PartialEq, Serialize)]
1291pub struct PsbtBumpFeeOptions {
1292    /// Confirmation target in blocks.
1293    #[serde(skip_serializing_if = "Option::is_none")]
1294    pub conf_target: Option<u16>,
1295
1296    /// Fee rate in sat/vB.
1297    #[serde(skip_serializing_if = "Option::is_none")]
1298    pub fee_rate: Option<FeeRate>,
1299
1300    /// Whether the new transaction should be BIP-125 replaceable.
1301    #[serde(skip_serializing_if = "Option::is_none")]
1302    pub replaceable: Option<bool>,
1303
1304    /// Fee estimate mode ("unset", "economical", "conservative").
1305    #[serde(skip_serializing_if = "Option::is_none")]
1306    pub estimate_mode: Option<String>,
1307
1308    /// New transaction outputs to replace the existing ones.
1309    #[serde(skip_serializing_if = "Option::is_none")]
1310    pub outputs: Option<Vec<CreateRawTransactionOutput>>,
1311
1312    /// Index of the change output to recycle from the original transaction.
1313    #[serde(skip_serializing_if = "Option::is_none")]
1314    pub original_change_index: Option<u32>,
1315}
1316
1317/// Result of the psbtbumpfee RPC method.
1318#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1319pub struct PsbtBumpFee {
1320    /// The base64-encoded unsigned PSBT of the new transaction.
1321    #[serde(deserialize_with = "deserialize_psbt")]
1322    pub psbt: Psbt,
1323
1324    /// The fee of the replaced transaction.
1325    #[serde(deserialize_with = "deserialize_feerate")]
1326    pub origfee: FeeRate,
1327
1328    /// The fee of the new transaction.
1329    #[serde(deserialize_with = "deserialize_feerate")]
1330    pub fee: FeeRate,
1331
1332    /// Errors encountered during processing (if any).
1333    pub errors: Option<Vec<String>>,
1334}
1335
1336#[cfg(test)]
1337mod tests {
1338    use super::*;
1339    use serde_json;
1340
1341    // Taken from https://docs.rs/bitcoin/0.32.6/src/bitcoin/psbt/mod.rs.html#1515-1520
1342    // BIP 174 test vector with inputs and outputs (more realistic than empty transaction)
1343    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    // Valid Bitcoin transaction hex (Genesis block coinbase transaction)
1346    const TEST_TX_HEX: &str = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000";
1347
1348    #[test]
1349    fn test_wallet_process_psbt_result() {
1350        let valid_psbt = TEST_PSBT;
1351
1352        // Test complete with hex
1353        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        // Test incomplete without hex
1364        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        // Test valid PSBT parsing
1398        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()); // BIP 174 test vector has inputs
1402
1403        // Test invalid PSBT parsing fails
1404        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}