• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.87
/lnwallet/musig_session.go
1
package lnwallet
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
10
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/btcsuite/btcd/txscript"
13
        "github.com/btcsuite/btcd/wire"
14
        "github.com/lightningnetwork/lnd/fn/v2"
15
        "github.com/lightningnetwork/lnd/input"
16
        "github.com/lightningnetwork/lnd/keychain"
17
        "github.com/lightningnetwork/lnd/lnwire"
18
        "github.com/lightningnetwork/lnd/shachain"
19
)
20

21
// MusigCommitType is an enum that denotes if this is the local or remote
22
// commitment.
23
type MusigCommitType uint8
24

25
const (
26
        // LocalMusigCommit denotes that this a session for the local
27
        // commitment.
28
        LocalMusigCommit MusigCommitType = iota
29

30
        // RemoteMusigCommit denotes that this is a session for the remote
31
        // commitment.
32
        RemoteMusigCommit
33
)
34

35
var (
36
        // ErrSessionNotFinalized is returned when the SignCommit method is
37
        // called for a local commitment, without the session being finalized
38
        // (missing nonce).
39
        ErrSessionNotFinalized = fmt.Errorf("musig2 session not finalized")
40
)
41

42
// tapscriptRootToSignOpt is a function that takes a tapscript root and returns
43
// a MuSig2 sign opt that'll apply the tweak when signing+verifying.
44
func tapscriptRootToSignOpt(root chainhash.Hash) musig2.SignOption {
×
45
        return musig2.WithTaprootSignTweak(root[:])
×
46
}
×
47

48
// TapscriptRootToTweak is a helper function that converts a tapscript root
49
// into a tweak that can be used with the MuSig2 API.
50
func TapscriptRootToTweak(root chainhash.Hash) input.MuSig2Tweaks {
×
51
        return input.MuSig2Tweaks{
×
52
                TaprootTweak: root[:],
×
53
        }
×
54
}
×
55

56
// MusigPartialSig is a wrapper around the base musig2.PartialSignature type
57
// that also includes information about the set of nonces used, and also the
58
// signer. This allows us to implement the input.Signature interface, as that
59
// requires the ability to perform abstract verification based on a public key.
60
type MusigPartialSig struct {
61
        // sig is the actual musig2 partial signature.
62
        sig *musig2.PartialSignature
63

64
        // signerNonce is the nonce used by the signer to generate the partial
65
        // signature.
66
        signerNonce lnwire.Musig2Nonce
67

68
        // combinedNonce is the combined nonce of all signers.
69
        combinedNonce lnwire.Musig2Nonce
70

71
        // signerKeys is the set of public keys of all signers.
72
        signerKeys []*btcec.PublicKey
73

74
        // tapscriptTweak is an optional tweak, that if specified, will be used
75
        // instead of the normal BIP 86 tweak when validating the signature.
76
        tapscriptTweak fn.Option[chainhash.Hash]
77
}
78

79
// NewMusigPartialSig creates a new MuSig2 partial signature.
80
func NewMusigPartialSig(sig *musig2.PartialSignature, signerNonce,
81
        combinedNonce lnwire.Musig2Nonce, signerKeys []*btcec.PublicKey,
82
        tapscriptTweak fn.Option[chainhash.Hash]) *MusigPartialSig {
3✔
83

3✔
84
        return &MusigPartialSig{
3✔
85
                sig:            sig,
3✔
86
                signerNonce:    signerNonce,
3✔
87
                combinedNonce:  combinedNonce,
3✔
88
                signerKeys:     signerKeys,
3✔
89
                tapscriptTweak: tapscriptTweak,
3✔
90
        }
3✔
91
}
3✔
92

