Skip to content

Commit 55a1729

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 f2a2dae commit 55a1729

1 file changed

Lines changed: 84 additions & 223 deletions

File tree

crates/wallet/src/wallet/signer.rs

Lines changed: 84 additions & 223 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv};
9191
use bitcoin::hashes::hash160;
9292
use bitcoin::secp256k1::Message;
9393
use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType};
94-
use bitcoin::{ecdsa, psbt, sighash, taproot, transaction};
94+
use bitcoin::{ecdsa, psbt, sighash, taproot};
9595
use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1};
9696
use bitcoin::{PrivateKey, Psbt, PublicKey};
9797

@@ -159,12 +159,10 @@ pub enum SignerError {
159159
NonStandardSighash,
160160
/// Invalid SIGHASH for the signing context in use
161161
InvalidSighash,
162-
/// Error while computing the hash to sign a P2WPKH input.
163-
SighashP2wpkh(sighash::P2wpkhError),
164162
/// Error while computing the hash to sign a Taproot input.
165163
SighashTaproot(sighash::TaprootError),
166-
/// Error while computing the hash, out of bounds access on the transaction inputs.
167-
TxInputsIndexError(transaction::InputsIndexError),
164+
/// PSBT sign error.
165+
Psbt(psbt::SignError),
168166
/// Miniscript PSBT error
169167
MiniscriptPsbt(MiniscriptPsbtError),
170168
/// To be used only by external libraries implementing [`InputSigner`] or
@@ -173,24 +171,6 @@ pub enum SignerError {
173171
External(String),
174172
}
175173

176-
impl From<transaction::InputsIndexError> for SignerError {
177-
fn from(v: transaction::InputsIndexError) -> Self {
178-
Self::TxInputsIndexError(v)
179-
}
180-
}
181-
182-
impl From<sighash::P2wpkhError> for SignerError {
183-
fn from(e: sighash::P2wpkhError) -> Self {
184-
Self::SighashP2wpkh(e)
185-
}
186-
}
187-
188-
impl From<sighash::TaprootError> for SignerError {
189-
fn from(e: sighash::TaprootError) -> Self {
190-
Self::SighashTaproot(e)
191-
}
192-
}
193-
194174
impl fmt::Display for SignerError {
195175
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196176
match self {
@@ -205,9 +185,8 @@ impl fmt::Display for SignerError {
205185
Self::MissingHdKeypath => write!(f, "Missing fingerprint and derivation path"),
206186
Self::NonStandardSighash => write!(f, "The psbt contains a non standard sighash"),
207187
Self::InvalidSighash => write!(f, "Invalid SIGHASH for the signing context in use"),
208-
Self::SighashP2wpkh(err) => write!(f, "Error while computing the hash to sign a P2WPKH input: {}", err),
209188
Self::SighashTaproot(err) => write!(f, "Error while computing the hash to sign a Taproot input: {}", err),
210-
Self::TxInputsIndexError(err) => write!(f, "Error while computing the hash, out of bounds access on the transaction inputs: {}", err),
189+
Self::Psbt(err) => write!(f, "Error computing the sighash: {}", err),
211190
Self::MiniscriptPsbt(err) => write!(f, "Miniscript PSBT error: {}", err),
212191
Self::External(err) => write!(f, "{}", err),
213192
}
@@ -472,92 +451,87 @@ impl InputSigner for SignerWrapper<PrivateKey> {
472451
}
473452

474453
let pubkey = PublicKey::from_private_key(secp, self);
475-
let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner);
476-
477-
if let SignerContext::Tap { is_internal_key } = self.ctx {
478-
if let Some(psbt_internal_key) = psbt.inputs[input_index].tap_internal_key {
479-
if is_internal_key
480-
&& psbt.inputs[input_index].tap_key_sig.is_none()
481-
&& sign_options.sign_with_tap_internal_key
482-
&& x_only_pubkey == psbt_internal_key
483-
{
484-
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, None)?;
485-
sign_psbt_schnorr(
486-
&self.inner,
487-
x_only_pubkey,
488-
None,
489-
&mut psbt.inputs[input_index],
490-
hash,
491-
hash_ty,
492-
secp,
493-
);
454+
455+
match self.ctx {
456+
SignerContext::Tap { is_internal_key } => {
457+
let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner);
458+
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 (sighash, sighash_type) = 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+
sighash,
472+
sighash_type,
473+
secp,
474+
);
475+
}
494476
}
495-
}
496477

497-
if let Some((leaf_hashes, _)) =
498-
psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey)
499-
{
500-
let leaf_hashes = leaf_hashes
501-
.iter()
502-
.filter(|lh| {
503-
// Removing the leaves we shouldn't sign for
504-
let should_sign = match &sign_options.tap_leaves_options {
505-
TapLeavesOptions::All => true,
506-
TapLeavesOptions::Include(v) => v.contains(lh),
507-
TapLeavesOptions::Exclude(v) => !v.contains(lh),
508-
TapLeavesOptions::None => false,
509-
};
510-
// Filtering out the leaves without our key
511-
should_sign
512-
&& !psbt.inputs[input_index]
513-
.tap_script_sigs
514-
.contains_key(&(x_only_pubkey, **lh))
515-
})
516-
.cloned()
517-
.collect::<Vec<_>>();
518-
for lh in leaf_hashes {
519-
let (hash, hash_ty) = compute_tap_sighash(psbt, input_index, Some(lh))?;
520-
sign_psbt_schnorr(
521-
&self.inner,
522-
x_only_pubkey,
523-
Some(lh),
524-
&mut psbt.inputs[input_index],
525-
hash,
526-
hash_ty,
527-
secp,
528-
);
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 (sighash, sighash_type) =
501+
compute_tap_sighash(psbt, input_index, Some(lh))?;
502+
sign_psbt_schnorr(
503+
&self.inner,
504+
x_only_pubkey,
505+
Some(lh),
506+
&mut psbt.inputs[input_index],
507+
sighash,
508+
sighash_type,
509+
secp,
510+
);
511+
}
529512
}
530513
}
514+
SignerContext::Segwitv0 | SignerContext::Legacy => {
515+
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
516+
return Ok(());
517+
}
531518

