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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

79.29
/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/txscript"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/input"
14
        "github.com/lightningnetwork/lnd/keychain"
15
        "github.com/lightningnetwork/lnd/lnwire"
16
        "github.com/lightningnetwork/lnd/shachain"
17
)
18

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

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

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

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

40
// MusigPartialSig is a wrapper around the base musig2.PartialSignature type
41
// that also includes information about the set of nonces used, and also the
42
// signer. This allows us to implement the input.Signature interface, as that
43
// requires the ability to perform abstract verification based on a public key.
44
type MusigPartialSig struct {
45
        // sig is the actual musig2 partial signature.
46
        sig *musig2.PartialSignature
47

48
        // signerNonce is the nonce used by the signer to generate the partial
49
        // signature.
50
        signerNonce lnwire.Musig2Nonce
51

52
        // combinedNonce is the combined nonce of all signers.
53
        combinedNonce lnwire.Musig2Nonce
54

55
        // signerKeys is the set of public keys of all signers.
56
        signerKeys []*btcec.PublicKey
57
}
58

59
// NewMusigPartialSig creates a new musig partial signature.
60
func NewMusigPartialSig(sig *musig2.PartialSignature,
61
        signerNonce, combinedNonce lnwire.Musig2Nonce,
62
        signerKeys []*btcec.PublicKey) *MusigPartialSig {
3✔
63

3✔
64
        return &MusigPartialSig{
3✔
65
                sig:           sig,
3✔
66
                signerNonce:   signerNonce,
3✔
67
                combinedNonce: combinedNonce,
3✔
68
                signerKeys:    signerKeys,
3✔
69
        }
3✔
70
}
3✔
71

72
// FromWireSig maps a wire partial sig to this internal type that we'll use to
73
// perform signature validation.
74
func (p *MusigPartialSig) FromWireSig(sig *lnwire.PartialSigWithNonce,
75
) *MusigPartialSig {
3✔
76

3✔
77
        p.sig = &musig2.PartialSignature{
3✔
78
                S: &sig.Sig,
3✔
79
        }
3✔
80
        p.signerNonce = sig.Nonce
3✔
81

3✔
82
        return p
3✔
83
}
3✔
84

85
// ToWireSig maps the partial signature to something that we can use to write
86
// out for the wire protocol.
87
func (p *MusigPartialSig) ToWireSig() *lnwire.PartialSigWithNonce {
3✔
88
        return &lnwire.PartialSigWithNonce{
3✔
89
                PartialSig: lnwire.NewPartialSig(*p.sig.S),
3✔
90
                Nonce:      p.signerNonce,
3✔
91
        }
3✔
92
}
3✔
93

94
// Serialize serializes the musig2 partial signature. The serializing includes
95
// the signer's public nonce _and_ the partial signature. The final signature
96
// is always 98 bytes in length.
97
func (p *MusigPartialSig) Serialize() []byte {
3✔
98
        var b bytes.Buffer
3✔
99

3✔
100
        _ = p.ToWireSig().Encode(&b)
3✔
101

3✔
102
        return b.Bytes()
3✔
103
}
3✔
104

105
// ToSchnorrShell converts the musig partial signature to a regular schnorr.
106
// This schnorr signature uses a zero value for the 'r' field, so we're just
107
// only using the last 32-bytes of the signature. This is useful when we need
108
// to convert an HTLC schnorr signature into something we can send using the
109
// existing messages.
110
func (p *MusigPartialSig) ToSchnorrShell() *schnorr.Signature {
×
111
        var zeroVal btcec.FieldVal
×
112
        return schnorr.NewSignature(&zeroVal, p.sig.S)
×
113
}
×
114

115
// FromSchnorrShell takes a schnorr signature and parses out the last 32 bytes
116
// as a normal musig2 partial signature.
117
func (p *MusigPartialSig) FromSchnorrShell(sig *schnorr.Signature) {
×
118
        var (
×
119
                partialS      btcec.ModNScalar
×
120
                partialSBytes [32]byte
×
121
        )
×
122
        copy(partialSBytes[:], sig.Serialize()[32:])
×
123
        partialS.SetBytes(&partialSBytes)
×
124

×
125
        p.sig = &musig2.PartialSignature{
×
126
                S: &partialS,
×
127
        }
×
128
}
×
129

130
// Verify attempts to verify the partial musig2 signature using the passed
131
// message and signer public key.
132
//
133
// NOTE: This implements the input.Signature interface.
134
func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool {
3✔
135
        var m [32]byte
3✔
136
        copy(m[:], msg)
3✔
137

3✔
138
        return p.sig.Verify(
3✔
139
                p.signerNonce, p.combinedNonce, p.signerKeys, pub, m,
3✔
140
                musig2.WithSortedKeys(), musig2.WithBip86SignTweak(),
3✔
141
        )
3✔
142
}
3✔
143