93
// FromWireSig maps a wire partial sig to this internal type that we'll use to
94
// perform signature validation.
95
func (p *MusigPartialSig) FromWireSig(
96
        sig *lnwire.PartialSigWithNonce) *MusigPartialSig {
3✔
97

3✔
98
        p.sig = &musig2.PartialSignature{
3✔
99
                S: &sig.Sig,
3✔
100
        }
3✔
101
        p.signerNonce = sig.Nonce
3✔
102

3✔
103
        return p
3✔
104
}
3✔
105

106
// ToWireSig maps the partial signature to something that we can use to write
107
// out for the wire protocol.
108
func (p *MusigPartialSig) ToWireSig() *lnwire.PartialSigWithNonce {
3✔
109
        return &lnwire.PartialSigWithNonce{
3✔
110
                PartialSig: lnwire.NewPartialSig(*p.sig.S),
3✔
111
                Nonce:      p.signerNonce,
3✔
112
        }
3✔
113
}
3✔
114

115
// Serialize serializes the musig2 partial signature. The serializing includes
116
// the signer's public nonce _and_ the partial signature. The final signature
117
// is always 98 bytes in length.
118
func (p *MusigPartialSig) Serialize() []byte {
3✔
119
        var b bytes.Buffer
3✔
120

3✔
121
        _ = p.ToWireSig().Encode(&b)
3✔
122

3✔
123
        return b.Bytes()
3✔
124
}
3✔
125

126
// ToSchnorrShell converts the musig partial signature to a regular schnorr.
127
// This schnorr signature uses a zero value for the 'r' field, so we're just
128
// only using the last 32-bytes of the signature. This is useful when we need
129
// to convert an HTLC schnorr signature into something we can send using the
130
// existing messages.
131
func (p *MusigPartialSig) ToSchnorrShell() *schnorr.Signature {
×
132
        var zeroVal btcec.FieldVal
×
133
        return schnorr.NewSignature(&zeroVal, p.sig.S)
×
134
}
×
135

136
// FromSchnorrShell takes a schnorr signature and parses out the last 32 bytes
137
// as a normal musig2 partial signature.
138
func (p *MusigPartialSig) FromSchnorrShell(sig *schnorr.Signature) {
×
139
        var (
×
140
                partialS      btcec.ModNScalar
×
141
                partialSBytes [32]byte
×
142
        )
×
143
        copy(partialSBytes[:], sig.Serialize()[32:])
×
144
        partialS.SetBytes(&partialSBytes)
×
145

×
146
        p.sig = &musig2.PartialSignature{
×
147
                S: &partialS,
×
148
        }
×
149
}
×
150

151
// Verify attempts to verify the partial musig2 signature using the passed
152
// message and signer public key.
153
//
154
// NOTE: This implements the input.Signature interface.
155
func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool {
3✔
156
        var m [32]byte
3✔
157
        copy(m[:], msg)
3✔
158

3✔
159
        // If we have a tapscript tweak, then we'll use that as a tweak
3✔
160
        // otherwise, we'll fall back to the normal BIP 86 sign tweak.
3✔
161
        signOpts := fn.MapOption(tapscriptRootToSignOpt)(
3✔
162
                p.tapscriptTweak,
3✔
163
        ).UnwrapOr(musig2.WithBip86SignTweak())
3✔
164

3✔
165
        return p.sig.Verify(
3✔
166
                p.signerNonce, p.combinedNonce, p.signerKeys, pub, m,
3✔
167
                musig2.WithSortedKeys(), signOpts,
3✔
168
        )
3✔
169
}
3✔
170

171
// MusigNoncePair holds the two nonces needed to sign/verify a new commitment
172
// state. The signer nonce is the nonce used by the signer (remote nonce), and
173
// the verification nonce, the nonce used by the verifier (local nonce).
174
type MusigNoncePair struct {
175
        // SigningNonce is the nonce used by the signer to sign the commitment.
176
        SigningNonce musig2.Nonces
177

178
        // VerificationNonce is the nonce used by the verifier to verify the
179
        // commitment.
180
        VerificationNonce musig2.Nonces
181
}
182

