Skip to content

Commit f5efd00

Browse files
committed
Add support for finalize input
1 parent 7e904a4 commit f5efd00

File tree

2 files changed

+132
-48
lines changed

2 files changed

+132
-48
lines changed

src/psbt/finalizer.rs

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -284,31 +284,43 @@ pub fn interpreter_check<C: secp256k1::Verification>(
284284
) -> Result<(), Error> {
285285
let utxos = prevouts(&psbt)?;
286286
let utxos = &Prevouts::All(&utxos);
287-
for (index, input) in psbt.inputs.iter().enumerate() {
288-
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
289-
let empty_script_sig = Script::new();
290-
let empty_witness = Witness::default();
291-
let script_sig = input.final_script_sig.as_ref().unwrap_or(&empty_script_sig);
292-
let witness = input
293-
.final_script_witness
294-
.as_ref()
295-
.map(|wit_slice| Witness::from_vec(wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness
296-
.unwrap_or(empty_witness);
287+
for (index, _input) in psbt.inputs.iter().enumerate() {
288+
interpreter_inp_check(psbt, secp, index, utxos)?;
289+
}
290+
Ok(())
291+
}
297292

298-
// Now look at all the satisfied constraints. If everything is filled in
299-
// corrected, there should be no errors
300-
// Interpreter check
301-
{
302-
let cltv = psbt.unsigned_tx.lock_time;
303-
let csv = psbt.unsigned_tx.input[index].sequence;
304-
let interpreter =
305-
interpreter::Interpreter::from_txdata(spk, &script_sig, &witness, cltv, csv)
306-
.map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
307-
let iter = interpreter.iter(secp, &psbt.unsigned_tx, index, &utxos);
308-
if let Some(error) = iter.filter_map(Result::err).next() {
309-
return Err(Error::InputError(InputError::Interpreter(error), index));
310-
};
311-
}
293+
// Run the miniscript interpreter on a single psbt input
294+
fn interpreter_inp_check<C: secp256k1::Verification>(
295+
psbt: &Psbt,
296+
secp: &Secp256k1<C>,
297+
index: usize,
298+
utxos: &Prevouts,
299+
) -> Result<(), Error> {
300+
let input = &psbt.inputs[index];
301+
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
302+
let empty_script_sig = Script::new();
303+
let empty_witness = Witness::default();
304+
let script_sig = input.final_script_sig.as_ref().unwrap_or(&empty_script_sig);
305+
let witness = input
306+
.final_script_witness
307+
.as_ref()
308+
.map(|wit_slice| Witness::from_vec(wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness
309+
.unwrap_or(empty_witness);
310+
311+
// Now look at all the satisfied constraints. If everything is filled in
312+
// corrected, there should be no errors
313+
// Interpreter check
314+
{
315+
let cltv = psbt.unsigned_tx.lock_time;
316+
let csv = psbt.unsigned_tx.input[index].sequence;
317+
let interpreter =
318+
interpreter::Interpreter::from_txdata(spk, &script_sig, &witness, cltv, csv)
319+
.map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
320+
let iter = interpreter.iter(secp, &psbt.unsigned_tx, index, &utxos);
321+
if let Some(error) = iter.filter_map(Result::err).next() {
322+
return Err(Error::InputError(InputError::Interpreter(error), index));
323+
};
312324
}
313325
Ok(())
314326
}
@@ -347,29 +359,46 @@ pub fn finalize_helper<C: secp256k1::Verification>(
347359

348360
// Actually construct the witnesses
349361
for index in 0..psbt.inputs.len() {
350-
let (witness, script_sig) = {
351-
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
352-
let sat = PsbtInputSatisfier::new(&psbt, index);
362+
finalize_input(psbt, index, secp, allow_mall)?;
363+
}
364+
// Double check everything with the interpreter
365+
// This only checks whether the script will be executed
366+
// correctly by the bitcoin interpreter under the current
367+
// psbt context.
368+
interpreter_check(&psbt, secp)?;
369+
Ok(())
370+
}
353371

354-
if spk.is_v1_p2tr() {
355-
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
356-
let wit = construct_tap_witness(spk, &sat, allow_mall)
357-
.map_err(|e| Error::InputError(e, index))?;
358-
(wit, Script::new())
359-
} else {
360-
// Get a descriptor for this input.
361-
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
372+
pub(super) fn finalize_input<C: secp256k1::Verification>(
373+
psbt: &mut Psbt,
374+
index: usize,
375+
secp: &Secp256k1<C>,
376+
allow_mall: bool,
377+
) -> Result<(), super::Error> {
378+
let (witness, script_sig) = {
379+
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
380+
let sat = PsbtInputSatisfier::new(&psbt, index);
362381

363-
//generate the satisfaction witness and scriptsig
364-
if !allow_mall {
365-
desc.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
366-
} else {
367-
desc.get_satisfaction_mall(PsbtInputSatisfier::new(&psbt, index))
368-
}
369-
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
382+
if spk.is_v1_p2tr() {
383+
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
384+
let wit = construct_tap_witness(spk, &sat, allow_mall)
385+
.map_err(|e| Error::InputError(e, index))?;
386+
(wit, Script::new())
387+
} else {
388+
// Get a descriptor for this input.
389+
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
390+
391+
//generate the satisfaction witness and scriptsig
392+
if !allow_mall {
393+
desc.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
394+
} else {
395+
desc.get_satisfaction_mall(PsbtInputSatisfier::new(&psbt, index))
370396
}
371-
};
397+
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
398+
}
399+
};
372400

401+
{
373402
let input = &mut psbt.inputs[index];
374403
//Fill in the satisfactions
375404
input.final_script_sig = if script_sig.is_empty() {
@@ -402,11 +431,10 @@ pub fn finalize_helper<C: secp256k1::Verification>(
402431
input.tap_internal_key = None; // x017
403432
input.tap_merkle_root = None; // 0x018
404433
}
405-
// Double check everything with the interpreter
406-
// This only checks whether the script will be executed
407-
// correctly by the bitcoin interpreter under the current
408-
// psbt context.
409-
interpreter_check(&psbt, secp)?;
434+
let utxos = prevouts(&psbt)?;
435+
let utxos = &Prevouts::All(&utxos);
436+
interpreter_inp_check(psbt, secp, index, utxos)?;
437+
410438
Ok(())
411439
}
412440

src/psbt/mod.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ pub enum Error {
120120
/// Input count in psbt
121121
in_map: usize,
122122
},
123+
/// Psbt Input index out of bounds
124+
InputIdxOutofBounds {
125+
/// Inputs in pbst
126+
psbt_inp: usize,
127+
/// requested index
128+
index: usize,
129+
},
123130
}
124131

125132
impl fmt::Display for InputError {
@@ -213,6 +220,11 @@ impl fmt::Display for Error {
213220
"PSBT had {} inputs in transaction but {} inputs in map",
214221
in_tx, in_map
215222
),
223+
Error::InputIdxOutofBounds { psbt_inp, index } => write!(
224+
f,
225+
"psbt input index {} out of bounds: psbt.inputs.len() {}",
226+
index, psbt_inp
227+
),
216228
}
217229
}
218230
}
@@ -431,6 +443,22 @@ pub trait PbstExt {
431443
secp: &Secp256k1<C>,
432444
) -> Result<(), Error>;
433445

446+
/// Same as [finalize], but only tries to finalize a single input leaving other
447+
/// inputs as is. Use this when not all of inputs that you are trying to
448+
/// satisfy are miniscripts
449+
fn finalize_inp<C: secp256k1::Verification>(
450+
&mut self,
451+
secp: &secp256k1::Secp256k1<C>,
452+
index: usize,
453+
) -> Result<(), Error>;
454+
455+
/// Same as [finalize_inp], but allows for malleable satisfactions
456+
fn finalize_inp_mall<C: secp256k1::Verification>(
457+
&mut self,
458+
secp: &secp256k1::Secp256k1<C>,
459+
index: usize,
460+
) -> Result<(), Error>;
461+
434462
/// Psbt extractor as defined in BIP174 that takes in a psbt reference
435463
/// and outputs a extracted bitcoin::Transaction
436464
/// Also does the interpreter sanity check
@@ -500,6 +528,34 @@ impl PbstExt for Psbt {
500528
finalizer::finalize_helper(self, secp, /*allow_mall*/ true)
501529
}
502530

531+
fn finalize_inp<C: secp256k1::Verification>(
532+
&mut self,
533+
secp: &secp256k1::Secp256k1<C>,
534+
index: usize,
535+
) -> Result<(), Error> {
536+
if index >= self.inputs.len() {
537+
return Err(Error::InputIdxOutofBounds {
538+
psbt_inp: self.inputs.len(),
539+
index: index,
540+
});
541+
}
542+
finalizer::finalize_input(self, index, secp, /*allow_mall*/ false)
543+
}
544+
545+
fn finalize_inp_mall<C: secp256k1::Verification>(
546+
&mut self,
547+
secp: &secp256k1::Secp256k1<C>,
548+
index: usize,
549+
) -> Result<(), Error> {
550+
if index >= self.inputs.len() {
551+
return Err(Error::InputIdxOutofBounds {
552+
psbt_inp: self.inputs.len(),
553+
index: index,
554+
});
555+
}
556+
finalizer::finalize_input(self, index, secp, /*allow_mall*/ false)
557+
}
558+
503559
fn extract<C: secp256k1::Verification>(
504560
&self,
505561
secp: &Secp256k1<C>,

0 commit comments

Comments
 (0)