diff --git a/cmd/chantools/signpsbt.go b/cmd/chantools/signpsbt.go index 65e9dcf..47ab20d 100644 --- a/cmd/chantools/signpsbt.go +++ b/cmd/chantools/signpsbt.go @@ -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 diff --git a/lnd/signer.go b/lnd/signer.go index 2173a43..a045612 100644 --- a/lnd/signer.go +++ b/lnd/signer.go @@ -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.