@@ -184,6 +184,55 @@ impl fmt::Display for AddressInfo {
184184/// A `CanonicalTx` managed by a `Wallet`.
185185pub 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+
187236impl Wallet {
188237 /// Build a new single descriptor [`Wallet`].
189238 ///
@@ -1866,23 +1915,55 @@ impl Wallet {
18661915 }
18671916 } )
18681917 . collect :: < HashMap < Txid , u32 > > ( ) ;
1918+ let current_height = sign_options
1919+ . assume_height
1920+ . unwrap_or_else ( || self . chain . tip ( ) . height ( ) ) ;
1921+
1922+ Ok ( self
1923+ . try_finalize_psbt_with ( psbt, |_, input| {
1924+ Some ( (
1925+ current_height,
1926+ confirmation_heights
1927+ . get ( & input. previous_output . txid )
1928+ . copied ( ) ,
1929+ ) )
1930+ } ) ?
1931+ . is_finalized ( ) )
1932+ }
1933+
1934+ /// Finalize a PSBT and return per-input finalization results. Use this method when you need to
1935+ /// inspect why a specific input could not be finalized.
1936+ ///
1937+ /// The method should only return `Err` when the PSBT is malformed, for example if its inputs
1938+ /// are out of bounds.
1939+ pub fn try_finalize_psbt (
1940+ & self ,
1941+ psbt : & mut Psbt ,
1942+ ) -> Result < FinalizedInputs , IndexOutOfBoundsError > {
1943+ self . try_finalize_psbt_with ( psbt, |_, _| None )
1944+ }
18691945
1870- let mut finished = true ;
1946+ fn try_finalize_psbt_with < F > (
1947+ & self ,
1948+ psbt : & mut Psbt ,
1949+ mut wallet_timelocks : F ,
1950+ ) -> Result < FinalizedInputs , IndexOutOfBoundsError >
1951+ where
1952+ F : FnMut ( usize , & bitcoin:: TxIn ) -> Option < ( u32 , Option < u32 > ) > ,
1953+ {
1954+ let tx = & psbt. unsigned_tx ;
1955+
1956+ let mut outcomes = BTreeMap :: new ( ) ;
18711957
18721958 for ( n, input) in tx. input . iter ( ) . enumerate ( ) {
18731959 let psbt_input = & psbt
18741960 . inputs
18751961 . get ( n)
18761962 . ok_or ( IndexOutOfBoundsError :: new ( n, psbt. inputs . len ( ) ) ) ?;
18771963 if psbt_input. final_script_sig . is_some ( ) || psbt_input. final_script_witness . is_some ( ) {
1964+ outcomes. insert ( n, FinalizeInputOutcome :: AlreadyFinalized ) ;
18781965 continue ;
18791966 }
1880- let confirmation_height = confirmation_heights
1881- . get ( & input. previous_output . txid )
1882- . copied ( ) ;
1883- let current_height = sign_options
1884- . assume_height
1885- . unwrap_or_else ( || self . chain . tip ( ) . height ( ) ) ;
18861967
18871968 // - Try to derive the descriptor by looking at the txout. If it's in our database, we
18881969 // know exactly which `keychain` to use, and which derivation index it is.
@@ -1902,14 +1983,22 @@ impl Wallet {
19021983 match desc {
19031984 Some ( desc) => {
19041985 let mut tmp_input = bitcoin:: TxIn :: default ( ) ;
1905- match desc. satisfy (
1906- & mut tmp_input,
1907- (
1908- PsbtInputSatisfier :: new ( psbt, n) ,
1909- After :: new ( Some ( current_height) , false ) ,
1910- Older :: new ( Some ( current_height) , confirmation_height, false ) ,
1911- ) ,
1912- ) {
1986+ let satisfy_result = if let Some ( ( current_height, confirmation_height) ) =
1987+ wallet_timelocks ( n, input)
1988+ {
1989+ desc. satisfy (
1990+ & mut tmp_input,
1991+ (
1992+ PsbtInputSatisfier :: new ( psbt, n) ,
1993+ After :: new ( Some ( current_height) , false ) ,
1994+ Older :: new ( Some ( current_height) , confirmation_height, false ) ,
1995+ ) ,
1996+ )
1997+ } else {
1998+ desc. satisfy ( & mut tmp_input, PsbtInputSatisfier :: new ( psbt, n) )
1999+ } ;
2000+
2001+ match satisfy_result {
19132002 Ok ( _) => {
19142003 let length = psbt. inputs . len ( ) ;
19152004 // Set the UTXO fields, final script_sig and witness
@@ -1927,23 +2016,29 @@ impl Wallet {
19272016 if !tmp_input. witness . is_empty ( ) {
19282017 psbt_input. final_script_witness = Some ( tmp_input. witness ) ;
19292018 }
2019+ outcomes. insert ( n, FinalizeInputOutcome :: Finalized ) ;
2020+ }
2021+ Err ( err) => {
2022+ outcomes. insert ( n, FinalizeInputOutcome :: CouldNotSatisfy ( err) ) ;
19302023 }
1931- Err ( _) => finished = false ,
19322024 }
19332025 }
1934- None => finished = false ,
2026+ None => {
2027+ outcomes. insert ( n, FinalizeInputOutcome :: MissingDescriptor ) ;
2028+ }
19352029 }
19362030 }
19372031
19382032 // Clear derivation paths from outputs.
1939- if finished {
2033+ let finalized = FinalizedInputs :: new ( outcomes) ;
2034+ if finalized. is_finalized ( ) {
19402035 for output in & mut psbt. outputs {
19412036 output. bip32_derivation . clear ( ) ;
19422037 output. tap_key_origins . clear ( ) ;
19432038 }
19442039 }
19452040
1946- Ok ( finished )
2041+ Ok ( finalized )
19472042 }
19482043
19492044 /// Return the secp256k1 context used for all signing operations.
0 commit comments