183
// String returns a string representation of the MusigNoncePair.
184
func (n *MusigNoncePair) String() string {
3✔
185
        return fmt.Sprintf("NoncePair(verification_nonce=%x, "+
3✔
186
                "signing_nonce=%x)", n.VerificationNonce.PubNonce[:],
3✔
187
                n.SigningNonce.PubNonce[:])
3✔
188
}
3✔
189

190
// TapscriptRootToTweak is a function that takes a MuSig2 taproot tweak and
191
// returns the root hash of the tapscript tree.
192
func muSig2TweakToRoot(tweak input.MuSig2Tweaks) chainhash.Hash {
×
193
        var root chainhash.Hash
×
194
        copy(root[:], tweak.TaprootTweak)
×
195
        return root
×
196
}
×
197

198
// MusigSession abstracts over the details of a logical musig session. A single
199
// session is used for each commitment transactions. The sessions use a JIT
200
// nonce style, wherein part of the session can be created using only the
201
// verifier nonce. Once a new state is signed, then the signer nonce is
202
// generated. Similarly, the verifier then uses the received signer nonce to
203
// complete the session and verify the incoming signature.
204
type MusigSession struct {
205
        // session is the backing musig2 session. We'll use this to interact
206
        // with the musig2 signer.
207
        session *input.MuSig2SessionInfo
208

209
        // combinedNonce is the combined nonce of all signers.
210
        combinedNonce lnwire.Musig2Nonce
211

212
        // nonces is the set of nonces that'll be used to generate/verify the
213
        // next commitment.
214
        nonces MusigNoncePair
215

216
        // inputTxOut is the funding input.
217
        inputTxOut *wire.TxOut
218

219
        // signerKeys is the set of public keys of all signers.
220
        signerKeys []*btcec.PublicKey
221

222
        // remoteKey is the key desc of the remote key.
223
        remoteKey keychain.KeyDescriptor
224

225
        // localKey is the key desc of the local key.
226
        localKey keychain.KeyDescriptor
227

228
        // signer is the signer that'll be used to interact with the musig
229
        // session.
230
        signer input.MuSig2Signer
231

232
        // commitType tracks if this is the session for the local or remote
233
        // commitment.
234
        commitType MusigCommitType
235

236
        // tapscriptTweak is an optional tweak, that if specified, will be used
237
        // instead of the normal BIP 86 tweak when creating the MuSig2
238
        // aggregate key and session.
239
        tapscriptTweak fn.Option[input.MuSig2Tweaks]
240
}
241

242
// NewPartialMusigSession creates a new musig2 session given only the
243
// verification nonce (local nonce), and the other information that has already
244
// been bound to the session.
245
func NewPartialMusigSession(verificationNonce musig2.Nonces,
246
        localKey, remoteKey keychain.KeyDescriptor, signer input.MuSig2Signer,
247
        inputTxOut *wire.TxOut, commitType MusigCommitType,
248
        tapscriptTweak fn.Option[input.MuSig2Tweaks]) *MusigSession {
3✔
249

3✔
250
        signerKeys := []*btcec.PublicKey{localKey.PubKey, remoteKey.PubKey}
3✔
251

3✔
252
        nonces := MusigNoncePair{
3✔
253
                VerificationNonce: verificationNonce,
3✔
254
        }
3✔
255

3✔
256
        return &MusigSession{
3✔
257
                nonces:         nonces,
3✔
258
                remoteKey:      remoteKey,
3✔
259
                localKey:       localKey,
3✔
260
                inputTxOut:     inputTxOut,
3✔
261
                signerKeys:     signerKeys,
3✔
262
                signer:         signer,
3✔
263
                commitType:     commitType,
3✔
264
                tapscriptTweak: tapscriptTweak,
3✔
265
        }
3✔
266
}
3✔
267