532-
return Ok(());
533-
}
534-
535-
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
536-
return Ok(());
537-
}
519+
let mut sighasher = sighash::SighashCache::new(psbt.unsigned_tx.clone());
520+
let (msg, sighash_type) = psbt
521+
.sighash_ecdsa(input_index, &mut sighasher)
522+
.map_err(SignerError::Psbt)?;
538523

539-
let (hash, hash_ty) = match self.ctx {
540-
SignerContext::Segwitv0 => {
541-
let (h, t) = compute_segwitv0_sighash(psbt, input_index)?;
542-
let h = h.to_raw_hash();
543-
(h, t)
544-
}
545-
SignerContext::Legacy => {
546-
let (h, t) = compute_legacy_sighash(psbt, input_index)?;
547-
let h = h.to_raw_hash();
548-
(h, t)
524+
sign_psbt_ecdsa(
525+
&self.inner,
526+
pubkey,
527+
&mut psbt.inputs[input_index],
528+
&msg,
529+
sighash_type,
530+
secp,
531+
sign_options.allow_grinding,
532+
);
549533
}
550-
_ => return Ok(()), // handled above
551-
};
552-
sign_psbt_ecdsa(
553-
&self.inner,
554-
pubkey,
555-
&mut psbt.inputs[input_index],
556-
hash,
557-
hash_ty,
558-
secp,
559-
sign_options.allow_grinding,
560-
);
534+
}
561535

562536
Ok(())
563537
}
@@ -567,12 +541,11 @@ fn sign_psbt_ecdsa(
567541
secret_key: &secp256k1::SecretKey,
568542
pubkey: PublicKey,
569543
psbt_input: &mut psbt::Input,
570-
hash: impl bitcoin::hashes::Hash<Bytes = [u8; 32]>,
544+
msg: &Message,
571545
sighash_type: EcdsaSighashType,
572546
secp: &SecpCtx,
573547
allow_grinding: bool,
574548
) {
575-
let msg = &Message::from_digest(hash.to_byte_array());
576549
let signature = if allow_grinding {
577550
secp.sign_ecdsa_low_r(msg, secret_key)
578551
} else {
@@ -594,7 +567,7 @@ fn sign_psbt_schnorr(
594567
pubkey: XOnlyPublicKey,
595568
leaf_hash: Option<taproot::TapLeafHash>,
596569
psbt_input: &mut psbt::Input,
597-
hash: TapSighash,
570+
sighash: TapSighash,
598571
sighash_type: TapSighashType,
599572
secp: &SecpCtx,
600573
) {
@@ -606,7 +579,7 @@ fn sign_psbt_schnorr(
606579
Some(_) => keypair, // no tweak for script spend
607580
};
608581

609-
let msg = &Message::from(hash);
582+
let msg = &Message::from(sighash);
610583
let signature = secp.sign_schnorr_no_aux_rand(msg, &keypair);
611584
secp.verify_schnorr(&signature, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
612585
.expect("invalid or corrupted schnorr signature");
@@ -853,120 +826,6 @@ impl Default for SignOptions {
853826
}
854827
}
855828

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

1011870
Ok((
1012-
cache.taproot_signature_hash(input_index, &prevouts, None, extra, sighash_type)?,
871+
cache
872+
.taproot_signature_hash(input_index, &prevouts, None, extra, sighash_type)
873+
.map_err(SignerError::SighashTaproot)?,
1013874
sighash_type,
1014875
))
1015876
}

0 commit comments

Comments
 (0)