refactor(signer): Remove trait ComputeSighash

This commit is contained in:
valued mammal 2024-05-01 14:47:20 -04:00
parent 6dab68d35b
commit f2a2dae84c
No known key found for this signature in database

View File

@ -99,7 +99,7 @@ use miniscript::descriptor::{
Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey,
InnerXKey, KeyMap, SinglePriv, SinglePubKey, InnerXKey, KeyMap, SinglePriv, SinglePubKey,
}; };
use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey}; use miniscript::{SigType, ToPublicKey};
use super::utils::SecpCtx; use super::utils::SecpCtx;
use crate::descriptor::{DescriptorMeta, XKeyUtils}; use crate::descriptor::{DescriptorMeta, XKeyUtils};
@ -481,7 +481,7 @@ impl InputSigner for SignerWrapper<PrivateKey> {
&& sign_options.sign_with_tap_internal_key && sign_options.sign_with_tap_internal_key
&& x_only_pubkey == psbt_internal_key && x_only_pubkey == psbt_internal_key
{ {
let (hash, hash_ty) = Tap::sighash(psbt, input_index, None)?; let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, None)?;
sign_psbt_schnorr( sign_psbt_schnorr(
&self.inner, &self.inner,
x_only_pubkey, x_only_pubkey,
@ -516,7 +516,7 @@ impl InputSigner for SignerWrapper<PrivateKey> {
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for lh in leaf_hashes { for lh in leaf_hashes {
let (hash, hash_ty) = Tap::sighash(psbt, input_index, Some(lh))?; let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, Some(lh))?;
sign_psbt_schnorr( sign_psbt_schnorr(
&self.inner, &self.inner,
x_only_pubkey, x_only_pubkey,
@ -538,12 +538,12 @@ impl InputSigner for SignerWrapper<PrivateKey> {
let (hash, hash_ty) = match self.ctx { let (hash, hash_ty) = match self.ctx {
SignerContext::Segwitv0 => { SignerContext::Segwitv0 => {
let (h, t) = Segwitv0::sighash(psbt, input_index, ())?; let (h, t) = compute_segwitv0_sighash(psbt, input_index)?;
let h = h.to_raw_hash(); let h = h.to_raw_hash();
(h, t) (h, t)
} }
SignerContext::Legacy => { SignerContext::Legacy => {
let (h, t) = Legacy::sighash(psbt, input_index, ())?; let (h, t) = compute_legacy_sighash(psbt, input_index)?;
let h = h.to_raw_hash(); let h = h.to_raw_hash();
(h, t) (h, t)
} }
@ -853,198 +853,165 @@ impl Default for SignOptions {
} }
} }
pub(crate) trait ComputeSighash { /// Computes the legacy sighash.
type Extra; fn compute_legacy_sighash(
type Sighash; psbt: &Psbt,
type SighashType; input_index: usize,
) -> Result<(sighash::LegacySighash, EcdsaSighashType), SignerError> {
fn sighash( if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
psbt: &Psbt, return Err(SignerError::InputIndexOutOfRange);
input_index: usize,
extra: Self::Extra,
) -> Result<(Self::Sighash, Self::SighashType), SignerError>;
}
impl ComputeSighash for Legacy {
type Extra = ();
type Sighash = sighash::LegacySighash;
type SighashType = EcdsaSighashType;
fn sighash(
psbt: &Psbt,
input_index: usize,
_extra: (),
) -> Result<(Self::Sighash, Self::SighashType), SignerError> {
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange);
}
let psbt_input = &psbt.inputs[input_index];
let tx_input = &psbt.unsigned_tx.input[input_index];
let sighash = psbt_input
.sighash_type
.unwrap_or_else(|| EcdsaSighashType::All.into())
.ecdsa_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?;
let script = match psbt_input.redeem_script {
Some(ref redeem_script) => redeem_script.clone(),
None => {
let non_witness_utxo = psbt_input
.non_witness_utxo
.as_ref()
.ok_or(SignerError::MissingNonWitnessUtxo)?;
let prev_out = non_witness_utxo
.output
.get(tx_input.previous_output.vout as usize)
.ok_or(SignerError::InvalidNonWitnessUtxo)?;
prev_out.script_pubkey.clone()
}
};
Ok((
sighash::SighashCache::new(&psbt.unsigned_tx).legacy_signature_hash(
input_index,
&script,
sighash.to_u32(),
)?,
sighash,
))
} }
}
impl ComputeSighash for Segwitv0 { let psbt_input = &psbt.inputs[input_index];
type Extra = (); let tx_input = &psbt.unsigned_tx.input[input_index];
type Sighash = sighash::SegwitV0Sighash;
type SighashType = EcdsaSighashType;
fn sighash( let sighash_type = psbt_input
psbt: &Psbt, .sighash_type
input_index: usize, .unwrap_or_else(|| EcdsaSighashType::All.into())
_extra: (), .ecdsa_hash_ty()
) -> Result<(Self::Sighash, Self::SighashType), SignerError> { .map_err(|_| SignerError::InvalidSighash)?;
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange);
}
let psbt_input = &psbt.inputs[input_index]; let script = match psbt_input.redeem_script {
let tx_input = &psbt.unsigned_tx.input[input_index]; Some(ref redeem_script) => redeem_script.clone(),
None => {
let sighash_type = psbt_input let non_witness_utxo = psbt_input
.sighash_type .non_witness_utxo
.unwrap_or_else(|| EcdsaSighashType::All.into()) .as_ref()
.ecdsa_hash_ty() .ok_or(SignerError::MissingNonWitnessUtxo)?;
.map_err(|_| SignerError::InvalidSighash)?; let prev_out = non_witness_utxo
// Always try first with the non-witness utxo
let utxo = if let Some(prev_tx) = &psbt_input.non_witness_utxo {
// Check the provided prev-tx
if prev_tx.compute_txid() != tx_input.previous_output.txid {
return Err(SignerError::InvalidNonWitnessUtxo);
}
// The output should be present, if it's missing the `non_witness_utxo` is invalid
prev_tx
.output .output
.get(tx_input.previous_output.vout as usize) .get(tx_input.previous_output.vout as usize)
.ok_or(SignerError::InvalidNonWitnessUtxo)? .ok_or(SignerError::InvalidNonWitnessUtxo)?;
} else if let Some(witness_utxo) = &psbt_input.witness_utxo {
// Fallback to the witness_utxo. If we aren't allowed to use it, signing should fail
// before we get to this point
witness_utxo
} else {
// Nothing has been provided
return Err(SignerError::MissingNonWitnessUtxo);
};
let value = utxo.value;
let mut sighasher = sighash::SighashCache::new(&psbt.unsigned_tx); prev_out.script_pubkey.clone()
}
};
let sighash = match psbt_input.witness_script { Ok((
Some(ref witness_script) => { sighash::SighashCache::new(&psbt.unsigned_tx).legacy_signature_hash(
sighasher.p2wsh_signature_hash(input_index, witness_script, value, sighash_type)? input_index,
} &script,
None => { sighash_type.to_u32(),
if utxo.script_pubkey.is_p2wpkh() { )?,
sighasher.p2wpkh_signature_hash( sighash_type,
input_index, ))
&utxo.script_pubkey,
value,
sighash_type,
)?
} else if psbt_input
.redeem_script
.as_ref()
.map(|s| s.is_p2wpkh())
.unwrap_or(false)
{
let script_pubkey = psbt_input.redeem_script.as_ref().unwrap();
sighasher.p2wpkh_signature_hash(
input_index,
script_pubkey,
value,
sighash_type,
)?
} else {
return Err(SignerError::MissingWitnessScript);
}
}
};
Ok((sighash, sighash_type))
}
} }
impl ComputeSighash for Tap { /// Computes the segwitv0 sighash.
type Extra = Option<taproot::TapLeafHash>; fn compute_segwitv0_sighash(
type Sighash = TapSighash; psbt: &Psbt,
type SighashType = TapSighashType; input_index: usize,
) -> Result<(sighash::SegwitV0Sighash, EcdsaSighashType), SignerError> {
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange);
}
fn sighash( let psbt_input = &psbt.inputs[input_index];
psbt: &Psbt, let tx_input = &psbt.unsigned_tx.input[input_index];
input_index: usize,
extra: Self::Extra, let sighash_type = psbt_input
) -> Result<(Self::Sighash, TapSighashType), SignerError> { .sighash_type
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() { .unwrap_or_else(|| EcdsaSighashType::All.into())
return Err(SignerError::InputIndexOutOfRange); .ecdsa_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?;
// Always try first with the non-witness utxo
let utxo = if let Some(prev_tx) = &psbt_input.non_witness_utxo {
// Check the provided prev-tx
if prev_tx.compute_txid() != tx_input.previous_output.txid {
return Err(SignerError::InvalidNonWitnessUtxo);
} }
let psbt_input = &psbt.inputs[input_index]; // The output should be present, if it's missing the `non_witness_utxo` is invalid
prev_tx
.output
.get(tx_input.previous_output.vout as usize)
.ok_or(SignerError::InvalidNonWitnessUtxo)?
} else if let Some(witness_utxo) = &psbt_input.witness_utxo {
// Fallback to the witness_utxo. If we aren't allowed to use it, signing should fail
// before we get to this point
witness_utxo
} else {
// Nothing has been provided
return Err(SignerError::MissingNonWitnessUtxo);
};
let value = utxo.value;
let sighash_type = psbt_input let mut sighasher = sighash::SighashCache::new(&psbt.unsigned_tx);
.sighash_type
.unwrap_or_else(|| TapSighashType::Default.into())
.taproot_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?;
let witness_utxos = (0..psbt.inputs.len())
.map(|i| psbt.get_utxo_for(i))
.collect::<Vec<_>>();
let mut all_witness_utxos = vec![];
let mut cache = sighash::SighashCache::new(&psbt.unsigned_tx); let sighash = match psbt_input.witness_script {
let is_anyone_can_pay = psbt::PsbtSighashType::from(sighash_type).to_u32() & 0x80 != 0; Some(ref witness_script) => {
let prevouts = if is_anyone_can_pay { sighasher.p2wsh_signature_hash(input_index, witness_script, value, sighash_type)?
sighash::Prevouts::One( }
input_index, None => {
witness_utxos[input_index] if utxo.script_pubkey.is_p2wpkh() {
.as_ref() sighasher.p2wpkh_signature_hash(
.ok_or(SignerError::MissingWitnessUtxo)?, input_index,
) &utxo.script_pubkey,
} else if witness_utxos.iter().all(Option::is_some) { value,
all_witness_utxos.extend(witness_utxos.iter().filter_map(|x| x.as_ref())); sighash_type,
sighash::Prevouts::All(&all_witness_utxos) )?
} else { } else if psbt_input
return Err(SignerError::MissingWitnessUtxo); .redeem_script
}; .as_ref()
.map(|s| s.is_p2wpkh())
.unwrap_or(false)
{
let script_pubkey = psbt_input.redeem_script.as_ref().unwrap();
sighasher.p2wpkh_signature_hash(input_index, script_pubkey, value, sighash_type)?
} else {
return Err(SignerError::MissingWitnessScript);
}
}
};
Ok((sighash, sighash_type))
}
// Assume no OP_CODESEPARATOR /// Computes the taproot sighash.
let extra = extra.map(|leaf_hash| (leaf_hash, 0xFFFFFFFF)); fn compute_tap_sighash(
psbt: &Psbt,
Ok(( input_index: usize,
cache.taproot_signature_hash(input_index, &prevouts, None, extra, sighash_type)?, extra: Option<taproot::TapLeafHash>,
sighash_type, ) -> Result<(sighash::TapSighash, TapSighashType), SignerError> {
)) if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange);
} }
let psbt_input = &psbt.inputs[input_index];
let sighash_type = psbt_input
.sighash_type
.unwrap_or_else(|| TapSighashType::Default.into())
.taproot_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?;
let witness_utxos = (0..psbt.inputs.len())
.map(|i| psbt.get_utxo_for(i))
.collect::<Vec<_>>();
let mut all_witness_utxos = vec![];
let mut cache = sighash::SighashCache::new(&psbt.unsigned_tx);
let is_anyone_can_pay = psbt::PsbtSighashType::from(sighash_type).to_u32() & 0x80 != 0;
let prevouts = if is_anyone_can_pay {
sighash::Prevouts::One(
input_index,
witness_utxos[input_index]
.as_ref()
.ok_or(SignerError::MissingWitnessUtxo)?,
)
} else if witness_utxos.iter().all(Option::is_some) {
all_witness_utxos.extend(witness_utxos.iter().filter_map(|x| x.as_ref()));
sighash::Prevouts::All(&all_witness_utxos)
} else {
return Err(SignerError::MissingWitnessUtxo);
};
// Assume no OP_CODESEPARATOR
let extra = extra.map(|leaf_hash| (leaf_hash, 0xFFFFFFFF));
Ok((
cache.taproot_signature_hash(input_index, &prevouts, None, extra, sighash_type)?,
sighash_type,
))
} }
impl PartialOrd for SignersContainerKey { impl PartialOrd for SignersContainerKey {