btcwallet: support taproot script signing in PSBT

This commit is contained in:
Oliver Gugger 2022-04-27 21:05:47 +02:00
parent 0f32a4456d
commit 35dd2d25ea
No known key found for this signature in database
GPG key ID: 8E4256593F177720

View file

@ -6,6 +6,7 @@ import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
@ -194,6 +195,17 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
in, tx, sigHashes, idx, privKey, rootHash,
)
// For p2tr script spend path.
case input.TaprootScriptSpendSignMethod:
leafScript := in.TaprootLeafScript[0]
leaf := txscript.TapLeaf{
LeafVersion: leafScript.LeafVersion,
Script: leafScript.Script,
}
err = signSegWitV1ScriptSpend(
in, tx, sigHashes, idx, privKey, leaf,
)
default:
err = fmt.Errorf("unsupported signing method for "+
"PSBT signing: %v", signMethod)
@ -268,6 +280,28 @@ func validateSigningMethod(in *psbt.PInput) (input.SignMethod, error) {
// the same time? What are the performance and security
// implications?
case len(derivation.LeafHashes) == 1:
// If we're supposed to be signing for a leaf hash, we
// also expect the leaf script that hashes to that hash
// in the appropriate field.
if len(in.TaprootLeafScript) != 1 {
return 0, fmt.Errorf("specified leaf hash in " +
"taproot BIP0032 derivation but " +
"missing taproot leaf script")
}
leafScript := in.TaprootLeafScript[0]
leaf := txscript.TapLeaf{
LeafVersion: leafScript.LeafVersion,
Script: leafScript.Script,
}
leafHash := leaf.TapHash()
if !bytes.Equal(leafHash[:], derivation.LeafHashes[0]) {
return 0, fmt.Errorf("specified leaf hash in" +
"taproot BIP0032 derivation but " +
"corresponding taproot leaf script " +
"was not found")
}
return input.TaprootScriptSpendSignMethod, nil
default:
@ -343,6 +377,36 @@ func signSegWitV1KeySpend(in *psbt.PInput, tx *wire.MsgTx,
return nil
}
// signSegWitV1ScriptSpend attempts to generate a signature for a SegWit version
// 1 (p2tr) input and stores it in the TaprootScriptSpendSig field.
func signSegWitV1ScriptSpend(in *psbt.PInput, tx *wire.MsgTx,
sigHashes *txscript.TxSigHashes, idx int, privKey *btcec.PrivateKey,
leaf txscript.TapLeaf) error {
rawSig, err := txscript.RawTxInTapscriptSignature(
tx, sigHashes, idx, in.WitnessUtxo.Value,
in.WitnessUtxo.PkScript, leaf, in.SighashType, privKey,
)
if err != nil {
return fmt.Errorf("error signing taproot script input %d: %v",
idx, err)
}
leafHash := leaf.TapHash()
in.TaprootScriptSpendSig = append(
in.TaprootScriptSpendSig, &psbt.TaprootScriptSpendSig{
XOnlyPubKey: in.TaprootBip32Derivation[0].XOnlyPubKey,
LeafHash: leafHash[:],
// We snip off the sighash flag from the end (if it was
// specified in the first place.)
Signature: rawSig[:schnorr.SignatureSize],
SigHash: in.SighashType,
},
)
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) {