144
// MusigNoncePair holds the two nonces needed to sign/verify a new commitment
145
// state. The signer nonce is the nonce used by the signer (remote nonce), and
146
// the verification nonce, the nonce used by the verifier (local nonce).
147
type MusigNoncePair struct {
148
        // SigningNonce is the nonce used by the signer to sign the commitment.
149
        SigningNonce musig2.Nonces
150

151
        // VerificationNonce is the nonce used by the verifier to verify the
152
        // commitment.
153
        VerificationNonce musig2.Nonces
154
}
155

156
// String returns a string representation of the MusigNoncePair.
157
func (n *MusigNoncePair) String() string {
3✔
158
        return fmt.Sprintf("NoncePair(verification_nonce=%x, "+
3✔
159
                "signing_nonce=%x)", n.VerificationNonce.PubNonce[:],
3✔
160
                n.SigningNonce.PubNonce[:])
3✔
161
}
3✔
162

163
// MusigSession abstracts over the details of a logical musig session. A single
164
// session is used for each commitment transactions. The sessions use a JIT
165
// nonce style, wherein part of the session can be created using only the
166
// verifier nonce. Once a new state is signed, then the signer nonce is
167
// generated. Similarly, the verifier then uses the received signer nonce to
168
// complete the session and verify the incoming signature.
169
type MusigSession struct {
170
        // session is the backing musig2 session. We'll use this to interact
171
        // with the musig2 signer.
172
        session *input.MuSig2SessionInfo
173

174
        // combinedNonce is the combined nonce of all signers.
175
        combinedNonce lnwire.Musig2Nonce
176

177
        // nonces is the set of nonces that'll be used to generate/verify the
178
        // next commitment.
179
        nonces MusigNoncePair
180

181
        // inputTxOut is the funding input.
182
        inputTxOut *wire.TxOut
183

184
        // signerKeys is the set of public keys of all signers.
185
        signerKeys []*btcec.PublicKey
186

187
        // remoteKey is the key desc of the remote key.
188
        remoteKey keychain.KeyDescriptor
189

190
        // localKey is the key desc of the local key.
191
        localKey keychain.KeyDescriptor
192

193
        // signer is the signer that'll be used to interact with the musig
194
        // session.
195
        signer input.MuSig2Signer
196

197
        // commitType tracks if this is the session for the local or remote
198
        // commitment.
199
        commitType MusigCommitType
200
}
201

202
// NewPartialMusigSession creates a new musig2 session given only the
203
// verification nonce (local nonce), and the other information that has already
204
// been bound to the session.
205
func NewPartialMusigSession(verificationNonce musig2.Nonces,
206
        localKey, remoteKey keychain.KeyDescriptor,
207
        signer input.MuSig2Signer, inputTxOut *wire.TxOut,
208
        commitType MusigCommitType) *MusigSession {
3✔
209

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

3✔
212
        nonces := MusigNoncePair{
3✔
213
                VerificationNonce: verificationNonce,
3✔
214
        }
3✔
215

3✔
216
        return &MusigSession{
3✔
217
                nonces:     nonces,
3✔
218
                remoteKey:  remoteKey,
3✔
219
                localKey:   localKey,
3✔
220
                inputTxOut: inputTxOut,
3✔
221
                signerKeys: signerKeys,
3✔
222
                signer:     signer,
3✔
223
                commitType: commitType,
3✔
224
        }
3✔
225
}
3✔
226