268
// FinalizeSession finalizes the session given the signer nonce.  This is
269
// called before signing or verifying a new commitment.
270
func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error {
3✔
271
        var (
3✔
272
                localNonce, remoteNonce musig2.Nonces
3✔
273
                err                     error
3✔
274
        )
3✔
275

3✔
276
        // First, we'll stash the freshly generated signing nonce. Depending on
3✔
277
        // who's commitment we're handling, this'll either be our generated
3✔
278
        // nonce, or the one we just got from the remote party.
3✔
279
        m.nonces.SigningNonce = signingNonce
3✔
280

3✔
281
        switch m.commitType {
3✔
282
        // If we're making a session for the remote commitment, then the nonce
283
        // we use to sign is actually will be the signing nonce for the
284
        // session, and their nonce the verification nonce.
285
        case RemoteMusigCommit:
3✔
286
                localNonce = m.nonces.SigningNonce
3✔
287
                remoteNonce = m.nonces.VerificationNonce
3✔
288

289
        // Otherwise, we're generating/receiving a signature for our local
290
        // commitment (to broadcast), so now our verification nonce is the one
291
        // we've already generated, and we want to bind their new signing
292
        // nonce.
293
        case LocalMusigCommit:
3✔
294
                localNonce = m.nonces.VerificationNonce
3✔
295
                remoteNonce = m.nonces.SigningNonce
3✔
296
        }
297

298
        tweakDesc := m.tapscriptTweak.UnwrapOr(input.MuSig2Tweaks{
3✔
299
                TaprootBIP0086Tweak: true,
3✔
300
        })
3✔
301
        m.session, err = m.signer.MuSig2CreateSession(
3✔
302
                input.MuSig2Version100RC2, m.localKey.KeyLocator, m.signerKeys,
3✔
303
                &tweakDesc, [][musig2.PubNonceSize]byte{remoteNonce.PubNonce},
3✔
304
                &localNonce,
3✔
305
        )
3✔
306
        if err != nil {
3✔
307
                return err
×
308
        }
×
309

310
        // We'll need the raw combined nonces later to be able to verify
311
        // partial signatures, and also combine partial signatures, so we'll
312
        // generate it now ourselves.
313
        aggNonce, err := musig2.AggregateNonces([][musig2.PubNonceSize]byte{
3✔
314
                m.nonces.SigningNonce.PubNonce,
3✔
315
                m.nonces.VerificationNonce.PubNonce,
3✔
316
        })
3✔
317
        if err != nil {
3✔
318
                return nil
×
319
        }
×
320

321
        m.combinedNonce = aggNonce
3✔
322

3✔
323
        return nil
3✔
324
}
325

326
// taprootKeyspendSighash generates the sighash for a taproot key spend. As
327
// this is a musig2 channel output, the keyspend is the only path we can take.
328
func taprootKeyspendSighash(tx *wire.MsgTx, pkScript []byte,
329
        value int64) ([]byte, error) {
3✔
330

3✔
331
        prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(
3✔
332
                pkScript, value,
3✔
333
        )
3✔
334

3✔
335
        sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
3✔
336

3✔
337
        return txscript.CalcTaprootSignatureHash(
3✔
338
                sigHashes, txscript.SigHashDefault, tx, 0, prevOutputFetcher,
3✔
339
        )
3✔
340
}
3✔
341

