Skip to content

Commit c71d30c

Browse files
committed
ref(signer): Use Psbt::sighash_ecdsa for computing sighashes
- Change param `hash` to `&Message` in `sign_psbt_ecdsa` - Remove unused methods `compute_legacy_sighash`, and `compute_segwitv0_sighash`. - Match on `self.ctx` when signing for `SignerWrapper<PrivateKey>`
1 parent 66bc7b1 commit c71d30c

File tree

1 file changed

+78
-195
lines changed

1 file changed

+78
-195
lines changed

crates/wallet/src/wallet/signer.rs

Lines changed: 78 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ pub enum SignerError {
166166
/// [`TransactionSigner`], so that they can return their own custom errors, without having to
167167
/// modify [`SignerError`] in BDK.
168168
External(String),
169+
/// PSBT sign error.
170+
Psbt(psbt::SignError),
169171
}
170172

171173
impl From<sighash::Error> for SignerError {
@@ -191,6 +193,7 @@ impl fmt::Display for SignerError {
191193
Self::SighashError(err) => write!(f, "Error while computing the hash to sign: {}", err),
192194
Self::MiniscriptPsbt(err) => write!(f, "Miniscript PSBT error: {}", err),
193195
Self::External(err) => write!(f, "{}", err),
196+
Self::Psbt(err) => write!(f, "{}", err),
194197
}
195198
}
196199
}
@@ -453,92 +456,86 @@ impl InputSigner for SignerWrapper<PrivateKey> {
453456
}
454457

455458
let pubkey = PublicKey::from_private_key(secp, self);
456-
let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner);
457-
458-
if let SignerContext::Tap { is_internal_key } = self.ctx {
459-
if let Some(psbt_internal_key) = psbt.inputs[input_index].tap_internal_key {
460-
if is_internal_key
461-
&& psbt.inputs[input_index].tap_key_sig.is_none()
462-
&& sign_options.sign_with_tap_internal_key
463-
&& x_only_pubkey == psbt_internal_key
464-
{
465-
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, None)?;
466-
sign_psbt_schnorr(
467-
&self.inner,
468-
x_only_pubkey,
469-
None,
470-
&mut psbt.inputs[input_index],
471-
hash,
472-
hash_ty,
473-
secp,
474-
);
459+
460+
match self.ctx {
461+
SignerContext::Tap { is_internal_key } => {
462+
let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner);
463+
464+
if let Some(psbt_internal_key) = psbt.inputs[input_index].tap_internal_key {
465+
if is_internal_key
466+
&& psbt.inputs[input_index].tap_key_sig.is_none()
467+
&& sign_options.sign_with_tap_internal_key
468+
&& x_only_pubkey == psbt_internal_key
469+
{
470+
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, None)?;
471+
sign_psbt_schnorr(
472+
&self.inner,
473+
x_only_pubkey,
474+
None,
475+
&mut psbt.inputs[input_index],
476+
hash,
477+
hash_ty,
478+
secp,
479+
);
480+
}
475481
}
476-
}
477482

478-
if let Some((leaf_hashes, _)) =
479-
psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey)
480-
{
481-
let leaf_hashes = leaf_hashes
482-
.iter()
483-
.filter(|lh| {
484-
// Removing the leaves we shouldn't sign for
485-
let should_sign = match &sign_options.tap_leaves_options {
486-
TapLeavesOptions::All => true,
487-
TapLeavesOptions::Include(v) => v.contains(lh),
488-
TapLeavesOptions::Exclude(v) => !v.contains(lh),
489-
TapLeavesOptions::None => false,
490-
};
491-
// Filtering out the leaves without our key
492-
should_sign
493-
&& !psbt.inputs[input_index]
494-
.tap_script_sigs
495-
.contains_key(&(x_only_pubkey, **lh))
496-
})
497-
.cloned()
498-
.collect::<Vec<_>>();
499-
for lh in leaf_hashes {
500-
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, Some(lh))?;
501-
sign_psbt_schnorr(
502-
&self.inner,
503-
x_only_pubkey,
504-
Some(lh),
505-
&mut psbt.inputs[input_index],
506-
hash,
507-
hash_ty,
508-
secp,
509-
);
483+
if let Some((leaf_hashes, _)) =
484+
psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey)
485+
{
486+
let leaf_hashes = leaf_hashes
487+
.iter()
488+
.filter(|lh| {
489+
// Removing the leaves we shouldn't sign for
490+
let should_sign = match &sign_options.tap_leaves_options {
491+
TapLeavesOptions::All => true,
492+
TapLeavesOptions::Include(v) => v.contains(lh),
493+
TapLeavesOptions::Exclude(v) => !v.contains(lh),
494+
TapLeavesOptions::None => false,
495+
};
496+
// Filtering out the leaves without our key
497+
should_sign
498+
&& !psbt.inputs[input_index]
499+
.tap_script_sigs
500+
.contains_key(&(x_only_pubkey, **lh))
501+
})
502+
.cloned()
503+
.collect::<Vec<_>>();
504+
for lh in leaf_hashes {
505+
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, Some(lh))?;
506+
sign_psbt_schnorr(
507+
&self.inner,
508+
x_only_pubkey,
509+
Some(lh),
510+
&mut psbt.inputs[input_index],
511+
hash,
512+
hash_ty,
513+
secp,
514+
);
515+
}
510516
}
511517
}
518+
SignerContext::Segwitv0 | SignerContext::Legacy => {
519+
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
520+
return Ok(());
521+
}
512522