227
// FinalizeSession finalizes the session given the signer nonce.  This is
228
// called before signing or verifying a new commitment.
229
func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error {
3✔
230
        var (
3✔
231
                localNonce, remoteNonce musig2.Nonces
3✔
232
                err                     error
3✔
233
        )
3✔
234

3✔
235
        // First, we'll stash the freshly generated signing nonce. Depending on
3✔
236
        // who's commitment we're handling, this'll either be our generated
3✔
237
        // nonce, or the one we just got from the remote party.
3✔
238
        m.nonces.SigningNonce = signingNonce
3✔
239

3✔
240
        switch m.commitType {
3✔
241
        // If we're making a session for the remote commitment, then the nonce
242
        // we use to sign is actually will be the signing nonce for the
243
        // session, and their nonce the verification nonce.
244
        case RemoteMusigCommit:
3✔
245
                localNonce = m.nonces.SigningNonce
3✔
246
                remoteNonce = m.nonces.VerificationNonce
3✔
247

248
        // Otherwise, we're generating/receiving a signature for our local
249
        // commitment (to broadcast), so now our verification nonce is the one
250
        // we've already generated, and we want to bind their new signing
251
        // nonce.
252
        case LocalMusigCommit:
3✔
253
                localNonce = m.nonces.VerificationNonce
3✔
254
                remoteNonce = m.nonces.SigningNonce
3✔
255
        }
256

257
        tweakDesc := input.MuSig2Tweaks{
3✔
258
                TaprootBIP0086Tweak: true,
3✔
259
        }
3✔
260
        m.session, err = m.signer.MuSig2CreateSession(
3✔
261
                input.MuSig2Version100RC2, m.localKey.KeyLocator, m.signerKeys,
3✔
262
                &tweakDesc, [][musig2.PubNonceSize]byte{remoteNonce.PubNonce},
3✔
263
                &localNonce,
3✔
264
        )
3✔
265
        if err != nil {
3✔
266
                return err
×
267
        }
×
268

269
        // We'll need the raw combined nonces later to be able to verify
270
        // partial signatures, and also combine partial signatures, so we'll
271
        // generate it now ourselves.
272
        aggNonce, err := musig2.AggregateNonces([][musig2.PubNonceSize]byte{
3✔
273
                m.nonces.SigningNonce.PubNonce,
3✔
274
                m.nonces.VerificationNonce.PubNonce,
3✔
275
        })
3✔
276
        if err != nil {
3✔
277
                return nil
×
278
        }
×
279

280
        m.combinedNonce = aggNonce
3✔
281

3✔
282
        return nil
3✔
283
}
284

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

3✔
290
        prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(
3✔
291
                pkScript, value,
3✔
292
        )
3✔
293

3✔
294
        sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
3✔
295

3✔
296
        return txscript.CalcTaprootSignatureHash(
3✔
297
                sigHashes, txscript.SigHashDefault, tx, 0, prevOutputFetcher,
3✔
298
        )
3✔
299
}
3✔
300

301
// SignCommit signs the passed commitment w/ the current signing (relative
302
// remote) nonce. Given nonces should only ever be used once, once the method
303
// returns a new nonce is returned, w/ the existing nonce blanked out.
304
func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) {
3✔
305
        switch {
3✔
306
        // If we already have a session, then we don't need to finalize as this
307
        // was done up front (symmetric nonce case, like for co-op close).
308
        case m.session == nil && m.commitType == RemoteMusigCommit:
3✔
309
                // Before we can sign a new commitment, we'll need to generate
3✔
310
                // a fresh nonce that'll be sent along side our signature. With
3✔
311
                // the nonce in hand, we can finalize the session.
3✔
312
                txHash := tx.TxHash()
3✔
313
                signingNonce, err := musig2.GenNonces(
3✔
314
                        musig2.WithPublicKey(m.localKey.PubKey),
3✔
315
                        musig2.WithNonceAuxInput(txHash[:]),
3✔
316
                )
3✔
317
                if err != nil {
3✔
318
                        return nil, err
×
319
                }
×
320
                if err := m.FinalizeSession(*signingNonce); err != nil {
3✔
321
                        return nil, err
×
322
                }
×
323

324
        // Otherwise, we're trying to make a new commitment transaction without
325
        // an active session, so we'll error out.
326
        case m.session == nil:
×
327
                return nil, ErrSessionNotFinalized
×
328
        }
329

330
        // Next we can sign, we'll need to generate the sighash for their
331
        // commitment transaction.
332
        sigHash, err := taprootKeyspendSighash(
3✔
333
                tx, m.inputTxOut.PkScript, m.inputTxOut.Value,
3✔
334
        )
3✔
335
        if err != nil {
3✔
336
                return nil, err
×
337
        }
×
338

339
        // Now that we have our session created, we'll use it to generate the
340
        // initial partial signature over our sighash.
341
        var sigHashMsg [32]byte
3✔
342
        copy(sigHashMsg[:], sigHash)
3✔
343

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

3✔
347
        sig, err := m.signer.MuSig2Sign(
3✔
348
                m.session.SessionID, sigHashMsg, false,
3✔
349
        )
3✔
350
        if err != nil {
3✔
351
                return nil, err
×
352
        }
×
353

354
        return NewMusigPartialSig(
3✔
355
                sig, m.session.PublicNonce, m.combinedNonce, m.signerKeys,
3✔
356
        ), nil
3✔
357
}
358

