Skip to content

Commit

Permalink
Add support for finalize input
Browse files Browse the repository at this point in the history
  • Loading branch information
sanket1729 committed Mar 11, 2022
1 parent 8e5abd0 commit d331bf6
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 48 deletions.
124 changes: 76 additions & 48 deletions src/psbt/finalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,31 +284,43 @@ pub fn interpreter_check<C: secp256k1::Verification>(
) -> Result<(), Error> {
let utxos = prevouts(&psbt)?;
let utxos = &Prevouts::All(&utxos);
for (index, input) in psbt.inputs.iter().enumerate() {
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
let empty_script_sig = Script::new();
let empty_witness = Witness::default();
let script_sig = input.final_script_sig.as_ref().unwrap_or(&empty_script_sig);
let witness = input
.final_script_witness
.as_ref()
.map(|wit_slice| Witness::from_vec(wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness
.unwrap_or(empty_witness);
for (index, _input) in psbt.inputs.iter().enumerate() {
interpreter_inp_check(psbt, secp, index, utxos)?;
}
Ok(())
}

// Now look at all the satisfied constraints. If everything is filled in
// corrected, there should be no errors
// Interpreter check
{
let cltv = psbt.unsigned_tx.lock_time;
let csv = psbt.unsigned_tx.input[index].sequence;
let interpreter =
interpreter::Interpreter::from_txdata(spk, &script_sig, &witness, cltv, csv)
.map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
let iter = interpreter.iter(secp, &psbt.unsigned_tx, index, &utxos);
if let Some(error) = iter.filter_map(Result::err).next() {
return Err(Error::InputError(InputError::Interpreter(error), index));
};
}
// Run the miniscript interpreter on a single psbt input
fn interpreter_inp_check<C: secp256k1::Verification>(
psbt: &Psbt,
secp: &Secp256k1<C>,
index: usize,
utxos: &Prevouts,
) -> Result<(), Error> {
let input = &psbt.inputs[index];
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
let empty_script_sig = Script::new();
let empty_witness = Witness::default();
let script_sig = input.final_script_sig.as_ref().unwrap_or(&empty_script_sig);
let witness = input
.final_script_witness
.as_ref()
.map(|wit_slice| Witness::from_vec(wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness
.unwrap_or(empty_witness);

// Now look at all the satisfied constraints. If everything is filled in
// corrected, there should be no errors
// Interpreter check
{
let cltv = psbt.unsigned_tx.lock_time;
let csv = psbt.unsigned_tx.input[index].sequence;
let interpreter =
interpreter::Interpreter::from_txdata(spk, &script_sig, &witness, cltv, csv)
.map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
let iter = interpreter.iter(secp, &psbt.unsigned_tx, index, &utxos);
if let Some(error) = iter.filter_map(Result::err).next() {
return Err(Error::InputError(InputError::Interpreter(error), index));
};
}
Ok(())
}
Expand Down Expand Up @@ -347,29 +359,46 @@ pub fn finalize_helper<C: secp256k1::Verification>(

// Actually construct the witnesses
for index in 0..psbt.inputs.len() {
let (witness, script_sig) = {
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
let sat = PsbtInputSatisfier::new(&psbt, index);
finalize_input(psbt, index, secp, allow_mall)?;
}
// Double check everything with the interpreter
// This only checks whether the script will be executed
// correctly by the bitcoin interpreter under the current
// psbt context.
interpreter_check(&psbt, secp)?;
Ok(())
}

if spk.is_v1_p2tr() {
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
let wit = construct_tap_witness(spk, &sat, allow_mall)
.map_err(|e| Error::InputError(e, index))?;
(wit, Script::new())
} else {
// Get a descriptor for this input.
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
pub(super) fn finalize_input<C: secp256k1::Verification>(
psbt: &mut Psbt,
index: usize,
secp: &Secp256k1<C>,
allow_mall: bool,
) -> Result<(), super::Error> {
let (witness, script_sig) = {
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
let sat = PsbtInputSatisfier::new(&psbt, index);

//generate the satisfaction witness and scriptsig
if !allow_mall {
desc.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
} else {
desc.get_satisfaction_mall(PsbtInputSatisfier::new(&psbt, index))
}
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
if spk.is_v1_p2tr() {
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
let wit = construct_tap_witness(spk, &sat, allow_mall)
.map_err(|e| Error::InputError(e, index))?;
(wit, Script::new())
} else {
// Get a descriptor for this input.
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;

//generate the satisfaction witness and scriptsig
if !allow_mall {
desc.get_satisfaction(PsbtInputSatisfier::new(&psbt, index))
} else {
desc.get_satisfaction_mall(PsbtInputSatisfier::new(&psbt, index))
}
};
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
}
};

{
let input = &mut psbt.inputs[index];
//Fill in the satisfactions
input.final_script_sig = if script_sig.is_empty() {
Expand Down Expand Up @@ -402,11 +431,10 @@ pub fn finalize_helper<C: secp256k1::Verification>(
input.tap_internal_key = None; // x017
input.tap_merkle_root = None; // 0x018
}
// Double check everything with the interpreter
// This only checks whether the script will be executed
// correctly by the bitcoin interpreter under the current
// psbt context.
interpreter_check(&psbt, secp)?;
let utxos = prevouts(&psbt)?;
let utxos = &Prevouts::All(&utxos);
interpreter_inp_check(psbt, secp, index, utxos)?;

Ok(())
}

Expand Down
56 changes: 56 additions & 0 deletions src/psbt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ pub enum Error {
/// Input count in psbt
in_map: usize,
},
/// Psbt Input index out of bounds
InputIdxOutofBounds {
/// Inputs in pbst
psbt_inp: usize,
/// requested index
index: usize,
},
}

impl fmt::Display for InputError {
Expand Down Expand Up @@ -213,6 +220,11 @@ impl fmt::Display for Error {
"PSBT had {} inputs in transaction but {} inputs in map",
in_tx, in_map
),
Error::InputIdxOutofBounds { psbt_inp, index } => write!(
f,
"psbt input index {} out of bounds: psbt.inputs.len() {}",
index, psbt_inp
),
}
}
}
Expand Down Expand Up @@ -431,6 +443,22 @@ pub trait PsbtExt {
secp: &Secp256k1<C>,
) -> Result<(), Error>;

/// Same as [finalize], but only tries to finalize a single input leaving other
/// inputs as is. Use this when not all of inputs that you are trying to
/// satisfy are miniscripts
fn finalize_inp<C: secp256k1::Verification>(
&mut self,
secp: &secp256k1::Secp256k1<C>,
index: usize,
) -> Result<(), Error>;

/// Same as [finalize_inp], but allows for malleable satisfactions
fn finalize_inp_mall<C: secp256k1::Verification>(
&mut self,
secp: &secp256k1::Secp256k1<C>,
index: usize,
) -> Result<(), Error>;

/// Psbt extractor as defined in BIP174 that takes in a psbt reference
/// and outputs a extracted bitcoin::Transaction
/// Also does the interpreter sanity check
Expand Down Expand Up @@ -500,6 +528,34 @@ impl PsbtExt for Psbt {
finalizer::finalize_helper(self, secp, /*allow_mall*/ true)
}

fn finalize_inp<C: secp256k1::Verification>(
&mut self,
secp: &secp256k1::Secp256k1<C>,
index: usize,
) -> Result<(), Error> {
if index >= self.inputs.len() {
return Err(Error::InputIdxOutofBounds {
psbt_inp: self.inputs.len(),
index: index,
});
}
finalizer::finalize_input(self, index, secp, /*allow_mall*/ false)
}

fn finalize_inp_mall<C: secp256k1::Verification>(
&mut self,
secp: &secp256k1::Secp256k1<C>,
index: usize,
) -> Result<(), Error> {
if index >= self.inputs.len() {
return Err(Error::InputIdxOutofBounds {
psbt_inp: self.inputs.len(),
index: index,
});
}
finalizer::finalize_input(self, index, secp, /*allow_mall*/ false)
}

fn extract<C: secp256k1::Verification>(
&self,
secp: &Secp256k1<C>,
Expand Down

0 comments on commit d331bf6

Please sign in to comment.