From 6e965989c802a85cdb884cdd06966c7d1b19b461 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 14 Feb 2022 15:23:06 +0100 Subject: [PATCH] txauthor: add taproot address type --- wallet/txauthor/author.go | 74 +++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/wallet/txauthor/author.go b/wallet/txauthor/author.go index 890fa41757..88351298dc 100644 --- a/wallet/txauthor/author.go +++ b/wallet/txauthor/author.go @@ -8,6 +8,7 @@ package txauthor import ( "errors" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" @@ -229,19 +230,32 @@ func AddAllInputScripts(tx *wire.MsgTx, prevPkScripts [][]byte, // function which generates both the sigScript, and the witness // script. case txscript.IsPayToScriptHash(pkScript): - err := spendNestedWitnessPubKeyHash(inputs[i], pkScript, - int64(inputValues[i]), chainParams, secrets, - tx, hashCache, i) + err := spendNestedWitnessPubKeyHash( + inputs[i], pkScript, int64(inputValues[i]), + chainParams, secrets, tx, hashCache, i, + ) if err != nil { return err } + case txscript.IsPayToWitnessPubKeyHash(pkScript): - err := spendWitnessKeyHash(inputs[i], pkScript, - int64(inputValues[i]), chainParams, secrets, - tx, hashCache, i) + err := spendWitnessKeyHash( + inputs[i], pkScript, int64(inputValues[i]), + chainParams, secrets, tx, hashCache, i, + ) + if err != nil { + return err + } + + case txscript.IsPayToTaproot(pkScript): + err := spendTaprootKeyHash( + inputs[i], pkScript, int64(inputValues[i]), + chainParams, secrets, tx, hashCache, i, + ) if err != nil { return err } + default: sigScript := inputs[i].SignatureScript script, err := txscript.SignTxOutput(chainParams, tx, i, @@ -309,6 +323,54 @@ func spendWitnessKeyHash(txIn *wire.TxIn, pkScript []byte, return nil } +// spendTaprootKey generates, and sets a valid witness for spending the passed +// pkScript with the specified input amount. The input amount *must* +// correspond to the output value of the previous pkScript, or else verification +// will fail since the new sighash digest algorithm defined in BIP0341 includes +// the input value in the sighash. +func spendTaprootKey(txIn *wire.TxIn, pkScript []byte, + inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource, + tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error { + + // First obtain the key pair associated with this p2tr address. + _, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript, chainParams) + if err != nil { + return err + } + privKey, _, err := secrets.GetKey(addrs[0]) + if err != nil { + return err + } + pubKey := privKey.PubKey() + + tapKey := txscript.ComputeTaprootKeyNoScript(pubKey) + p2trAddr, err := btcutil.NewAddressTaproot( + schnorr.SerializePubKey(tapKey), chainParams, + ) + if err != nil { + return err + } + + // With the concrete address type, we can now generate the + // corresponding witness program to be used to generate a valid witness + // which will allow us to spend this output. + witnessProgram, err := txscript.PayToAddrScript(p2trAddr) + if err != nil { + return err + } + witnessScript, err := txscript.TaprootWitnessSignature( + tx, hashCache, idx, inputValue, witnessProgram, + txscript.SigHashDefault, privKey, + ) + if err != nil { + return err + } + + txIn.Witness = witnessScript + + return nil +} + // spendNestedWitnessPubKey generates both a sigScript, and valid witness for // spending the passed pkScript with the specified input amount. The generated // sigScript is the version 0 p2wkh witness program corresponding to the queried