359
// Refresh is called once we receive a new verification nonce from the remote
360
// party after sending a signature. This nonce will be coupled within the
361
// revoke-and-ack message of the remote party.
362
func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces,
363
) (*MusigSession, error) {
3✔
364

3✔
365
        return NewPartialMusigSession(
3✔
366
                *verificationNonce, m.localKey, m.remoteKey, m.signer,
3✔
367
                m.inputTxOut, m.commitType,
3✔
368
        ), nil
3✔
369
}
3✔
370

371
// VerificationNonce returns the current verification nonce for the session.
372
func (m *MusigSession) VerificationNonce() *musig2.Nonces {
×
373
        return &m.nonces.VerificationNonce
×
374
}
×
375

376
// musigSessionOpts is a set of options that can be used to modify calls to the
377
// musig session.
378
type musigSessionOpts struct {
379
        // customRand is an optional custom random source that can be used to
380
        // generate nonces via a counter scheme.
381
        customRand io.Reader
382
}
383

384
// defaultMusigSessionOpts returns the default set of options for the musig
385
// session.
386
func defaultMusigSessionOpts() *musigSessionOpts {
3✔
387
        return &musigSessionOpts{}
3✔
388
}
3✔
389

390
// MusigSessionOpt is a functional option that can be used to modify calls to
391
// the musig session.
392
type MusigSessionOpt func(*musigSessionOpts)
393

394
// WithLocalCounterNonce is used to generate local nonces based on the shachain
395
// producer and the current height. This allows us to not have to write secret
396
// nonce state to disk. Instead, we can use this to derive the nonce we need to
397
// sign and broadcast our own commitment transaction.
398
func WithLocalCounterNonce(targetHeight uint64,
399
        shaGen shachain.Producer) MusigSessionOpt {
3✔
400

3✔
401
        return func(opt *musigSessionOpts) {
6✔
402
                nextPreimage, _ := shaGen.AtIndex(targetHeight)
3✔
403

3✔
404
                opt.customRand = bytes.NewBuffer(nextPreimage[:])
3✔
405
        }
3✔
406
}
407

408
// invalidPartialSigError is used to return additional debug information to a
409
// caller that encounters an invalid partial sig.
410
type invalidPartialSigError struct {
411
        partialSig        []byte
412
        sigHash           []byte
413
        signingNonce      [musig2.PubNonceSize]byte
414
        verificationNonce [musig2.PubNonceSize]byte
415
}
416

417
// Error returns the error string for the partial sig error.
418
func (i invalidPartialSigError) Error() string {
×
419
        return fmt.Sprintf("invalid partial sig: partial_sig=%x, "+
×
420
                "sig_hash=%x, signing_nonce=%x, verification_nonce=%x",
×
421
                i.partialSig, i.sigHash, i.signingNonce[:],
×
422
                i.verificationNonce[:])
×
423
}
×
424

425
// VerifyCommitSig attempts to verify the passed partial signature against the
426
// passed commitment transaction. A keyspend sighash is assumed to generate the
427
// signed message. As we never re-use nonces, a new verification nonce (our
428
// relative local nonce) returned to transmit to the remote party, which allows
429
// them to generate another signature.
430
func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx,
431
        sig *lnwire.PartialSigWithNonce,