342
// SignCommit signs the passed commitment w/ the current signing (relative
343
// remote) nonce. Given nonces should only ever be used once, once the method
344
// returns a new nonce is returned, w/ the existing nonce blanked out.
345
func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) {
3✔
346
        switch {
3✔
347
        // If we already have a session, then we don't need to finalize as this
348
        // was done up front (symmetric nonce case, like for co-op close).
349
        case m.session == nil && m.commitType == RemoteMusigCommit:
3✔
350
                // Before we can sign a new commitment, we'll need to generate
3✔
351
                // a fresh nonce that'll be sent along side our signature. With
3✔
352
                // the nonce in hand, we can finalize the session.
3✔
353
                txHash := tx.TxHash()
3✔
354
                signingNonce, err := musig2.GenNonces(
3✔
355
                        musig2.WithPublicKey(m.localKey.PubKey),
3✔
356
                        musig2.WithNonceAuxInput(txHash[:]),
3✔
357
                )
3✔
358
                if err != nil {
3✔
359
                        return nil, err
×
360
                }
×
361
                if err := m.FinalizeSession(*signingNonce); err != nil {
3✔
362
                        return nil, err
×
363
                }
×
364

365
        // Otherwise, we're trying to make a new commitment transaction without
366
        // an active session, so we'll error out.
367
        case m.session == nil:
×
368
                return nil, ErrSessionNotFinalized
×
369
        }
370

371
        // Next we can sign, we'll need to generate the sighash for their
372
        // commitment transaction.
373
        sigHash, err := taprootKeyspendSighash(
3✔
374
                tx, m.inputTxOut.PkScript, m.inputTxOut.Value,
3✔
375
        )
3✔
376
        if err != nil {
3✔
377
                return nil, err
×
378
        }
×
379

380
        // Now that we have our session created, we'll use it to generate the
381
        // initial partial signature over our sighash.
382
        var sigHashMsg [32]byte
3✔
383
        copy(sigHashMsg[:], sigHash)
3✔
384

3✔
385
        walletLog.Infof("Generating new musig2 sig for session=%x, nonces=%s",
3✔
386
                m.session.SessionID[:], m.nonces.String())
3✔
387

3✔
388
        sig, err := m.signer.MuSig2Sign(
3✔
389
                m.session.SessionID, sigHashMsg, false,
3✔
390
        )
3✔
391
        if err != nil {
3✔
392
                return nil, err
×
393
        }
×
394

395
        tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak)
3✔
396

3✔
397
        return NewMusigPartialSig(
3✔
398
                sig, m.session.PublicNonce, m.combinedNonce, m.signerKeys,
3✔
399
                tapscriptRoot,
3✔
400
        ), nil
3✔
401
}
402

403
// Refresh is called once we receive a new verification nonce from the remote
404
// party after sending a signature. This nonce will be coupled within the
405
// revoke-and-ack message of the remote party.
406
func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces,
407
) (*MusigSession, error) {
3✔
408

3✔
409
        return NewPartialMusigSession(
3✔
410
                *verificationNonce, m.localKey, m.remoteKey, m.signer,
3✔
411
                m.inputTxOut, m.commitType, m.tapscriptTweak,
3✔
412
        ), nil
3✔
413
}
3✔
414

415
// VerificationNonce returns the current verification nonce for the session.
416
func (m *MusigSession) VerificationNonce() *musig2.Nonces {
×
417
        return &m.nonces.VerificationNonce
×
418
}
×
419

420
// musigSessionOpts is a set of options that can be used to modify calls to the
421
// musig session.
422
type musigSessionOpts struct {
423
        // customRand is an optional custom random source that can be used to
424
        // generate nonces via a counter scheme.
425
        customRand io.Reader
426
}
427

428
// defaultMusigSessionOpts returns the default set of options for the musig
429
// session.
430
func defaultMusigSessionOpts() *musigSessionOpts {
3✔
431
        return &musigSessionOpts{}
3✔
432
}
3✔
433

434
// MusigSessionOpt is a functional option that can be used to modify calls to
435
// the musig session.
436
type MusigSessionOpt func(*musigSessionOpts)
437

438
// WithLocalCounterNonce is used to generate local nonces based on the shachain
439
// producer and the current height. This allows us to not have to write secret
440
// nonce state to disk. Instead, we can use this to derive the nonce we need to
441
// sign and broadcast our own commitment transaction.
442
func WithLocalCounterNonce(targetHeight uint64,
443
        shaGen shachain.Producer) MusigSessionOpt {
3✔
444

3✔
445
        return func(opt *musigSessionOpts) {
6✔
446
                nextPreimage, _ := shaGen.AtIndex(targetHeight)
3✔
447

3✔
448
                opt.customRand = bytes.NewBuffer(nextPreimage[:])
3✔
449
        }
3✔
450
}
451

