Skip to content

Commit f3fb2a6

Browse files
committed
feat: expose detailed try_finalize_psbt outcomes
1 parent fb7681a commit f3fb2a6

1 file changed

Lines changed: 74 additions & 5 deletions

File tree

src/wallet/mod.rs

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,55 @@ impl fmt::Display for AddressInfo {
184184
/// A `CanonicalTx` managed by a `Wallet`.
185185
pub type WalletTx<'a> = CanonicalTx<'a, Arc<Transaction>, ConfirmationBlockTime>;
186186

187+
/// The finalization status for a single PSBT input.
188+
#[derive(Debug, PartialEq)]
189+
pub enum FinalizeInputOutcome {
190+
/// The input was already finalized before this call.
191+
AlreadyFinalized,
192+
/// The input was successfully finalized during this call.
193+
Finalized,
194+
/// The wallet could not derive a descriptor for the input.
195+
MissingDescriptor,
196+
/// The wallet found the descriptor but could not construct the input satisfaction.
197+
CouldNotSatisfy(miniscript::Error),
198+
}
199+
200+
impl FinalizeInputOutcome {
201+
/// Whether the input is finalized after this call.
202+
pub fn is_finalized(&self) -> bool {
203+
matches!(self, Self::AlreadyFinalized | Self::Finalized)
204+
}
205+
}
206+
207+
/// Holds per-input PSBT finalization outcomes.
208+
#[derive(Debug, PartialEq)]
209+
pub struct FinalizedInputs {
210+
outcomes: BTreeMap<usize, FinalizeInputOutcome>,
211+
}
212+
213+
impl FinalizedInputs {
214+
fn new(outcomes: BTreeMap<usize, FinalizeInputOutcome>) -> Self {
215+
Self { outcomes }
216+
}
217+
218+
/// Whether all inputs are finalized after this call.
219+
pub fn is_finalized(&self) -> bool {
220+
self.outcomes
221+
.values()
222+
.all(FinalizeInputOutcome::is_finalized)
223+
}
224+
225+
/// Borrow the per-input finalization outcomes.
226+
pub fn outcomes(&self) -> &BTreeMap<usize, FinalizeInputOutcome> {
227+
&self.outcomes
228+
}
229+
230+
/// Consume the collection and return the per-input finalization outcomes.
231+
pub fn into_outcomes(self) -> BTreeMap<usize, FinalizeInputOutcome> {
232+
self.outcomes
233+
}
234+
}
235+
187236
impl Wallet {
188237
/// Build a new single descriptor [`Wallet`].
189238
///
@@ -1842,6 +1891,19 @@ impl Wallet {
18421891
psbt: &mut Psbt,
18431892
sign_options: SignOptions,
18441893
) -> Result<bool, SignerError> {
1894+
Ok(self.try_finalize_psbt(psbt, sign_options)?.is_finalized())
1895+
}
1896+
1897+
/// Finalize a PSBT and return per-input finalization results. Use this method when you need to
1898+
/// inspect why a specific input could not be finalized.
1899+
///
1900+
/// The method should only return `Err` when the PSBT is malformed, for example if its inputs
1901+
/// are out of bounds.
1902+
pub fn try_finalize_psbt(
1903+
&self,
1904+
psbt: &mut Psbt,
1905+
sign_options: SignOptions,
1906+
) -> Result<FinalizedInputs, IndexOutOfBoundsError> {
18451907
let tx = &psbt.unsigned_tx;
18461908
let chain_tip = self.chain.tip().block_id();
18471909
let prev_txids = tx
@@ -1867,14 +1929,15 @@ impl Wallet {
18671929
})
18681930
.collect::<HashMap<Txid, u32>>();
18691931

1870-
let mut finished = true;
1932+
let mut outcomes = BTreeMap::new();
18711933

18721934
for (n, input) in tx.input.iter().enumerate() {
18731935
let psbt_input = &psbt
18741936
.inputs
18751937
.get(n)
18761938
.ok_or(IndexOutOfBoundsError::new(n, psbt.inputs.len()))?;
18771939
if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
1940+
outcomes.insert(n, FinalizeInputOutcome::AlreadyFinalized);
18781941
continue;
18791942
}
18801943
let confirmation_height = confirmation_heights
@@ -1927,23 +1990,29 @@ impl Wallet {
19271990
if !tmp_input.witness.is_empty() {
19281991
psbt_input.final_script_witness = Some(tmp_input.witness);
19291992
}
1993+
outcomes.insert(n, FinalizeInputOutcome::Finalized);
1994+
}
1995+
Err(err) => {
1996+
outcomes.insert(n, FinalizeInputOutcome::CouldNotSatisfy(err));
19301997
}
1931-
Err(_) => finished = false,
19321998
}
19331999
}
1934-
None => finished = false,
2000+
None => {
2001+
outcomes.insert(n, FinalizeInputOutcome::MissingDescriptor);
2002+
}
19352003
}
19362004
}
19372005

19382006
// Clear derivation paths from outputs.
1939-
if finished {
2007+
let finalized = FinalizedInputs::new(outcomes);
2008+
if finalized.is_finalized() {
19402009
for output in &mut psbt.outputs {
19412010
output.bip32_derivation.clear();
19422011
output.tap_key_origins.clear();
19432012
}
19442013
}
19452014

1946-
Ok(finished)
2015+
Ok(finalized)
19472016
}
19482017

19492018
/// Return the secp256k1 context used for all signing operations.

0 commit comments

Comments
 (0)