mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
btcwallet: support taproot key spend signing in PSBT
This commit is contained in:
parent
e4aa32fdf7
commit
0f32a4456d
@ -2,6 +2,7 @@ package btcwallet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
@ -179,6 +180,20 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
|
||||
case input.WitnessV0SignMethod:
|
||||
err = signSegWitV0(in, tx, sigHashes, idx, privKey)
|
||||
|
||||
// For p2tr BIP0086 key spend only.
|
||||
case input.TaprootKeySpendBIP0086SignMethod:
|
||||
rootHash := make([]byte, 0)
|
||||
err = signSegWitV1KeySpend(
|
||||
in, tx, sigHashes, idx, privKey, rootHash,
|
||||
)
|
||||
|
||||
// For p2tr with script commitment key spend path.
|
||||
case input.TaprootKeySpendSignMethod:
|
||||
rootHash := in.TaprootMerkleRoot
|
||||
err = signSegWitV1KeySpend(
|
||||
in, tx, sigHashes, idx, privKey, rootHash,
|
||||
)
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unsupported signing method for "+
|
||||
"PSBT signing: %v", signMethod)
|
||||
@ -207,6 +222,60 @@ func validateSigningMethod(in *psbt.PInput) (input.SignMethod, error) {
|
||||
|
||||
return input.WitnessV0SignMethod, nil
|
||||
|
||||
case txscript.WitnessV1TaprootTy:
|
||||
if len(in.TaprootBip32Derivation) == 0 {
|
||||
return 0, fmt.Errorf("cannot sign for taproot input " +
|
||||
"without taproot BIP0032 derivation info")
|
||||
}
|
||||
|
||||
// Currently, we only support creating one signature per input.
|
||||
//
|
||||
// TODO(guggero): Should we support signing multiple paths at
|
||||
// the same time? What are the performance and security
|
||||
// implications?
|
||||
if len(in.TaprootBip32Derivation) > 1 {
|
||||
return 0, fmt.Errorf("unsupported multiple taproot " +
|
||||
"BIP0032 derivation info found, can only " +
|
||||
"sign for one at a time")
|
||||
}
|
||||
|
||||
derivation := in.TaprootBip32Derivation[0]
|
||||
switch {
|
||||
// No leaf hashes means this is the internal key we're signing
|
||||
// with, so it's a key spend. And no merkle root means this is
|
||||
// a BIP0086 output we're signing for.
|
||||
case len(derivation.LeafHashes) == 0 &&
|
||||
len(in.TaprootMerkleRoot) == 0:
|
||||
|
||||
return input.TaprootKeySpendBIP0086SignMethod, nil
|
||||
|
||||
// A non-empty merkle root means we committed to a taproot hash
|
||||
// that we need to use in the tap tweak.
|
||||
case len(derivation.LeafHashes) == 0:
|
||||
// Getting here means the merkle root isn't empty, but
|
||||
// is it exactly the length we need?
|
||||
if len(in.TaprootMerkleRoot) != sha256.Size {
|
||||
return 0, fmt.Errorf("invalid taproot merkle "+
|
||||
"root length, got %d expected %d",
|
||||
len(in.TaprootMerkleRoot), sha256.Size)
|
||||
}
|
||||
|
||||
return input.TaprootKeySpendSignMethod, nil
|
||||
|
||||
// Currently, we only support signing for one leaf at a time.
|
||||
//
|
||||
// TODO(guggero): Should we support signing multiple paths at
|
||||
// the same time? What are the performance and security
|
||||
// implications?
|
||||
case len(derivation.LeafHashes) == 1:
|
||||
return input.TaprootScriptSpendSignMethod, nil
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported number of leaf " +
|
||||
"hashes in taproot BIP0032 derivation info, " +
|
||||
"can only sign for one at a time")
|
||||
}
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported script class for signing "+
|
||||
"PSBT: %v", script.Class())
|
||||
@ -253,6 +322,27 @@ func signSegWitV0(in *psbt.PInput, tx *wire.MsgTx,
|
||||
return nil
|
||||
}
|
||||
|
||||
// signSegWitV1KeySpend attempts to generate a signature for a SegWit version 1
|
||||
// (p2tr) input and stores it in the TaprootKeySpendSig field.
|
||||
func signSegWitV1KeySpend(in *psbt.PInput, tx *wire.MsgTx,
|
||||
sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
|
||||
tapscriptRootHash []byte) error {
|
||||
|
||||
rawSig, err := txscript.RawTxInTaprootSignature(
|
||||
tx, sigHashes, idx, in.WitnessUtxo.Value,
|
||||
in.WitnessUtxo.PkScript, tapscriptRootHash, in.SighashType,
|
||||
privKey,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error signing taproot input %d: %v", idx,
|
||||
err)
|
||||
}
|
||||
|
||||
in.TaprootKeySpendSig = rawSig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareScriptsV0 returns the appropriate witness v0 and/or legacy scripts,
|
||||
// depending on the type of input that should be signed.
|
||||
func prepareScriptsV0(in *psbt.PInput) ([]byte, []byte, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user