lnwallet: integrate new taproot channels into internal funding flow

In this commit, we build on all the prior commits and integrate the new
taproot channels into the existing internal funding flow. Along the way,
we do some refactoring to unify things like signing and verifying
incoming commitment transaction signatures.

For our local nonce, we use the existing functional option type to
derive the nonce based on the initial shachain pre-image we'll use as
our revocation.
This commit is contained in:
Olaoluwa Osuntokun committed 2023-08-22 16:31:07 -07:00
1 parent 9e8b00241f
commit 67ecefaac3
6 files changed
+313 -106

No files matched your search

-1
View File
@@ -2586,7 +2586,6 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog,
// Read the amounts from the breach transaction. // Read the amounts from the breach transaction.
theirAmt = spendTx.TxOut[theirOutpoint.Index].Value theirAmt = spendTx.TxOut[theirOutpoint.Index].Value
} else { } else {
// Otherwise, we check to see if the revocation log // Otherwise, we check to see if the revocation log
// contains remote parties' output amount. Due to a // contains remote parties' output amount. Due to a
+27
View File
@@ -2,12 +2,15 @@ package lnwallet
import ( import (
"bytes" "bytes"
"crypto/hmac"
"crypto/sha256"
"fmt" "fmt"
"io" "io"
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
@@ -545,3 +548,27 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
signer: cfg.Signer, signer: cfg.Signer,
} }
} }
var (
// taprootRevRootKey is the key used to derive the revocation root for
// the taproot nonces. This is done via HMAC of the existing revocation
// root.
taprootRevRootKey = []byte("taproot-rev-root")
)
// deriveMusig2Shachain derives a shachain producer for the taproot channel
// from normal shachain revocation root.
func deriveMusig2Shachain(revRoot chainhash.Hash) (shachain.Producer, error) {
// For taproot channel types, we'll also generate a distinct shachain
// root using the same seed information. We'll use this to generate
// verification nonces for the channel. We'll bind with this a simple
// hmac.
taprootRevHmac := hmac.New(sha256.New, taprootRevRootKey)
taprootRevRoot := taprootRevHmac.Sum(nil)
// Once we have the root, we can then generate our shachain producer
// and from that generate the per-commitment point.
return shachain.NewRevocationProducerFromBytes(
taprootRevRoot,
)
}
+1 -1
View File
@@ -46,7 +46,7 @@ const (
// CommitmentTypeSimpleTaproot is the base commitment type for the // CommitmentTypeSimpleTaproot is the base commitment type for the
// channels that use a musig2 funding output and the tapscript tree // channels that use a musig2 funding output and the tapscript tree
// where relevant for the commitment transaciton pk scripts. // where relevant for the commitment transaction pk scripts.
CommitmentTypeSimpleTaproot CommitmentTypeSimpleTaproot
) )
+15 -7
View File
@@ -8,17 +8,19 @@ import (
) )
// nextRevocationProducer creates a new revocation producer, deriving the // nextRevocationProducer creates a new revocation producer, deriving the
// revocation root by applying ECDH to a new key from our revocation root family // revocation root by applying ECDH to a new key from our revocation root
// and the multisig key we use for the channel. // family and the multisig key we use for the channel. For taproot channels a
// related shachain revocation root is also returned.
func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation, func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
keyRing keychain.KeyRing) (shachain.Producer, error) { keyRing keychain.KeyRing,
) (shachain.Producer, shachain.Producer, error) {
// Derive the next key in the revocation root family. // Derive the next key in the revocation root family.
nextRevocationKeyDesc, err := keyRing.DeriveNextKey( nextRevocationKeyDesc, err := keyRing.DeriveNextKey(
keychain.KeyFamilyRevocationRoot, keychain.KeyFamilyRevocationRoot,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// If the DeriveNextKey call returns the first key with Index 0, we need // If the DeriveNextKey call returns the first key with Index 0, we need
@@ -29,7 +31,7 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
keychain.KeyFamilyRevocationRoot, keychain.KeyFamilyRevocationRoot,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
} }
@@ -42,10 +44,16 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
nextRevocationKeyDesc, res.ourContribution.MultiSigKey.PubKey, nextRevocationKeyDesc, res.ourContribution.MultiSigKey.PubKey,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Once we have the root, we can then generate our shachain producer // Once we have the root, we can then generate our shachain producer
// and from that generate the per-commitment point. // and from that generate the per-commitment point.
return shachain.NewRevocationProducer(revRoot), nil shaChainRoot := shachain.NewRevocationProducer(revRoot)
taprootShaChainRoot, err := deriveMusig2Shachain(revRoot)
if err != nil {
return nil, nil, err
}
return shaChainRoot, taprootShaChainRoot, nil
} }
+21 -8
View File
@@ -12,14 +12,15 @@ import (
// revocation root by applying ECDH to a new key from our revocation root family // revocation root by applying ECDH to a new key from our revocation root family
// and the multisig key we use for the channel. // and the multisig key we use for the channel.
func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation, func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
keyRing keychain.KeyRing) (shachain.Producer, error) { keyRing keychain.KeyRing,
) (shachain.Producer, shachain.Producer, error) {
// Derive the next key in the revocation root family. // Derive the next key in the revocation root family.
nextRevocationKeyDesc, err := keyRing.DeriveNextKey( nextRevocationKeyDesc, err := keyRing.DeriveNextKey(
keychain.KeyFamilyRevocationRoot, keychain.KeyFamilyRevocationRoot,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Within our itests, we want to make sure we can still restore channel // Within our itests, we want to make sure we can still restore channel
@@ -34,17 +35,23 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
if res.pendingChanID == itestLegacyFormatChanID { if res.pendingChanID == itestLegacyFormatChanID {
revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc) revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Once we have the root, we can then generate our shachain // Once we have the root, we can then generate our shachain
// producer and from that generate the per-commitment point. // producer and from that generate the per-commitment point.
revRoot, err := chainhash.NewHash(revocationRoot.Serialize()) revRoot, err := chainhash.NewHash(revocationRoot.Serialize())
if err != nil { if err != nil {
return nil, err return nil, nil, err
}
shaChainRoot := shachain.NewRevocationProducer(*revRoot)
taprootShaChainRoot, err := deriveMusig2Shachain(*revRoot)
if err != nil {
return nil, nil, err
} }
return shachain.NewRevocationProducer(*revRoot), nil return shaChainRoot, taprootShaChainRoot, nil
} }
// If the DeriveNextKey call returns the first key with Index 0, we need // If the DeriveNextKey call returns the first key with Index 0, we need
@@ -55,7 +62,7 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
keychain.KeyFamilyRevocationRoot, keychain.KeyFamilyRevocationRoot,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
} }
@@ -68,10 +75,16 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
nextRevocationKeyDesc, res.ourContribution.MultiSigKey.PubKey, nextRevocationKeyDesc, res.ourContribution.MultiSigKey.PubKey,
) )
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Once we have the root, we can then generate our shachain producer // Once we have the root, we can then generate our shachain producer
// and from that generate the per-commitment point. // and from that generate the per-commitment point.
return shachain.NewRevocationProducer(revRoot), nil shaChainRoot := shachain.NewRevocationProducer(revRoot)
taprootShaChainRoot, err := deriveMusig2Shachain(revRoot)
if err != nil {
return nil, nil, err
}
return shaChainRoot, taprootShaChainRoot, nil
} }
+249 -89
View File
@@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/btcutil/txsort" "github.com/btcsuite/btcd/btcutil/txsort"
@@ -1330,8 +1331,11 @@ func (l *LightningWallet) initOurContribution(reservation *ChannelReservation,
} }
// With the above keys created, we'll also need to initialize our // With the above keys created, we'll also need to initialize our
// revocation tree state, and from that generate the per-commitment point. // revocation tree state, and from that generate the per-commitment
producer, err := l.nextRevocationProducer(reservation, keyRing) // point.
producer, taprootNonceProducer, err := l.nextRevocationProducer(
reservation, keyRing,
)
if err != nil { if err != nil {
return err return err
} }
@@ -1347,6 +1351,34 @@ func (l *LightningWallet) initOurContribution(reservation *ChannelReservation,
reservation.partialState.RevocationProducer = producer reservation.partialState.RevocationProducer = producer
reservation.ourContribution.ChannelConstraints = l.Cfg.DefaultConstraints reservation.ourContribution.ChannelConstraints = l.Cfg.DefaultConstraints
// If taproot channels are active, then we'll generate our verification
// nonce here. We'll use this nonce to verify the signature for our
// local commitment transaction. If we need to force close, then this
// is also what'll be used to sign that transaction.
if reservation.partialState.ChanType.IsTaproot() {
firstNoncePreimage, err := taprootNonceProducer.AtIndex(0)
if err != nil {
return err
}
// As we'd like the local nonce we send over to be generated
// deterministically, we'll provide a custom reader that
// actually just uses our sha-chain pre-image as the primary
// randomness source.
shaChainRand := musig2.WithCustomRand(
bytes.NewBuffer(firstNoncePreimage[:]),
)
pubKeyOpt := musig2.WithPublicKey(
reservation.ourContribution.MultiSigKey.PubKey,
)
reservation.ourContribution.LocalNonce, err = musig2.GenNonces(
pubKeyOpt, shaChainRand,
)
if err != nil {
return err
}
}
return nil return nil
} }
@@ -1603,8 +1635,94 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
}) })
} }
// handleChanPointReady continues the funding process once the channel point // genMusigSession generates a new musig2 pair session that we can use to sign
// is known and the funding transaction can be completed. // the commitment transaction for the remote party, and verify their incoming
// partial signature.
func genMusigSession(ourContribution, theirContribution *ChannelContribution,
signer input.MuSig2Signer,
fundingOutput *wire.TxOut) *MusigPairSession {
return NewMusigPairSession(&MusigSessionCfg{
LocalKey: ourContribution.MultiSigKey,
RemoteKey: theirContribution.MultiSigKey,
LocalNonce: *ourContribution.LocalNonce,
RemoteNonce: *theirContribution.LocalNonce,
Signer: signer,
InputTxOut: fundingOutput,
})
}
// signCommitTx generates a valid input.Signature to send to the remote party
// for their version of the commitment transaction. For regular channels, this
// will be a normal ECDSA signature. For taproot channels, this will instead be
// a musig2 partial signature that also includes the nonce used to generate it.
func (l *LightningWallet) signCommitTx(pendingReservation *ChannelReservation,
commitTx *wire.MsgTx, fundingOutput *wire.TxOut,
fundingWitnessScript []byte) (input.Signature, error) {
ourContribution := pendingReservation.ourContribution
theirContribution := pendingReservation.theirContribution
var (
sigTheirCommit input.Signature
err error
)
switch {
// For regular channels, we can just send over a normal ECDSA signature
// w/o any extra steps.
case !pendingReservation.partialState.ChanType.IsTaproot():
ourKey := ourContribution.MultiSigKey
signDesc := input.SignDescriptor{
WitnessScript: fundingWitnessScript,
KeyDesc: ourKey,
Output: fundingOutput,
HashType: txscript.SigHashAll,
SigHashes: input.NewTxSigHashesV0Only(
commitTx,
),
InputIndex: 0,
}
sigTheirCommit, err = l.Cfg.Signer.SignOutputRaw(
commitTx, &signDesc,
)
if err != nil {
return nil, err
}
// If this is a taproot channel, then we'll need to create an initial
// musig2 session here as we'll be sending over a _partial_ signature.
default:
// We're now ready to sign the first commitment. However, we'll
// only create the session if that hasn't been done already.
if pendingReservation.musigSessions == nil {
musigSessions := genMusigSession(
ourContribution, theirContribution,
l.Cfg.Signer, fundingOutput,
)
pendingReservation.musigSessions = musigSessions
}
// Now that we have the funding outpoint, we'll generate a
// musig2 signature for their version of the commitment
// transaction. We use the remote session as this is for the
// remote commitment transaction.
musigSessions := pendingReservation.musigSessions
partialSig, err := musigSessions.RemoteSession.SignCommit(
commitTx,
)
if err != nil {
return nil, fmt.Errorf("unable to sign "+
"commitment: %w", err)
}
sigTheirCommit = partialSig
}
return sigTheirCommit, nil
}
// handleChanPointReady continues the funding process once the channel point is
// known and the funding transaction can be completed.
func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
l.limboMtx.Lock() l.limboMtx.Lock()
pendingReservation, ok := l.fundingLimbo[req.pendingFundingID] pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
@@ -1614,6 +1732,7 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
"funding state") "funding state")
return return
} }
ourContribution := pendingReservation.ourContribution ourContribution := pendingReservation.ourContribution
theirContribution := pendingReservation.theirContribution theirContribution := pendingReservation.theirContribution
chanPoint := pendingReservation.partialState.FundingOutpoint chanPoint := pendingReservation.partialState.FundingOutpoint
@@ -1754,26 +1873,22 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
fundingIntent := pendingReservation.fundingIntent fundingIntent := pendingReservation.fundingIntent
fundingWitnessScript, fundingOutput, err := fundingIntent.FundingOutput() fundingWitnessScript, fundingOutput, err := fundingIntent.FundingOutput()
if err != nil { if err != nil {
req.err <- fmt.Errorf("unable to obtain funding output") req.err <- fmt.Errorf("unable to obtain funding "+
"output: %w", err)
return return
} }
// Generate a signature for their version of the initial commitment // Generate a signature for their version of the initial commitment
// transaction. // transaction.
ourKey := ourContribution.MultiSigKey sigTheirCommit, err := l.signCommitTx(
signDesc := input.SignDescriptor{ pendingReservation, theirCommitTx, fundingOutput,
WitnessScript: fundingWitnessScript, fundingWitnessScript,
KeyDesc: ourKey, )
Output: fundingOutput,
HashType: txscript.SigHashAll,
SigHashes: input.NewTxSigHashesV0Only(theirCommitTx),
InputIndex: 0,
}
sigTheirCommit, err := l.Cfg.Signer.SignOutputRaw(theirCommitTx, &signDesc)
if err != nil { if err != nil {
req.err <- err req.err <- err
return return
} }
pendingReservation.ourCommitmentSig = sigTheirCommit pendingReservation.ourCommitmentSig = sigTheirCommit
req.err <- nil req.err <- nil
@@ -1898,6 +2013,89 @@ func (l *LightningWallet) verifyFundingInputs(fundingTx *wire.MsgTx,
return nil return nil
} }
// verifyCommitSig verifies an incoming signature for our version of the
// commitment transaction. For normal channels, this will verify that the ECDSA
// signature is valid. For taproot channels, we'll verify that their partial
// signature is valid, so it can properly be combined with our eventual
// signature when we need to broadcast.
func (l *LightningWallet) verifyCommitSig(res *ChannelReservation,
commitSig input.Signature, commitTx *wire.MsgTx) error {
localKey := res.ourContribution.MultiSigKey.PubKey
remoteKey := res.theirContribution.MultiSigKey.PubKey
channelValue := int64(res.partialState.Capacity)
switch {
// If this isn't a taproot channel, then we'll construct a segwit v0
// p2wsh sighash.
case !res.partialState.ChanType.IsTaproot():
hashCache := input.NewTxSigHashesV0Only(commitTx)
witnessScript, _, err := input.GenFundingPkScript(
localKey.SerializeCompressed(),
remoteKey.SerializeCompressed(), channelValue,
)
if err != nil {
return err
}
sigHash, err := txscript.CalcWitnessSigHash(
witnessScript, hashCache, txscript.SigHashAll,
commitTx, 0, channelValue,
)
if err != nil {
return err
}
// Verify that we've received a valid signature from the remote
// party for our version of the commitment transaction.
if !commitSig.Verify(sigHash, remoteKey) {
return fmt.Errorf("counterparty's commitment " +
"signature is invalid")
}
return nil
// Otherwise for taproot channels, we'll compute the segwit v1 sighash,
// which is slightly different.
default:
// First, check to see if we've generated the musig session
// already. If we're the responder in the funding flow, we may
// not have generated it already.
if res.musigSessions == nil {
_, fundingOutput, err := input.GenTaprootFundingScript(
localKey, remoteKey, channelValue,
)
if err != nil {
return err
}
res.musigSessions = genMusigSession(
res.ourContribution, res.theirContribution,
l.Cfg.Signer, fundingOutput,
)
}
// For the musig2 based channels, we'll use the generated local
// musig2 session to verify the signature.
localSession := res.musigSessions.LocalSession
// At this point, the commitment signature passed in should
// actually be a wrapped musig2 signature, so we'll do a type
// asset to the get the signature we actually need.
partialSig, ok := commitSig.(*MusigPartialSig)
if !ok {
return fmt.Errorf("expected *musig2.PartialSignature, "+
"got: %T", commitSig)
}
_, err := localSession.VerifyCommitSig(
commitTx, partialSig.ToWireSig(),
)
return err
}
}
// handleFundingCounterPartySigs is the final step in the channel reservation // handleFundingCounterPartySigs is the final step in the channel reservation
// workflow. During this step, we validate *all* the received signatures for // workflow. During this step, we validate *all* the received signatures for
// inputs to the funding transaction. If any of these are invalid, we bail, // inputs to the funding transaction. If any of these are invalid, we bail,
@@ -1940,44 +2138,15 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
// commitment transaction. // commitment transaction.
res.theirCommitmentSig = msg.theirCommitmentSig res.theirCommitmentSig = msg.theirCommitmentSig
commitTx := res.partialState.LocalCommitment.CommitTx commitTx := res.partialState.LocalCommitment.CommitTx
ourKey := res.ourContribution.MultiSigKey
theirKey := res.theirContribution.MultiSigKey
// Re-generate both the witnessScript and p2sh output. We sign the err := l.verifyCommitSig(res, msg.theirCommitmentSig, commitTx)
// witnessScript script, but include the p2sh output as the subscript
// for verification.
witnessScript, _, err := input.GenFundingPkScript(
ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(),
int64(res.partialState.Capacity),
)
if err != nil { if err != nil {
msg.err <- err msg.err <- fmt.Errorf("counterparty's commitment signature is "+
"invalid: %w", err)
msg.completeChan <- nil msg.completeChan <- nil
return return
} }
// Next, create the spending scriptSig, and then verify that the script
// is complete, allowing us to spend from the funding transaction.
channelValue := int64(res.partialState.Capacity)
hashCache := input.NewTxSigHashesV0Only(commitTx)
sigHash, err := txscript.CalcWitnessSigHash(
witnessScript, hashCache, txscript.SigHashAll, commitTx,
0, channelValue,
)
if err != nil {
msg.err <- err
msg.completeChan <- nil
return
}
// Verify that we've received a valid signature from the remote party
// for our version of the commitment transaction.
if !msg.theirCommitmentSig.Verify(sigHash, theirKey.PubKey) {
msg.err <- fmt.Errorf("counterparty's commitment signature is invalid")
msg.completeChan <- nil
return
}
theirCommitSigBytes := msg.theirCommitmentSig.Serialize() theirCommitSigBytes := msg.theirCommitmentSig.Serialize()
res.partialState.LocalCommitment.CommitSig = theirCommitSigBytes res.partialState.LocalCommitment.CommitSig = theirCommitSigBytes
@@ -2056,6 +2225,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
defer pendingReservation.Unlock() defer pendingReservation.Unlock()
chanState := pendingReservation.partialState chanState := pendingReservation.partialState
chanType := pendingReservation.partialState.ChanType
chanState.FundingOutpoint = *req.fundingOutpoint chanState.FundingOutpoint = *req.fundingOutpoint
fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil) fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil)
@@ -2074,7 +2244,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
pendingReservation.theirContribution.ChannelConfig, pendingReservation.theirContribution.ChannelConfig,
pendingReservation.ourContribution.FirstCommitmentPoint, pendingReservation.ourContribution.FirstCommitmentPoint,
pendingReservation.theirContribution.FirstCommitmentPoint, pendingReservation.theirContribution.FirstCommitmentPoint,
*fundingTxIn, pendingReservation.partialState.ChanType, *fundingTxIn, chanType,
pendingReservation.partialState.IsInitiator, leaseExpiry, pendingReservation.partialState.IsInitiator, leaseExpiry,
) )
if err != nil { if err != nil {
@@ -2110,67 +2280,57 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
walletLog.Debugf("Remote commit tx for ChannelPoint(%v): %v", walletLog.Debugf("Remote commit tx for ChannelPoint(%v): %v",
req.fundingOutpoint, spew.Sdump(theirCommitTx)) req.fundingOutpoint, spew.Sdump(theirCommitTx))
// With both commitment transactions created, we'll now verify their
// signature on our commitment.
err = l.verifyCommitSig(
pendingReservation, req.theirCommitmentSig, ourCommitTx,
)
if err != nil {
req.err <- err
req.completeChan <- nil
return
}
theirCommitSigBytes := req.theirCommitmentSig.Serialize()
chanState.LocalCommitment.CommitSig = theirCommitSigBytes
channelValue := int64(pendingReservation.partialState.Capacity) channelValue := int64(pendingReservation.partialState.Capacity)
hashCache := input.NewTxSigHashesV0Only(ourCommitTx)
theirKey := pendingReservation.theirContribution.MultiSigKey theirKey := pendingReservation.theirContribution.MultiSigKey
ourKey := pendingReservation.ourContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey
witnessScript, _, err := input.GenFundingPkScript(
var (
fundingWitnessScript []byte
fundingTxOut *wire.TxOut
)
if chanType.IsTaproot() {
fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( //nolint:lll
ourKey.PubKey, theirKey.PubKey, channelValue,
)
} else {
fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( //nolint:lll
ourKey.PubKey.SerializeCompressed(), ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(), channelValue, theirKey.PubKey.SerializeCompressed(), channelValue,
) )
}
if err != nil { if err != nil {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
return return
} }
sigHash, err := txscript.CalcWitnessSigHash(
witnessScript, hashCache, txscript.SigHashAll, ourCommitTx, 0,
channelValue,
)
if err != nil {
req.err <- err
req.completeChan <- nil
return
}
// Verify that we've received a valid signature from the remote party
// for our version of the commitment transaction.
if !req.theirCommitmentSig.Verify(sigHash, theirKey.PubKey) {
req.err <- fmt.Errorf("counterparty's commitment signature " +
"is invalid")
req.completeChan <- nil
return
}
theirCommitSigBytes := req.theirCommitmentSig.Serialize()
chanState.LocalCommitment.CommitSig = theirCommitSigBytes
// With their signature for our version of the commitment transactions // With their signature for our version of the commitment transactions
// verified, we can now generate a signature for their version, // verified, we can now generate a signature for their version,
// allowing the funding transaction to be safely broadcast. // allowing the funding transaction to be safely broadcast.
p2wsh, err := input.WitnessScriptHash(witnessScript) sigTheirCommit, err := l.signCommitTx(
if err != nil { pendingReservation, theirCommitTx, fundingTxOut,
req.err <- err fundingWitnessScript,
req.completeChan <- nil )
return
}
signDesc := input.SignDescriptor{
WitnessScript: witnessScript,
KeyDesc: ourKey,
Output: &wire.TxOut{
PkScript: p2wsh,
Value: channelValue,
},
HashType: txscript.SigHashAll,
SigHashes: input.NewTxSigHashesV0Only(theirCommitTx),
InputIndex: 0,
}
sigTheirCommit, err := l.Cfg.Signer.SignOutputRaw(theirCommitTx, &signDesc)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
return return
} }
pendingReservation.ourCommitmentSig = sigTheirCommit pendingReservation.ourCommitmentSig = sigTheirCommit
_, bestHeight, err := l.Cfg.ChainIO.GetBestBlock() _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock()