Skip to content

Commit

Permalink
signer: move p2wsh signing into a dedicated function
Browse files Browse the repository at this point in the history
  • Loading branch information
darosior committed Feb 27, 2024
1 parent d0276db commit ac8be89
Showing 1 changed file with 58 additions and 39 deletions.
97 changes: 58 additions & 39 deletions src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use miniscript::bitcoin::{
bip32::{self, Error as Bip32Error},
ecdsa,
hashes::Hash,
psbt::Psbt,
psbt::{Input as PsbtIn, Psbt},
secp256k1, sighash,
};

Expand Down Expand Up @@ -228,6 +228,56 @@ impl HotSigner {
bip32::Xpub::from_priv(secp, &xpriv)
}

// Provide an ECDSA signature for this transaction input from the PSBT input information.
fn sign_p2wsh(
&self,
secp: &secp256k1::Secp256k1<impl secp256k1::Signing>,
sighash_cache: &mut sighash::SighashCache<&bitcoin::Transaction>,
master_fingerprint: bip32::Fingerprint,
psbt_in: &mut PsbtIn,
input_index: usize,
) -> Result<(), SignerError> {
// First of all compute the sighash for this input. We assume P2WSH spend: the sighash
// script code is always the witness script.
let witscript = psbt_in
.witness_script
.as_ref()
.ok_or(SignerError::IncompletePsbt)?;
let value = psbt_in
.witness_utxo
.as_ref()
.ok_or(SignerError::IncompletePsbt)?
.value;
let sig_type = sighash::EcdsaSighashType::All;
let sighash = sighash_cache
.p2wsh_signature_hash(input_index, witscript, value, sig_type)
.map_err(|_| SignerError::InsanePsbt)?;
let sighash = secp256k1::Message::from_digest_slice(sighash.as_byte_array())
.expect("Sighash is always 32 bytes.");

// Then provide a signature for all the keys they asked for.
for (curr_pubkey, (fingerprint, der_path)) in psbt_in.bip32_derivation.iter() {
if *fingerprint != master_fingerprint {
continue;
}
let privkey = self.xpriv_at(der_path, secp).to_priv();
let pubkey = privkey.public_key(secp);
if pubkey.inner != *curr_pubkey {
return Err(SignerError::InsanePsbt);
}
let sig = secp.sign_ecdsa_low_r(&sighash, &privkey.inner);
psbt_in.partial_sigs.insert(
pubkey,
ecdsa::Signature {
sig,
hash_ty: sig_type,
},
);
}

Ok(())
}

/// Sign all inputs of the given PSBT.
///
/// **This does not perform any check. It will blindly sign anything that's passed.**
Expand All @@ -241,44 +291,13 @@ impl HotSigner {

// Sign each input in the PSBT.
for i in 0..psbt.inputs.len() {
// First of all compute the sighash for this input. We assume P2WSH spend: the sighash
// script code is always the witness script.
let witscript = psbt.inputs[i]
.witness_script
.as_ref()
.ok_or(SignerError::IncompletePsbt)?;
let value = psbt.inputs[i]
.witness_utxo
.as_ref()
.ok_or(SignerError::IncompletePsbt)?
.value;
let sig_type = sighash::EcdsaSighashType::All;
let sighash = sighash_cache
.p2wsh_signature_hash(i, witscript, value, sig_type)
.map_err(|_| SignerError::InsanePsbt)?;
let sighash = secp256k1::Message::from_digest_slice(sighash.as_byte_array())
.expect("Sighash is always 32 bytes.");

// Then provide a signature for all the keys they asked for.
let input = &mut psbt.inputs[i]; // for borrowck reasons
for (curr_pubkey, (fingerprint, der_path)) in input.bip32_derivation.iter() {
if *fingerprint != master_fingerprint {
continue;
}
let privkey = self.xpriv_at(der_path, secp).to_priv();
let pubkey = privkey.public_key(secp);
if pubkey.inner != *curr_pubkey {
return Err(SignerError::InsanePsbt);
}
let sig = secp.sign_ecdsa_low_r(&sighash, &privkey.inner);
input.partial_sigs.insert(
pubkey,
ecdsa::Signature {
sig,
hash_ty: sig_type,
},
);
}
self.sign_p2wsh(
secp,
&mut sighash_cache,
master_fingerprint,
&mut psbt.inputs[i],
i,
)?;
}

Ok(psbt)
Expand Down

0 comments on commit ac8be89

Please sign in to comment.