452
// invalidPartialSigError is used to return additional debug information to a
453
// caller that encounters an invalid partial sig.
454
type invalidPartialSigError struct {
455
        partialSig        []byte
456
        sigHash           []byte
457
        signingNonce      [musig2.PubNonceSize]byte
458
        verificationNonce [musig2.PubNonceSize]byte
459
}
460

461
// Error returns the error string for the partial sig error.
462
func (i invalidPartialSigError) Error() string {
×
463
        return fmt.Sprintf("invalid partial sig: partial_sig=%x, "+
×
464
                "sig_hash=%x, signing_nonce=%x, verification_nonce=%x",
×
465
                i.partialSig, i.sigHash, i.signingNonce[:],
×
466
                i.verificationNonce[:])
×
467
}
×
468

469
// VerifyCommitSig attempts to verify the passed partial signature against the
470
// passed commitment transaction. A keyspend sighash is assumed to generate the
471
// signed message. As we never re-use nonces, a new verification nonce (our
472
// relative local nonce) returned to transmit to the remote party, which allows
473
// them to generate another signature.
474
func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx,
475
        sig *lnwire.PartialSigWithNonce,
476
        musigOpts ...MusigSessionOpt) (*musig2.Nonces, error) {
3✔
477

3✔
478
        opts := defaultMusigSessionOpts()
3✔
479
        for _, optFunc := range musigOpts {
6✔
480
                optFunc(opts)
3✔
481
        }
3✔
482

483
        if sig == nil {
3✔
484
                return nil, fmt.Errorf("sig not provided")
×
485
        }
×
486

487
        // Before we can verify the signature, we'll need to finalize the
488
        // session by binding the remote party's provided signing nonce.
489
        if err := m.FinalizeSession(musig2.Nonces{
3✔
490
                PubNonce: sig.Nonce,
3✔
491
        }); err != nil {
3✔
492
                return nil, err
×
493
        }
×
494

495
        // When we verify a commitment signature, we always assume that we're
496
        // verifying a signature on our local commitment. Therefore, we'll use:
497
        // their remote nonce, and also public key.
498
        tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak)
3✔
499
        partialSig := NewMusigPartialSig(
3✔
500
                &musig2.PartialSignature{S: &sig.Sig},
3✔
501
                m.nonces.SigningNonce.PubNonce, m.combinedNonce, m.signerKeys,
3✔
502
                tapscriptRoot,
3✔
503
        )
3✔
504

3✔
505
        // With the partial sig loaded with the proper context, we'll now
3✔
506
        // generate the sighash that the remote party should have signed.
3✔
507
        sigHash, err := taprootKeyspendSighash(
3✔
508
                commitTx, m.inputTxOut.PkScript, m.inputTxOut.Value,
3✔
509
        )
3✔
510
        if err != nil {
3✔
511
                return nil, err
×
512
        }
×
513

514
        walletLog.Infof("Verifying new musig2 sig for session=%x, nonce=%s",
3✔
515
                m.session.SessionID[:], m.nonces.String())
3✔
516

3✔
517
        if partialSig == nil {
3✔
518
                return nil, fmt.Errorf("partial sig not set")
×
519
        }
×
520

521
        if !partialSig.Verify(sigHash, m.remoteKey.PubKey) {
3✔
522
                return nil, &invalidPartialSigError{
×
523
                        partialSig:        partialSig.Serialize(),
×
524
                        sigHash:           sigHash,
×
525
                        verificationNonce: m.nonces.VerificationNonce.PubNonce,
×
526
                        signingNonce:      m.nonces.SigningNonce.PubNonce,
×
527
                }
×
528
        }
×
529

530
        nonceOpts := []musig2.NonceGenOption{
3✔
531
                musig2.WithPublicKey(m.localKey.PubKey),
3✔
532
        }