513-
return Ok(());
514-
}
515-
516-
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
517-
return Ok(());
518-
}
519-
520-
let (hash, hash_ty) = match self.ctx {
521-
SignerContext::Segwitv0 => {
522-
let (h, t) = compute_segwitv0_sighash(psbt, input_index)?;
523-
let h = h.to_raw_hash();
524-
(h, t)
525-
}
526-
SignerContext::Legacy => {
527-
let (h, t) = compute_legacy_sighash(psbt, input_index)?;
528-
let h = h.to_raw_hash();
529-
(h, t)
523+
let mut sighasher = sighash::SighashCache::new(psbt.unsigned_tx.clone());
524+
let (msg, hash_ty) = psbt
525+
.sighash_ecdsa(input_index, &mut sighasher)
526+
.map_err(SignerError::Psbt)?;
527+
528+
sign_psbt_ecdsa(
529+
&self.inner,
530+
pubkey,
531+
&mut psbt.inputs[input_index],
532+
&msg,
533+
hash_ty,
534+
secp,
535+
sign_options.allow_grinding,
536+
);
530537
}
531-
_ => return Ok(()), // handled above
532-
};
533-
sign_psbt_ecdsa(
534-
&self.inner,
535-
pubkey,
536-
&mut psbt.inputs[input_index],
537-
hash,
538-
hash_ty,
539-
secp,
540-
sign_options.allow_grinding,
541-
);
538+
}
542539

