From 1e71b1e544bedf3965a337ff29e6d33980fb9076 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:46:33 -0400 Subject: [PATCH] multi: add new tapscript root option to GenTaprootFundingScript This'll allow us to create a funding output that uses musig2, but uses a tapscript tweak rather than a normal BIP 86 tweak. --- contractcourt/chain_watcher.go | 2 +- funding/manager.go | 2 ++ graph/builder.go | 4 +++- input/script_utils.go | 24 +++++++++++++++--------- itest/lnd_funding_test.go | 2 ++ lnwallet/chanfunding/canned_assembler.go | 7 ++++--- lnwallet/channel.go | 1 + lnwallet/wallet.go | 10 ++++++++-- 8 files changed, 36 insertions(+), 16 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 3cbc7422d..155fca2a9 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -308,7 +308,7 @@ func (c *chainWatcher) Start() error { ) if chanState.ChanType.IsTaproot() { c.fundingPkScript, _, err = input.GenTaprootFundingScript( - localKey, remoteKey, 0, + localKey, remoteKey, 0, fn.None[chainhash.Hash](), ) if err != nil { return err diff --git a/funding/manager.go b/funding/manager.go index 61b03200e..ad6286090 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -2899,6 +2900,7 @@ func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { if channel.ChanType.IsTaproot() { pkScript, _, err := input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return nil, err diff --git a/graph/builder.go b/graph/builder.go index 82a36eb36..7f7823ab4 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -11,12 +11,14 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnutils" @@ -1138,7 +1140,7 @@ func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, } fundingScript, _, err := input.GenTaprootFundingScript( - pubKey1, pubKey2, 0, + pubKey1, pubKey2, 0, fn.None[chainhash.Hash](), ) if err != nil { return nil, err diff --git a/input/script_utils.go b/input/script_utils.go index d801846f8..d3a6dc9d3 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -11,8 +11,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" "golang.org/x/crypto/ripemd160" @@ -199,26 +201,30 @@ func GenFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, erro } // GenTaprootFundingScript constructs the taproot-native funding output that -// uses musig2 to create a single aggregated key to anchor the channel. +// uses MuSig2 to create a single aggregated key to anchor the channel. func GenTaprootFundingScript(aPub, bPub *btcec.PublicKey, - amt int64) ([]byte, *wire.TxOut, error) { + amt int64, tapscriptRoot fn.Option[chainhash.Hash]) ([]byte, + *wire.TxOut, error) { + + muSig2Opt := musig2.WithBIP86KeyTweak() + tapscriptRoot.WhenSome(func(scriptRoot chainhash.Hash) { + muSig2Opt = musig2.WithTaprootKeyTweak(scriptRoot[:]) + }) // Similar to the existing p2wsh funding script, we'll always make sure // we sort the keys before any major operations. In order to ensure // that there's no other way this output can be spent, we'll use a BIP - // 86 tweak here during aggregation. - // - // TODO(roasbeef): revisit if BIP 86 is needed here? + // 86 tweak here during aggregation, unless the user has explicitly + // specified a tapscript root. combinedKey, _, _, err := musig2.AggregateKeys( - []*btcec.PublicKey{aPub, bPub}, true, - musig2.WithBIP86KeyTweak(), + []*btcec.PublicKey{aPub, bPub}, true, muSig2Opt, ) if err != nil { return nil, nil, fmt.Errorf("unable to combine keys: %w", err) } // Now that we have the combined key, we can create a taproot pkScript - // from this, and then make the txout given the amount. + // from this, and then make the txOut given the amount. pkScript, err := PayToTaprootScript(combinedKey.FinalKey) if err != nil { return nil, nil, fmt.Errorf("unable to make taproot "+ @@ -228,7 +234,7 @@ func GenTaprootFundingScript(aPub, bPub *btcec.PublicKey, txOut := wire.NewTxOut(amt, pkScript) // For the "witness program" we just return the raw pkScript since the - // output we create can _only_ be spent with a musig2 signature. + // output we create can _only_ be spent with a MuSig2 signature. return pkScript, txOut, nil } diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index 6e2f0070c..a1c2e292d 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainreg" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/labels" @@ -1192,6 +1193,7 @@ func deriveFundingShim(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, _, fundingOutput, err = input.GenTaprootFundingScript( carolKey, daveKey, int64(chanSize), + fn.None[chainhash.Hash](), ) require.NoError(ht, err) diff --git a/lnwallet/chanfunding/canned_assembler.go b/lnwallet/chanfunding/canned_assembler.go index 21dd47339..02dd7169d 100644 --- a/lnwallet/chanfunding/canned_assembler.go +++ b/lnwallet/chanfunding/canned_assembler.go @@ -5,7 +5,9 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" ) @@ -76,9 +78,8 @@ func (s *ShimIntent) FundingOutput() ([]byte, *wire.TxOut, error) { // Similar to the existing p2wsh script, we'll always ensure // the keys are sorted before use. return input.GenTaprootFundingScript( - s.localKey.PubKey, - s.remoteKey, - int64(totalAmt), + s.localKey.PubKey, s.remoteKey, int64(totalAmt), + fn.None[chainhash.Hash](), ) } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 59d9921a7..b77a4a76e 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1013,6 +1013,7 @@ func (lc *LightningChannel) createSignDesc() error { if chanState.ChanType.IsTaproot() { fundingPkScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(lc.channelState.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return err diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 90a0bea2d..bcba419f4 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -23,6 +23,7 @@ import ( "github.com/btcsuite/btcwallet/wallet" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -2102,6 +2103,7 @@ func (l *LightningWallet) verifyCommitSig(res *ChannelReservation, if res.musigSessions == nil { _, fundingOutput, err := input.GenTaprootFundingScript( localKey, remoteKey, channelValue, + fn.None[chainhash.Hash](), ) if err != nil { return err @@ -2341,11 +2343,14 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { fundingTxOut *wire.TxOut ) if chanType.IsTaproot() { - fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( //nolint:lll + //nolint:lll + fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( ourKey.PubKey, theirKey.PubKey, channelValue, + fn.None[chainhash.Hash](), ) } else { - fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( //nolint:lll + //nolint:lll + fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( ourKey.PubKey.SerializeCompressed(), theirKey.PubKey.SerializeCompressed(), channelValue, ) @@ -2482,6 +2487,7 @@ func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, if channelState.ChanType.IsTaproot() { fundingScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return err