signpsbt: implement Taproot keyspend signing

This commit is contained in:
Oliver Gugger 2024-12-27 13:47:32 +01:00
parent f478242346
commit 172c5da72e
No known key found for this signature in database
GPG key ID: 8E4256593F177720
2 changed files with 63 additions and 7 deletions

View file

@ -172,12 +172,30 @@ func signPsbt(rootKey *hdkeychain.ExtendedKey,
}
utxo := pIn.WitnessUtxo
localPrivateKey, err := localKey.ECPrivKey()
if err != nil {
return fmt.Errorf("error getting private key: %w", err)
}
// The signing is a bit different for P2WPKH, we need to specify
// the pk script as the witness script.
var witnessScript []byte
if txscript.IsPayToWitnessPubKeyHash(utxo.PkScript) {
switch {
case txscript.IsPayToWitnessPubKeyHash(utxo.PkScript):
witnessScript = utxo.PkScript
} else {
case txscript.IsPayToTaproot(utxo.PkScript):
err := signer.AddTaprootSignature(
packet, inputIndex, utxo, localPrivateKey,
)
if err != nil {
return fmt.Errorf("error adding taproot "+
"signature: %w", err)
}
continue
default:
if len(pIn.WitnessScript) == 0 {
return fmt.Errorf("invalid PSBT, input %d is "+
"missing witness script", inputIndex)
@ -185,11 +203,6 @@ func signPsbt(rootKey *hdkeychain.ExtendedKey,
witnessScript = pIn.WitnessScript
}
localPrivateKey, err := localKey.ECPrivKey()
if err != nil {
return fmt.Errorf("error getting private key: %w", err)
}
// Do we already have a partial signature for our key?
localPubKey := localPrivateKey.PubKey().SerializeCompressed()
haveSig := false

View file

@ -1,6 +1,7 @@
package lnd
import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
@ -234,6 +235,48 @@ func (s *Signer) AddPartialSignatureForPrivateKey(packet *psbt.Packet,
return nil
}
func (s *Signer) AddTaprootSignature(packet *psbt.Packet, inputIndex int,
utxo *wire.TxOut, privateKey *btcec.PrivateKey) error {
pIn := &packet.Inputs[inputIndex]
// Now we add our partial signature.
prevOutFetcher := wallet.PsbtPrevOutputFetcher(packet)
signDesc := &input.SignDescriptor{
Output: utxo,
InputIndex: inputIndex,
HashType: txscript.SigHashDefault,
PrevOutputFetcher: prevOutFetcher,
SigHashes: txscript.NewTxSigHashes(
packet.UnsignedTx, prevOutFetcher,
),
SignMethod: input.TaprootKeySpendBIP0086SignMethod,
}
if len(pIn.TaprootMerkleRoot) > 0 {
signDesc.SignMethod = input.TaprootKeySpendSignMethod
signDesc.TapTweak = pIn.TaprootMerkleRoot
}
ourSigRaw, err := s.SignOutputRawWithPrivateKey(
packet.UnsignedTx, signDesc, privateKey,
)
if err != nil {
return fmt.Errorf("error signing with our key: %w", err)
}
witness := wire.TxWitness{ourSigRaw.Serialize()}
var witnessBuf bytes.Buffer
err = psbt.WriteTxWitness(&witnessBuf, witness)
if err != nil {
return fmt.Errorf("error serializing witness: %w", err)
}
pIn.FinalScriptWitness = witnessBuf.Bytes()
return nil
}
// maybeTweakPrivKey examines the single tweak parameters on the passed sign
// descriptor and may perform a mapping on the passed private key in order to
// utilize the tweaks, if populated.