543540
Ok(())
544541
}
@@ -548,12 +545,11 @@ fn sign_psbt_ecdsa(
548545
secret_key: &secp256k1::SecretKey,
549546
pubkey: PublicKey,
550547
psbt_input: &mut psbt::Input,
551-
hash: impl bitcoin::hashes::Hash + bitcoin::secp256k1::ThirtyTwoByteHash,
548+
msg: &Message,
552549
hash_ty: EcdsaSighashType,
553550
secp: &SecpCtx,
554551
allow_grinding: bool,
555552
) {
556-
let msg = &Message::from(hash);
557553
let sig = if allow_grinding {
558554
secp.sign_ecdsa_low_r(msg, secret_key)
559555
} else {
@@ -845,119 +841,6 @@ impl Default for SignOptions {
845841
}
846842
}
847843

848-
/// Computes the legacy sighash.
849-
fn compute_legacy_sighash(
850-
psbt: &Psbt,
851-
input_index: usize,
852-
) -> Result<(sighash::LegacySighash, EcdsaSighashType), SignerError> {
853-
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
854-
return Err(SignerError::InputIndexOutOfRange);
855-
}
856-
857-
let psbt_input = &psbt.inputs[input_index];
858-
let tx_input = &psbt.unsigned_tx.input[input_index];
859-
860-
let sighash = psbt_input
861-
.sighash_type
862-
.unwrap_or_else(|| EcdsaSighashType::All.into())
863-
.ecdsa_hash_ty()
864-
.map_err(|_| SignerError::InvalidSighash)?;
865-
let script = match psbt_input.redeem_script {
866-
Some(ref redeem_script) => redeem_script.clone(),
867-
None => {
868-
let non_witness_utxo = psbt_input
869-
.non_witness_utxo
870-
.as_ref()
871-
.ok_or(SignerError::MissingNonWitnessUtxo)?;
872-
let prev_out = non_witness_utxo
873-
.output
874-
.get(tx_input.previous_output.vout as usize)
875-
.ok_or(SignerError::InvalidNonWitnessUtxo)?;
876-
877-
prev_out.script_pubkey.clone()
878-
}
879-
};
880-
881-
Ok((
882-
sighash::SighashCache::new(&psbt.unsigned_tx).legacy_signature_hash(
883-
input_index,
884-
&script,
885-
sighash.to_u32(),
886-
)?,
887-
sighash,
888-
))
889-
}
890-
891-
/// Computes the segwitv0 sighash.
892-
fn compute_segwitv0_sighash(
893-
psbt: &Psbt,
894-
input_index: usize,
895-
) -> Result<(sighash::SegwitV0Sighash, EcdsaSighashType), SignerError> {
896-
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
897-
return Err(SignerError::InputIndexOutOfRange);
898-
}
899-
900-
let psbt_input = &psbt.inputs[input_index];
901-
let tx_input = &psbt.unsigned_tx.input[input_index];
902-
903-
let sighash_type = psbt_input
904-
.sighash_type
905-
.unwrap_or_else(|| EcdsaSighashType::All.into())
906-
.ecdsa_hash_ty()
907-
.map_err(|_| SignerError::InvalidSighash)?;
908-
909-
// Always try first with the non-witness utxo
910-
let utxo = if let Some(prev_tx) = &psbt_input.non_witness_utxo {
911-
// Check the provided prev-tx
912-
if prev_tx.txid() != tx_input.previous_output.txid {
913-
return Err(SignerError::InvalidNonWitnessUtxo);
914-
}
915-
916-
// The output should be present, if it's missing the `non_witness_utxo` is invalid
917-
prev_tx
918-
.output
919-
.get(tx_input.previous_output.vout as usize)
920-
.ok_or(SignerError::InvalidNonWitnessUtxo)?
921-
} else if let Some(witness_utxo) = &psbt_input.witness_utxo {
922-
// Fallback to the witness_utxo. If we aren't allowed to use it, signing should fail
923-
// before we get to this point
924-
witness_utxo
925-
} else {
926-
// Nothing has been provided
927-
return Err(SignerError::MissingNonWitnessUtxo);
928-
};
929-
let value = utxo.value;
930-
931-
let mut sighasher = sighash::SighashCache::new(&psbt.unsigned_tx);
932-
933-
let sighash = match psbt_input.witness_script {
934-
Some(ref witness_script) => {
935-
sighasher.p2wsh_signature_hash(input_index, witness_script, value, sighash_type)?
936-
}
937-
None => {
938-
if utxo.script_pubkey.is_p2wpkh() {
939-
sighasher.p2wpkh_signature_hash(
940-
input_index,
941-
&utxo.script_pubkey,
942-
value,
943-
sighash_type,
944-
)?
945-
} else if psbt_input
946-
.redeem_script
947-
.as_ref()
948-
.map(|s| s.is_p2wpkh())
949-
.unwrap_or(false)
950-
{
951-
let script_pubkey = psbt_input.redeem_script.as_ref().unwrap();
952-
sighasher.p2wpkh_signature_hash(input_index, script_pubkey, value, sighash_type)?
953-
} else {
954-
return Err(SignerError::MissingWitnessScript);
955-
}
956-
}
957-
};
958-
Ok((sighash, sighash_type))
959-
}
960-
961844
/// Computes the taproot sighash.
962845
fn compute_tap_sighash(
963846
psbt: &Psbt,

0 commit comments

Comments
 (0)