3✔
533
        if opts.customRand != nil {
6✔
534
                nonceOpts = append(
3✔
535
                        nonceOpts, musig2.WithCustomRand(opts.customRand),
3✔
536
                )
3✔
537
        }
3✔
538

539
        // At this point, we know that their signature is valid, so we'll
540
        // generate another verification nonce for them, so they can generate a
541
        // new state transition.
542
        nextVerificationNonce, err := musig2.GenNonces(nonceOpts...)
3✔
543
        if err != nil {
3✔
544
                return nil, fmt.Errorf("unable to gen new nonce: %w", err)
×
545
        }
×
546

547
        return nextVerificationNonce, nil
3✔
548
}
549

550
// CombineSigs combines the passed partial signatures into a valid schnorr
551
// signature.
552
func (m *MusigSession) CombineSigs(sigs ...*musig2.PartialSignature,
553
) (*schnorr.Signature, error) {
3✔
554

3✔
555
        sig, _, err := m.signer.MuSig2CombineSig(
3✔
556
                m.session.SessionID, sigs,
3✔
557
        )
3✔
558
        if err != nil {
3✔
559
                return nil, err
×
560
        }
×
561

562
        return sig, nil
3✔
563
}
564

565
// MusigSessionCfg is used to create a new musig2 pair session. It contains the
566
// keys for both parties, as well as their initial verification nonces.
567
type MusigSessionCfg struct {
568
        // LocalKey is a key desc for the local key.
569
        LocalKey keychain.KeyDescriptor
570

571
        // RemoteKey is a key desc for the remote key.
572
        RemoteKey keychain.KeyDescriptor
573

574
        // LocalNonce is the local party's initial verification nonce.
575
        LocalNonce musig2.Nonces
576

577
        // RemoteNonce is the remote party's initial verification nonce.
578
        RemoteNonce musig2.Nonces
579

580
        // Signer is the signer that will be used to generate the session.
581
        Signer input.MuSig2Signer
582

583
        // InputTxOut is the output that we're signing for. This will be the
584
        // funding input.
585
        InputTxOut *wire.TxOut
586

587
        // TapscriptTweak is an optional tweak that can be used to modify the
588
        // MuSig2 public key used in the session.
589
        TapscriptTweak fn.Option[chainhash.Hash]
590
}
591

592
// MusigPairSession houses the two musig2 sessions needed to do funding and
593
// drive forward the state machine.  The local session is used to verify
594
// incoming commitment states. The remote session is used to propose new
595
// commitment states to the remote party.
596
type MusigPairSession struct {
597
        // LocalSession is the local party's musig2 session.
598
        LocalSession *MusigSession
599

600
        // RemoteSession is the remote party's musig2 session.
601
        RemoteSession *MusigSession
602

603
        // signer is the signer that will be used to drive the session.
604
        signer input.MuSig2Signer
605
}
606

607
// NewMusigPairSession creates a new musig2 pair session.
608
func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
3✔
609
        // Given the config passed in, we'll now create our two sessions: one
3✔
610
        // for the local commit, and one for the remote commit.
3✔
611
        //
3✔
612
        // Both sessions will be created using only the verification nonce for
3✔
613
        // the local+remote party.
3✔
614
        tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(cfg.TapscriptTweak)
3✔
615
        localSession := NewPartialMusigSession(
3✔
616
                cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer,
3✔
617
                cfg.InputTxOut, LocalMusigCommit, tapscriptTweak,
3✔
618
        )
3✔
619
        remoteSession := NewPartialMusigSession(
3✔
620
                cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer,
3✔
621
                cfg.InputTxOut, RemoteMusigCommit, tapscriptTweak,
3✔
622
        )
3✔
623

3✔
624
        return &MusigPairSession{
3✔
625
                LocalSession:  localSession,
3✔
626
                RemoteSession: remoteSession,
3✔
627
                signer:        cfg.Signer,
3✔
628
        }
3✔
629
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc