use bitcoin::util::sighash::Prevouts;
use std::borrow::Borrow;
use util::witness_size;
use super::{sanity_check, Psbt};
use super::{Error, InputError, PsbtInputSatisfier};
use bitcoin::blockdata::witness::Witness;
use bitcoin::secp256k1::{self, Secp256k1};
use bitcoin::util::key::XOnlyPublicKey;
use bitcoin::util::taproot::LeafVersion;
use bitcoin::{self, PublicKey, Script, TxOut};
use descriptor::DescriptorTrait;
use interpreter;
use Descriptor;
use Miniscript;
use Satisfier;
use {BareCtx, Legacy, Segwitv0, Tap};
fn construct_tap_witness(
spk: &Script,
sat: &PsbtInputSatisfier,
allow_mall: bool,
) -> Result<Vec<Vec<u8>>, InputError> {
assert!(spk.is_v1_p2tr());
if let Some(sig) =
<PsbtInputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_key_spend_sig(sat)
{
return Ok(vec![sig.to_vec()]);
}
let (mut min_wit, mut min_wit_len) = (None, None);
if let Some(block_map) =
<PsbtInputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_control_block_map(sat)
{
for (control_block, (script, ver)) in block_map {
if *ver != LeafVersion::TapScript {
continue;
}
let ms = match Miniscript::<XOnlyPublicKey, Tap>::parse_insane(script) {
Ok(ms) => ms,
Err(..) => continue, };
let mut wit = if allow_mall {
match ms.satisfy_malleable(sat) {
Ok(ms) => ms,
Err(..) => continue,
}
} else {
match ms.satisfy(sat) {
Ok(ms) => ms,
Err(..) => continue,
}
};
wit.push(ms.encode().into_bytes());
wit.push(control_block.serialize());
let wit_len = Some(witness_size(&wit));
if min_wit_len.is_some() && wit_len > min_wit_len {
continue;
} else {
min_wit = Some(wit);
min_wit_len = wit_len;
}
}
min_wit.ok_or(InputError::CouldNotSatisfyTr)
} else {
Err(InputError::CouldNotSatisfyTr)
}
}
pub(super) fn get_scriptpubkey(psbt: &Psbt, index: usize) -> Result<&Script, InputError> {
get_utxo(psbt, index).map(|utxo| &utxo.script_pubkey)
}
pub(super) fn get_utxo(psbt: &Psbt, index: usize) -> Result<&bitcoin::TxOut, InputError> {
let inp = &psbt.inputs[index];
let utxo = if let Some(ref witness_utxo) = inp.witness_utxo {
&witness_utxo
} else if let Some(ref non_witness_utxo) = inp.non_witness_utxo {
let vout = psbt.unsigned_tx.input[index].previous_output.vout;
&non_witness_utxo.output[vout as usize]
} else {
return Err(InputError::MissingUtxo);
};
Ok(utxo)
}
pub(super) fn prevouts<'a>(psbt: &'a Psbt) -> Result<Vec<&bitcoin::TxOut>, super::Error> {
let mut utxos = vec![];
for i in 0..psbt.inputs.len() {
let utxo_ref = get_utxo(psbt, i).map_err(|e| Error::InputError(e, i))?;
utxos.push(utxo_ref);
}
Ok(utxos)
}
fn get_descriptor(psbt: &Psbt, index: usize) -> Result<Descriptor<PublicKey>, InputError> {
let script_pubkey = get_scriptpubkey(psbt, index)?;
let inp = &psbt.inputs[index];
if script_pubkey.is_p2pk() {
let script_pubkey_len = script_pubkey.len();
let pk_bytes = &script_pubkey.to_bytes();
match bitcoin::PublicKey::from_slice(&pk_bytes[1..script_pubkey_len - 1]) {
Ok(pk) => Ok(Descriptor::new_pk(pk)),
Err(e) => Err(InputError::from(e)),
}
} else if script_pubkey.is_p2pkh() {
let partial_sig_contains_pk = inp
.partial_sigs
.iter()
.filter(|&(&pk, _sig)| {
let addr = bitcoin::Address::p2pkh(&pk, bitcoin::Network::Bitcoin);
*script_pubkey == addr.script_pubkey()
})
.next();
match partial_sig_contains_pk {
Some((pk, _sig)) => Ok(Descriptor::new_pkh(*pk)),
None => Err(InputError::MissingPubkey),
}
} else if script_pubkey.is_v0_p2wpkh() {
let partial_sig_contains_pk = inp
.partial_sigs
.iter()
.filter(|&(&pk, _sig)| {
let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin)
.expect("Address corresponding to valid pubkey");
*script_pubkey == addr.script_pubkey()
})
.next();
match partial_sig_contains_pk {
Some((pk, _sig)) => Ok(Descriptor::new_wpkh(*pk)?),
None => Err(InputError::MissingPubkey),
}
} else if script_pubkey.is_v0_p2wsh() {
if inp.redeem_script.is_some() {
return Err(InputError::NonEmptyRedeemScript);
}
if let Some(ref witness_script) = inp.witness_script {
if witness_script.to_v0_p2wsh() != *script_pubkey {
return Err(InputError::InvalidWitnessScript {
witness_script: witness_script.clone(),
p2wsh_expected: script_pubkey.clone(),
});
}
let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_insane(witness_script)?;
Ok(Descriptor::new_wsh(ms)?)
} else {
Err(InputError::MissingWitnessScript)
}
} else if script_pubkey.is_p2sh() {
match &inp.redeem_script {
&None => return Err(InputError::MissingRedeemScript),
&Some(ref redeem_script) => {
if redeem_script.to_p2sh() != *script_pubkey {
return Err(InputError::InvalidRedeemScript {
redeem: redeem_script.clone(),
p2sh_expected: script_pubkey.clone(),
});
}
if redeem_script.is_v0_p2wsh() {
if let Some(ref witness_script) = inp.witness_script {
if witness_script.to_v0_p2wsh() != *redeem_script {
return Err(InputError::InvalidWitnessScript {
witness_script: witness_script.clone(),
p2wsh_expected: redeem_script.clone(),
});
}
let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_insane(
witness_script,
)?;
Ok(Descriptor::new_sh_wsh(ms)?)
} else {
Err(InputError::MissingWitnessScript)
}
} else if redeem_script.is_v0_p2wpkh() {
let partial_sig_contains_pk = inp
.partial_sigs
.iter()
.filter(|&(&pk, _sig)| {
let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin)
.expect("Address corresponding to valid pubkey");
*redeem_script == addr.script_pubkey()
})
.next();
match partial_sig_contains_pk {
Some((pk, _sig)) => Ok(Descriptor::new_sh_wpkh(*pk)?),
None => Err(InputError::MissingPubkey),
}
} else {
if inp.witness_script.is_some() {
return Err(InputError::NonEmptyWitnessScript);
}
if let Some(ref redeem_script) = inp.redeem_script {
let ms =
Miniscript::<bitcoin::PublicKey, Legacy>::parse_insane(redeem_script)?;
Ok(Descriptor::new_sh(ms)?)
} else {
Err(InputError::MissingWitnessScript)
}
}
}
}
} else {
if inp.witness_script.is_some() {
return Err(InputError::NonEmptyWitnessScript);
}
if inp.redeem_script.is_some() {
return Err(InputError::NonEmptyRedeemScript);
}
let ms = Miniscript::<bitcoin::PublicKey, BareCtx>::parse_insane(script_pubkey)?;
Ok(Descriptor::new_bare(ms)?)
}
}
pub fn interpreter_check<C: secp256k1::Verification>(
psbt: &Psbt,
secp: &Secp256k1<C>,
) -> Result<(), Error> {
let utxos = prevouts(&psbt)?;
let utxos = &Prevouts::All(&utxos);
for (index, input) in psbt.inputs.iter().enumerate() {
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())) .unwrap_or(empty_witness);
interpreter_inp_check(psbt, secp, index, utxos, &witness, &script_sig)?;
}
Ok(())
}
fn interpreter_inp_check<C: secp256k1::Verification, T: Borrow<TxOut>>(
psbt: &Psbt,
secp: &Secp256k1<C>,
index: usize,
utxos: &Prevouts<T>,
witness: &Witness,
script_sig: &Script,
) -> Result<(), Error> {
let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
{
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,
super::get_ctv_hash(&psbt.unsigned_tx, index as u32),
)
.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(())
}
#[deprecated(since = "7.0", note = "Please use PsbtExt::finalize instead")]
pub fn finalize<C: secp256k1::Verification>(
psbt: &mut Psbt,
secp: &Secp256k1<C>,
) -> Result<(), super::Error> {
finalize_helper(psbt, secp, false)
}
pub fn finalize_mall<C: secp256k1::Verification>(
psbt: &mut Psbt,
secp: &Secp256k1<C>,
) -> Result<(), super::Error> {
finalize_helper(psbt, secp, true)
}
pub fn finalize_helper<C: secp256k1::Verification>(
psbt: &mut Psbt,
secp: &Secp256k1<C>,
allow_mall: bool,
) -> Result<(), super::Error> {
sanity_check(psbt)?;
for index in 0..psbt.inputs.len() {
finalize_input(psbt, index, secp, allow_mall)?;
}
Ok(())
}
fn finalize_input_helper<C: secp256k1::Verification>(
psbt: &Psbt,
index: usize,
secp: &Secp256k1<C>,
allow_mall: bool,
) -> Result<(Witness, Script), 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);
if spk.is_v1_p2tr() {
let wit = construct_tap_witness(spk, &sat, allow_mall)
.map_err(|e| Error::InputError(e, index))?;
(wit, Script::new())
} else {
let desc = get_descriptor(&psbt, index).map_err(|e| Error::InputError(e, index))?;
let sat = PsbtInputSatisfier::new(&psbt, index);
if !allow_mall {
desc.get_satisfaction(sat)
} else {
desc.get_satisfaction_mall(sat)
}
.map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
}
};
let witness = bitcoin::Witness::from_vec(witness);
let utxos = prevouts(&psbt)?;
let utxos = &Prevouts::All(&utxos);
interpreter_inp_check(psbt, secp, index, utxos, &witness, &script_sig)?;
Ok((witness, script_sig))
}
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) = finalize_input_helper(psbt, index, secp, allow_mall)?;
{
let input = &mut psbt.inputs[index];
input.final_script_sig = if script_sig.is_empty() {
None
} else {
Some(script_sig)
};
input.final_script_witness = if witness.is_empty() {
None
} else {
Some(witness)
};
input.partial_sigs.clear(); input.sighash_type = None; input.redeem_script = None; input.witness_script = None; input.bip32_derivation.clear(); input.ripemd160_preimages.clear(); input.sha256_preimages.clear(); input.hash160_preimages.clear(); input.hash256_preimages.clear(); input.tap_key_sig = None; input.tap_script_sigs.clear(); input.tap_scripts.clear(); input.tap_key_origins.clear(); input.tap_internal_key = None; input.tap_merkle_root = None; }
Ok(())
}
#[cfg(test)]
mod tests {
use psbt::PsbtExt;
use super::*;
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex;
#[test]
fn tests_from_bip174() {
let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
let secp = Secp256k1::verification_only();
psbt.finalize_mut(&secp).unwrap();
let expected: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
assert_eq!(psbt, expected);
}
}