Skip to content

Commit

Permalink
signpsbt: implement Taproot keyspend signing
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Dec 27, 2024
1 parent f478242 commit 172c5da
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 7 deletions.
27 changes: 20 additions & 7 deletions cmd/chantools/signpsbt.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,24 +172,37 @@ 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)
}
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
Expand Down
43 changes: 43 additions & 0 deletions lnd/signer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lnd

import (
"bytes"
"crypto/sha256"
"errors"
"fmt"
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 172c5da

Please sign in to comment.