mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
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:
parent
9e8b00241f
commit
67ecefaac3
@ -2586,7 +2586,6 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog,
|
||||
|
||||
// Read the amounts from the breach transaction.
|
||||
theirAmt = spendTx.TxOut[theirOutpoint.Index].Value
|
||||
|
||||
} else {
|
||||
// Otherwise, we check to see if the revocation log
|
||||
// contains remote parties' output amount. Due to a
|
||||
|
@ -2,12 +2,15 @@ package lnwallet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
@ -545,3 +548,27 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ const (
|
||||
|
||||
// CommitmentTypeSimpleTaproot is the base commitment type for the
|
||||
// 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
|
||||
)
|
||||
|
||||
|
@ -8,17 +8,19 @@ import (
|
||||
)
|
||||
|
||||
// nextRevocationProducer creates a new revocation producer, deriving the
|
||||
// revocation root by applying ECDH to a new key from our revocation root family
|
||||
// and the multisig key we use for the channel.
|
||||
// revocation root by applying ECDH to a new key from our revocation root
|
||||
// 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,
|
||||
keyRing keychain.KeyRing) (shachain.Producer, error) {
|
||||
keyRing keychain.KeyRing,
|
||||
) (shachain.Producer, shachain.Producer, error) {
|
||||
|
||||
// Derive the next key in the revocation root family.
|
||||
nextRevocationKeyDesc, err := keyRing.DeriveNextKey(
|
||||
keychain.KeyFamilyRevocationRoot,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 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,
|
||||
)
|
||||
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,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Once we have the root, we can then generate our shachain producer
|
||||
// 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
|
||||
}
|
||||
|
@ -12,14 +12,15 @@ import (
|
||||
// revocation root by applying ECDH to a new key from our revocation root family
|
||||
// and the multisig key we use for the channel.
|
||||
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.
|
||||
nextRevocationKeyDesc, err := keyRing.DeriveNextKey(
|
||||
keychain.KeyFamilyRevocationRoot,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
revocationRoot, err := l.DerivePrivKey(nextRevocationKeyDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Once we have the root, we can then generate our shachain
|
||||
// producer and from that generate the per-commitment point.
|
||||
revRoot, err := chainhash.NewHash(revocationRoot.Serialize())
|
||||
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
|
||||
@ -55,7 +62,7 @@ func (l *LightningWallet) nextRevocationProducer(res *ChannelReservation,
|
||||
keychain.KeyFamilyRevocationRoot,
|
||||
)
|
||||
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,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Once we have the root, we can then generate our shachain producer
|
||||
// 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
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||
"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
|
||||
// revocation tree state, and from that generate the per-commitment point.
|
||||
producer, err := l.nextRevocationProducer(reservation, keyRing)
|
||||
// revocation tree state, and from that generate the per-commitment
|
||||
// point.
|
||||
producer, taprootNonceProducer, err := l.nextRevocationProducer(
|
||||
reservation, keyRing,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1347,6 +1351,34 @@ func (l *LightningWallet) initOurContribution(reservation *ChannelReservation,
|
||||
reservation.partialState.RevocationProducer = producer
|
||||
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
|
||||
}
|
||||
|
||||
@ -1603,8 +1635,94 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleChanPointReady continues the funding process once the channel point
|
||||
// is known and the funding transaction can be completed.
|
||||
// genMusigSession generates a new musig2 pair session that we can use to sign
|
||||
// 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) {
|
||||
l.limboMtx.Lock()
|
||||
pendingReservation, ok := l.fundingLimbo[req.pendingFundingID]
|
||||
@ -1614,6 +1732,7 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
|
||||
"funding state")
|
||||
return
|
||||
}
|
||||
|
||||
ourContribution := pendingReservation.ourContribution
|
||||
theirContribution := pendingReservation.theirContribution
|
||||
chanPoint := pendingReservation.partialState.FundingOutpoint
|
||||
@ -1754,26 +1873,22 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
|
||||
fundingIntent := pendingReservation.fundingIntent
|
||||
fundingWitnessScript, fundingOutput, err := fundingIntent.FundingOutput()
|
||||
if err != nil {
|
||||
req.err <- fmt.Errorf("unable to obtain funding output")
|
||||
req.err <- fmt.Errorf("unable to obtain funding "+
|
||||
"output: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a signature for their version of the initial commitment
|
||||
// transaction.
|
||||
ourKey := ourContribution.MultiSigKey
|
||||
signDesc := input.SignDescriptor{
|
||||
WitnessScript: fundingWitnessScript,
|
||||
KeyDesc: ourKey,
|
||||
Output: fundingOutput,
|
||||
HashType: txscript.SigHashAll,
|
||||
SigHashes: input.NewTxSigHashesV0Only(theirCommitTx),
|
||||
InputIndex: 0,
|
||||
}
|
||||
sigTheirCommit, err := l.Cfg.Signer.SignOutputRaw(theirCommitTx, &signDesc)
|
||||
sigTheirCommit, err := l.signCommitTx(
|
||||
pendingReservation, theirCommitTx, fundingOutput,
|
||||
fundingWitnessScript,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
return
|
||||
}
|
||||
|
||||
pendingReservation.ourCommitmentSig = sigTheirCommit
|
||||
|
||||
req.err <- nil
|
||||
@ -1898,6 +2013,89 @@ func (l *LightningWallet) verifyFundingInputs(fundingTx *wire.MsgTx,
|
||||
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
|
||||
// workflow. During this step, we validate *all* the received signatures for
|
||||
// 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.
|
||||
res.theirCommitmentSig = msg.theirCommitmentSig
|
||||
commitTx := res.partialState.LocalCommitment.CommitTx
|
||||
ourKey := res.ourContribution.MultiSigKey
|
||||
theirKey := res.theirContribution.MultiSigKey
|
||||
|
||||
// Re-generate both the witnessScript and p2sh output. We sign the
|
||||
// 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),
|
||||
)
|
||||
err := l.verifyCommitSig(res, msg.theirCommitmentSig, commitTx)
|
||||
if err != nil {
|
||||
msg.err <- err
|
||||
msg.err <- fmt.Errorf("counterparty's commitment signature is "+
|
||||
"invalid: %w", err)
|
||||
msg.completeChan <- nil
|
||||
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()
|
||||
res.partialState.LocalCommitment.CommitSig = theirCommitSigBytes
|
||||
|
||||
@ -2056,6 +2225,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
defer pendingReservation.Unlock()
|
||||
|
||||
chanState := pendingReservation.partialState
|
||||
chanType := pendingReservation.partialState.ChanType
|
||||
chanState.FundingOutpoint = *req.fundingOutpoint
|
||||
fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil)
|
||||
|
||||
@ -2074,7 +2244,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
pendingReservation.theirContribution.ChannelConfig,
|
||||
pendingReservation.ourContribution.FirstCommitmentPoint,
|
||||
pendingReservation.theirContribution.FirstCommitmentPoint,
|
||||
*fundingTxIn, pendingReservation.partialState.ChanType,
|
||||
*fundingTxIn, chanType,
|
||||
pendingReservation.partialState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
@ -2110,13 +2280,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
walletLog.Debugf("Remote commit tx for ChannelPoint(%v): %v",
|
||||
req.fundingOutpoint, spew.Sdump(theirCommitTx))
|
||||
|
||||
channelValue := int64(pendingReservation.partialState.Capacity)
|
||||
hashCache := input.NewTxSigHashesV0Only(ourCommitTx)
|
||||
theirKey := pendingReservation.theirContribution.MultiSigKey
|
||||
ourKey := pendingReservation.ourContribution.MultiSigKey
|
||||
witnessScript, _, err := input.GenFundingPkScript(
|
||||
ourKey.PubKey.SerializeCompressed(),
|
||||
theirKey.PubKey.SerializeCompressed(), channelValue,
|
||||
// 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
|
||||
@ -2124,53 +2291,46 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
||||
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
|
||||
|
||||
channelValue := int64(pendingReservation.partialState.Capacity)
|
||||
theirKey := pendingReservation.theirContribution.MultiSigKey
|
||||
ourKey := pendingReservation.ourContribution.MultiSigKey
|
||||
|
||||
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(),
|
||||
theirKey.PubKey.SerializeCompressed(), channelValue,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.completeChan <- nil
|
||||
return
|
||||
}
|
||||
|
||||
// With their signature for our version of the commitment transactions
|
||||
// verified, we can now generate a signature for their version,
|
||||
// allowing the funding transaction to be safely broadcast.
|
||||
p2wsh, err := input.WitnessScriptHash(witnessScript)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
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)
|
||||
sigTheirCommit, err := l.signCommitTx(
|
||||
pendingReservation, theirCommitTx, fundingTxOut,
|
||||
fundingWitnessScript,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
req.completeChan <- nil
|
||||
return
|
||||
}
|
||||
|
||||
pendingReservation.ourCommitmentSig = sigTheirCommit
|
||||
|
||||
_, bestHeight, err := l.Cfg.ChainIO.GetBestBlock()
|
||||
|
Loading…
Reference in New Issue
Block a user