432
        musigOpts ...MusigSessionOpt) (*musig2.Nonces, error) {
3✔
433

3✔
434
        opts := defaultMusigSessionOpts()
3✔
435
        for _, optFunc := range musigOpts {
6✔
436
                optFunc(opts)
3✔
437
        }
3✔
438

439
        if sig == nil {
3✔
440
                return nil, fmt.Errorf("sig not provided")
×
441
        }
×
442

443
        // Before we can verify the signature, we'll need to finalize the
444
        // session by binding the remote party's provided signing nonce.
445
        if err := m.FinalizeSession(musig2.Nonces{
3✔
446
                PubNonce: sig.Nonce,
3✔
447
        }); err != nil {
3✔
448
                return nil, err
×
449
        }
×
450

451
        // When we verify a commitment signature, we always assume that we're
452
        // verifying a signature on our local commitment. Therefore, we'll use:
453
        // their remote nonce, and also public key.
454
        partialSig := NewMusigPartialSig(
3✔
455
                &musig2.PartialSignature{S: &sig.Sig},
3✔
456
                m.nonces.SigningNonce.PubNonce, m.combinedNonce, m.signerKeys,
3✔
457
        )
3✔
458

3✔
459
        // With the partial sig loaded with the proper context, we'll now
3✔
460
        // generate the sighash that the remote party should have signed.
3✔
461
        sigHash, err := taprootKeyspendSighash(
3✔
462
                commitTx, m.inputTxOut.PkScript, m.inputTxOut.Value,
3✔
463
        )
3✔
464
        if err != nil {
3✔
465
                return nil, err
×
466
        }
×
467

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

3✔
471
        if partialSig == nil {
3✔
472
                return nil, fmt.Errorf("partial sig not set")
×
473
        }
×
474

475
        if !partialSig.Verify(sigHash, m.remoteKey.PubKey) {
3✔
476
                return nil, &invalidPartialSigError{
×
477
                        partialSig:        partialSig.Serialize(),
×
478
                        sigHash:           sigHash,
×
479
                        verificationNonce: m.nonces.VerificationNonce.PubNonce,
×
480
                        signingNonce:      m.nonces.SigningNonce.PubNonce,
×
481
                }
×
482
        }
×
483

484
        nonceOpts := []musig2.NonceGenOption{
3✔
485
                musig2.WithPublicKey(m.localKey.PubKey),
3✔
486
        }
3✔
487
        if opts.customRand != nil {
6✔
488
                nonceOpts = append(
3✔
489
                        nonceOpts, musig2.WithCustomRand(opts.customRand),
3✔
490
                )
3✔
491
        }
3✔
492

493
        // At this point, we know that their signature is valid, so we'll
494
        // generate another verification nonce for them, so they can generate a
495
        // new state transition.
496
        nextVerificationNonce, err := musig2.GenNonces(nonceOpts...)
3✔
497
        if err != nil {
3✔
498
                return nil, fmt.Errorf("unable to gen new nonce: %w", err)
×
499
        }
×
500

501
        return nextVerificationNonce, nil
3✔
502
}
503

504
// CombineSigs combines the passed partial signatures into a valid schnorr
505
// signature.
506
func (m *MusigSession) CombineSigs(sigs ...*musig2.PartialSignature,
507
) (*schnorr.Signature, error) {
3✔
508

3✔
509
        sig, _, err := m.signer.MuSig2CombineSig(
3✔
510
                m.session.SessionID, sigs,
3✔
511
        )
3✔
512
        if err != nil {
3✔
513
                return nil, err
×
514
        }
×
515

516
        return sig, nil
3✔
517
}
518

519
// MusigSessionCfg is used to create a new musig2 pair session. It contains the
520
// keys for both parties, as well as their initial verification nonces.
521
type MusigSessionCfg struct {
522
        // LocalKey is a key desc for the local key.
523
        LocalKey keychain.KeyDescriptor
524

525
        // RemoteKey is a key desc for the remote key.
526
        RemoteKey keychain.KeyDescriptor
527

528
        // LocalNonce is the local party's initial verification nonce.
529
        LocalNonce musig2.Nonces
530

531
        // RemoteNonce is the remote party's initial verification nonce.
532
        RemoteNonce musig2.Nonces
533

534
        // Signer is the signer that will be used to generate the session.
535
        Signer input.MuSig2Signer
536

537
        // InputTxOut is the output that we're signing for. This will be the
538
        // funding input.
539
        InputTxOut *wire.TxOut
540
}
541

542
// MusigPairSession houses the two musig2 sessions needed to do funding and
543
// drive forward the state machine.  The local session is used to verify
544
// incoming commitment states. The remote session is used to propose new
545
// commitment states to the remote party.
546
type MusigPairSession struct {
547
        // LocalSession is the local party's musig2 session.
548
        LocalSession *MusigSession
549

550
        // RemoteSession is the remote party's musig2 session.
551
        RemoteSession *MusigSession
552

553
        // signer is the signer that will be used to drive the session.
554
        signer input.MuSig2Signer
555
}
556

557
// NewMusigPairSession creates a new musig2 pair session.
558
func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
3✔
559
        // Given the config passed in, we'll now create our two sessions: one
3✔
560
        // for the local commit, and one for the remote commit.
3✔
561
        //
3✔
562
        // Both sessions will be created using only the verification nonce for
3✔
563
        // the local+remote party.
3✔
564
        localSession := NewPartialMusigSession(
3✔
565
                cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey,
3✔
566
                cfg.Signer, cfg.InputTxOut, LocalMusigCommit,
3✔
567
        )
3✔
568
        remoteSession := NewPartialMusigSession(
3✔
569
                cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey,
3✔
570
                cfg.Signer, cfg.InputTxOut, RemoteMusigCommit,
3✔
571
        )
3✔
572

3✔
573
        return &MusigPairSession{
3✔
574
                LocalSession:  localSession,
3✔
575
                RemoteSession: remoteSession,
3✔
576
                signer:        cfg.Signer,
3✔
577
        }
3✔
578
}
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