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

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 hits per line

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

38.25
/input/musig2.go
1
package input
2

3
import (
4
        "bytes"
5
        "crypto/sha256"
6
        "fmt"
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/lightningnetwork/lnd/internal/musig2v040"
12
        "github.com/lightningnetwork/lnd/keychain"
13
)
14

15
// MuSig2Version is a type that defines the different versions of the MuSig2
16
// as defined in the BIP draft:
17
// (https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki)
18
type MuSig2Version uint8
19

20
const (
21
        // MuSig2Version040 is version 0.4.0 of the MuSig2 BIP draft. This will
22
        // use the lnd internal/musig2v040 package.
23
        MuSig2Version040 MuSig2Version = 0
24

25
        // MuSig2Version100RC2 is version 1.0.0rc2 of the MuSig2 BIP draft. This
26
        // uses the github.com/btcsuite/btcd/btcec/v2/schnorr/musig2 package
27
        // at git tag `btcec/v2.3.1`.
28
        MuSig2Version100RC2 MuSig2Version = 1
29
)
30

31
const (
32
        // MuSig2PartialSigSize is the size of a MuSig2 partial signature.
33
        // Because a partial signature is just the s value, this corresponds to
34
        // the length of a scalar.
35
        MuSig2PartialSigSize = 32
36
)
37

38
// MuSig2SessionID is a type for a session ID that is just a hash of the MuSig2
39
// combined key and the local public nonces.
40
type MuSig2SessionID [sha256.Size]byte
41

42
// MuSig2Signer is an interface that declares all methods that a MuSig2
43
// compatible signer needs to implement.
44
type MuSig2Signer interface {
45
        // MuSig2CreateSession creates a new MuSig2 signing session using the
46
        // local key identified by the key locator. The complete list of all
47
        // public keys of all signing parties must be provided, including the
48
        // public key of the local signing key. If nonces of other parties are
49
        // already known, they can be submitted as well to reduce the number of
50
        // method calls necessary later on.
51
        //
52
        // The localNonces field is optional. If it is set, then the specified
53
        // nonces will be used instead of generating from scratch.  This is
54
        // useful in instances where the nonces are generated ahead of time
55
        // before the set of signers is known.
56
        MuSig2CreateSession(MuSig2Version, keychain.KeyLocator,
57
                []*btcec.PublicKey, *MuSig2Tweaks, [][musig2.PubNonceSize]byte,
58
                *musig2.Nonces) (*MuSig2SessionInfo, error)
59

60
        // MuSig2RegisterNonces registers one or more public nonces of other
61
        // signing participants for a session identified by its ID. This method
62
        // returns true once we have all nonces for all other signing
63
        // participants.
64
        MuSig2RegisterNonces(MuSig2SessionID,
65
                [][musig2.PubNonceSize]byte) (bool, error)
66

67
        // MuSig2Sign creates a partial signature using the local signing key
68
        // that was specified when the session was created. This can only be
69
        // called when all public nonces of all participants are known and have
70
        // been registered with the session. If this node isn't responsible for
71
        // combining all the partial signatures, then the cleanup parameter
72
        // should be set, indicating that the session can be removed from memory
73
        // once the signature was produced.
74
        MuSig2Sign(MuSig2SessionID, [sha256.Size]byte,
75
                bool) (*musig2.PartialSignature, error)
76

77
        // MuSig2CombineSig combines the given partial signature(s) with the
78
        // local one, if it already exists. Once a partial signature of all
79
        // participants is registered, the final signature will be combined and
80
        // returned.
81
        MuSig2CombineSig(MuSig2SessionID,
82
                []*musig2.PartialSignature) (*schnorr.Signature, bool, error)
83

84
        // MuSig2Cleanup removes a session from memory to free up resources.
85
        MuSig2Cleanup(MuSig2SessionID) error
86
}
87

88
// MuSig2Context is an interface that is an abstraction over the MuSig2 signing
89
// context. This interface does not contain all of the methods the underlying
90
// implementations have because those use package specific types which cannot
91
// easily be made compatible. Those calls (such as NewSession) are implemented
92
// in this package instead and do the necessary type switch (see
93
// MuSig2CreateContext).
94
type MuSig2Context interface {
95
        // SigningKeys returns the set of keys used for signing.
96
        SigningKeys() []*btcec.PublicKey
97

98
        // CombinedKey returns the combined public key that will be used to
99
        // generate multi-signatures  against.
100
        CombinedKey() (*btcec.PublicKey, error)
101

102
        // TaprootInternalKey returns the internal taproot key, which is the
103
        // aggregated key _before_ the tweak is applied. If a taproot tweak was
104
        // specified, then CombinedKey() will return the fully tweaked output
105
        // key, with this method returning the internal key. If a taproot tweak
106
        // wasn't specified, then this method will return an error.
107
        TaprootInternalKey() (*btcec.PublicKey, error)
108
}
109

110
// MuSig2Session is an interface that is an abstraction over the MuSig2 signing
111
// session. This interface does not contain all of the methods the underlying
112
// implementations have because those use package specific types which cannot
113
// easily be made compatible. Those calls (such as CombineSig or Sign) are
114
// implemented in this package instead and do the necessary type switch (see
115
// MuSig2CombineSig or MuSig2Sign).
116
type MuSig2Session interface {
117
        // FinalSig returns the final combined multi-signature, if present.
118
        FinalSig() *schnorr.Signature
119

120
        // PublicNonce returns the public nonce for a signer. This should be
121
        // sent to other parties before signing begins, so they can compute the
122
        // aggregated public nonce.
123
        PublicNonce() [musig2.PubNonceSize]byte
124

125
        // NumRegisteredNonces returns the total number of nonces that have been
126
        // registered so far.
127
        NumRegisteredNonces() int
128

129
        // RegisterPubNonce should be called for each public nonce from the set
130
        // of signers. This method returns true once all the public nonces have
131
        // been accounted for.
132
        RegisterPubNonce(nonce [musig2.PubNonceSize]byte) (bool, error)
133
}
134

135
// MuSig2SessionInfo is a struct for keeping track of a signing session
136
// information in memory.
137
type MuSig2SessionInfo struct {
138
        // SessionID is the wallet's internal unique ID of this session. The ID
139
        // is the hash over the combined public key and the local public nonces.
140
        SessionID [32]byte
141

142
        // Version is the version of the MuSig2 BIP this signing session is
143
        // using.
144
        Version MuSig2Version
145

146
        // PublicNonce contains the public nonce of the local signer session.
147
        PublicNonce [musig2.PubNonceSize]byte
148

149
        // CombinedKey is the combined public key with all tweaks applied to it.
150
        CombinedKey *btcec.PublicKey
151

152
        // TaprootTweak indicates whether a taproot tweak (BIP-0086 or script
153
        // path) was used. The TaprootInternalKey will only be set if this is
154
        // set to true.
155
        TaprootTweak bool
156

157
        // TaprootInternalKey is the raw combined public key without any tweaks
158
        // applied to it. This is only set if TaprootTweak is true.
159
        TaprootInternalKey *btcec.PublicKey
160

161
        // HaveAllNonces indicates whether this session already has all nonces
162
        // of all other signing participants registered.
163
        HaveAllNonces bool
164

165
        // HaveAllSigs indicates whether this session already has all partial
166
        // signatures of all other signing participants registered.
167
        HaveAllSigs bool
168
}
169

170
// MuSig2Tweaks is a struct that contains all tweaks that can be applied to a
171
// MuSig2 combined public key.
172
type MuSig2Tweaks struct {
173
        // GenericTweaks is a list of normal tweaks to apply to the combined
174
        // public key (and to the private key when signing).
175
        GenericTweaks []musig2.KeyTweakDesc
176

177
        // TaprootBIP0086Tweak indicates that the final key should use the
178
        // taproot tweak as defined in BIP 341, with the BIP 86 modification:
179
        //     outputKey = internalKey + h_tapTweak(internalKey)*G.
180
        // In this case, the aggregated key before the tweak will be used as the
181
        // internal key. If this is set to true then TaprootTweak will be
182
        // ignored.
183
        TaprootBIP0086Tweak bool
184

185
        // TaprootTweak specifies that the final key should use the taproot
186
        // tweak as defined in BIP 341:
187
        //     outputKey = internalKey + h_tapTweak(internalKey || scriptRoot).
188
        // In this case, the aggregated key before the tweak will be used as the
189
        // internal key. Will be ignored if TaprootBIP0086Tweak is set to true.
190
        TaprootTweak []byte
191
}
192

193
// HasTaprootTweak returns true if either a taproot BIP0086 tweak or a taproot
194
// script root tweak is set.
195
func (t *MuSig2Tweaks) HasTaprootTweak() bool {
438✔
196
        return t.TaprootBIP0086Tweak || len(t.TaprootTweak) > 0
438✔
197
}
438✔
198

199
// ToContextOptions converts the tweak descriptor to context options.
200
func (t *MuSig2Tweaks) ToContextOptions() []musig2.ContextOption {
219✔
201
        var tweakOpts []musig2.ContextOption
219✔
202
        if len(t.GenericTweaks) > 0 {
219✔
203
                tweakOpts = append(tweakOpts, musig2.WithTweakedContext(
×
204
                        t.GenericTweaks...,
×
205
                ))
×
206
        }
×
207

208
        // The BIP0086 tweak and the taproot script tweak are mutually
209
        // exclusive.
210
        if t.TaprootBIP0086Tweak {
361✔
211
                tweakOpts = append(tweakOpts, musig2.WithBip86TweakCtx())
142✔
212
        } else if len(t.TaprootTweak) > 0 {
296✔
213
                tweakOpts = append(tweakOpts, musig2.WithTaprootTweakCtx(
77✔
214
                        t.TaprootTweak,
77✔
215
                ))
77✔
216
        }
77✔
217

218
        return tweakOpts
219✔
219
}
220

221
// ToV040ContextOptions converts the tweak descriptor to v0.4.0 context options.
UNCOV
222
func (t *MuSig2Tweaks) ToV040ContextOptions() []musig2v040.ContextOption {
×
UNCOV
223
        var tweakOpts []musig2v040.ContextOption
×
UNCOV
224
        if len(t.GenericTweaks) > 0 {
×
225
                genericTweaksCopy := make(
×
226
                        []musig2v040.KeyTweakDesc, len(t.GenericTweaks),
×
227
                )
×
228
                for idx := range t.GenericTweaks {
×
229
                        genericTweaksCopy[idx] = musig2v040.KeyTweakDesc{
×
230
                                Tweak:   t.GenericTweaks[idx].Tweak,
×
231
                                IsXOnly: t.GenericTweaks[idx].IsXOnly,
×
232
                        }
×
233
                }
×
234
                tweakOpts = append(tweakOpts, musig2v040.WithTweakedContext(
×
235
                        genericTweaksCopy...,
×
236
                ))
×
237
        }
238

239
        // The BIP0086 tweak and the taproot script tweak are mutually
240
        // exclusive.
UNCOV
241
        if t.TaprootBIP0086Tweak {
×
UNCOV
242
                tweakOpts = append(tweakOpts, musig2v040.WithBip86TweakCtx())
×
UNCOV
243
        } else if len(t.TaprootTweak) > 0 {
×
UNCOV
244
                tweakOpts = append(tweakOpts, musig2v040.WithTaprootTweakCtx(
×
UNCOV
245
                        t.TaprootTweak,
×
UNCOV
246
                ))
×
UNCOV
247
        }
×
248

UNCOV
249
        return tweakOpts
×
250
}
251

252
// MuSig2ParsePubKeys parses a list of raw public keys as the signing keys of a
253
// MuSig2 signing session.
254
func MuSig2ParsePubKeys(bipVersion MuSig2Version,
UNCOV
255
        rawPubKeys [][]byte) ([]*btcec.PublicKey, error) {
×
UNCOV
256

×
UNCOV
257
        allSignerPubKeys := make([]*btcec.PublicKey, len(rawPubKeys))
×
UNCOV
258
        if len(rawPubKeys) < 2 {
×
259
                return nil, fmt.Errorf("need at least two signing public keys")
×
260
        }
×
261

UNCOV
262
        for idx, pubKeyBytes := range rawPubKeys {
×
UNCOV
263
                switch bipVersion {
×
UNCOV
264
                case MuSig2Version040:
×
UNCOV
265
                        pubKey, err := schnorr.ParsePubKey(pubKeyBytes)
×
UNCOV
266
                        if err != nil {
×
267
                                return nil, fmt.Errorf("error parsing signer "+
×
268
                                        "public key %d for v0.4.0 (x-only "+
×
269
                                        "format): %v", idx, err)
×
270
                        }
×
UNCOV
271
                        allSignerPubKeys[idx] = pubKey
×
272

UNCOV
273
                case MuSig2Version100RC2:
×
UNCOV
274
                        pubKey, err := btcec.ParsePubKey(pubKeyBytes)
×
UNCOV
275
                        if err != nil {
×
276
                                return nil, fmt.Errorf("error parsing signer "+
×
277
                                        "public key %d for v1.0.0rc2 ("+
×
278
                                        "compressed format): %v", idx, err)
×
279
                        }
×
UNCOV
280
                        allSignerPubKeys[idx] = pubKey
×
281

282
                default:
×
283
                        return nil, fmt.Errorf("unknown MuSig2 version: <%d>",
×
284
                                bipVersion)
×
285
                }
286
        }
287

UNCOV
288
        return allSignerPubKeys, nil
×
289
}
290

291
// MuSig2CombineKeys combines the given set of public keys into a single
292
// combined MuSig2 combined public key, applying the given tweaks.
293
func MuSig2CombineKeys(bipVersion MuSig2Version,
294
        allSignerPubKeys []*btcec.PublicKey, sortKeys bool,
295
        tweaks *MuSig2Tweaks) (*musig2.AggregateKey, error) {
10✔
296

10✔
297
        switch bipVersion {
10✔
298
        case MuSig2Version040:
5✔
299
                return combineKeysV040(allSignerPubKeys, sortKeys, tweaks)
5✔
300

301
        case MuSig2Version100RC2:
4✔
302
                return combineKeysV100RC2(allSignerPubKeys, sortKeys, tweaks)
4✔
303

304
        default:
1✔
305
                return nil, fmt.Errorf("unknown MuSig2 version: <%d>",
1✔
306
                        bipVersion)
1✔
307
        }
308
}
309

310
// combineKeysV100rc1 implements the MuSigCombineKeys logic for the MuSig2 BIP
311
// draft version 1.0.0rc2.
312
func combineKeysV100RC2(allSignerPubKeys []*btcec.PublicKey, sortKeys bool,
313
        tweaks *MuSig2Tweaks) (*musig2.AggregateKey, error) {
4✔
314

4✔
315
        // Convert the tweak options into the appropriate MuSig2 API functional
4✔
316
        // options.
4✔
317
        var keyAggOpts []musig2.KeyAggOption
4✔
318
        switch {
4✔
319
        case tweaks.TaprootBIP0086Tweak:
4✔
320
                keyAggOpts = append(keyAggOpts, musig2.WithBIP86KeyTweak())
4✔
UNCOV
321
        case len(tweaks.TaprootTweak) > 0:
×
UNCOV
322
                keyAggOpts = append(keyAggOpts, musig2.WithTaprootKeyTweak(
×
UNCOV
323
                        tweaks.TaprootTweak,
×
UNCOV
324
                ))
×
325
        case len(tweaks.GenericTweaks) > 0:
×
326
                keyAggOpts = append(keyAggOpts, musig2.WithKeyTweaks(
×
327
                        tweaks.GenericTweaks...,
×
328
                ))
×
329
        }
330

331
        // Then we'll use this information to compute the aggregated public key.
332
        combinedKey, _, _, err := musig2.AggregateKeys(
4✔
333
                allSignerPubKeys, sortKeys, keyAggOpts...,
4✔
334
        )
4✔
335
        return combinedKey, err
4✔
336
}
337

338
// combineKeysV040 implements the MuSigCombineKeys logic for the MuSig2 BIP
339
// draft version 0.4.0.
340
func combineKeysV040(allSignerPubKeys []*btcec.PublicKey, sortKeys bool,
341
        tweaks *MuSig2Tweaks) (*musig2.AggregateKey, error) {
5✔
342

5✔
343
        // Convert the tweak options into the appropriate MuSig2 API functional
5✔
344
        // options.
5✔
345
        var keyAggOpts []musig2v040.KeyAggOption
5✔
346
        switch {
5✔
347
        case tweaks.TaprootBIP0086Tweak:
5✔
348
                keyAggOpts = append(keyAggOpts, musig2v040.WithBIP86KeyTweak())
5✔
UNCOV
349
        case len(tweaks.TaprootTweak) > 0:
×
UNCOV
350
                keyAggOpts = append(keyAggOpts, musig2v040.WithTaprootKeyTweak(
×
UNCOV
351
                        tweaks.TaprootTweak,
×
UNCOV
352
                ))
×
353
        case len(tweaks.GenericTweaks) > 0:
×
354
                genericTweaksCopy := make(
×
355
                        []musig2v040.KeyTweakDesc, len(tweaks.GenericTweaks),
×
356
                )
×
357
                for idx := range tweaks.GenericTweaks {
×
358
                        genericTweaksCopy[idx] = musig2v040.KeyTweakDesc{
×
359
                                Tweak:   tweaks.GenericTweaks[idx].Tweak,
×
360
                                IsXOnly: tweaks.GenericTweaks[idx].IsXOnly,
×
361
                        }
×
362
                }
×
363
                keyAggOpts = append(keyAggOpts, musig2v040.WithKeyTweaks(
×
364
                        genericTweaksCopy...,
×
365
                ))
×
366
        }
367

368
        // Then we'll use this information to compute the aggregated public key.
369
        combinedKey, _, _, err := musig2v040.AggregateKeys(
5✔
370
                allSignerPubKeys, sortKeys, keyAggOpts...,
5✔
371
        )
5✔
372

5✔
373
        // Copy the result back into the default version's native type.
5✔
374
        return &musig2.AggregateKey{
5✔
375
                FinalKey:      combinedKey.FinalKey,
5✔
376
                PreTweakedKey: combinedKey.PreTweakedKey,
5✔
377
        }, err
5✔
378
}
379

380
// MuSig2CreateContext creates a new MuSig2 signing context.
381
func MuSig2CreateContext(bipVersion MuSig2Version, privKey *btcec.PrivateKey,
382
        allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
383
        localNonces *musig2.Nonces,
384
) (MuSig2Context, MuSig2Session, error) {
219✔
385

219✔
386
        switch bipVersion {
219✔
UNCOV
387
        case MuSig2Version040:
×
UNCOV
388
                return createContextV040(
×
UNCOV
389
                        privKey, allSignerPubKeys, tweaks, localNonces,
×
UNCOV
390
                )
×
391

392
        case MuSig2Version100RC2:
219✔
393
                return createContextV100RC2(
219✔
394
                        privKey, allSignerPubKeys, tweaks, localNonces,
219✔
395
                )
219✔
396

397
        default:
×
398
                return nil, nil, fmt.Errorf("unknown MuSig2 version: <%d>",
×
399
                        bipVersion)
×
400
        }
401
}
402

403
// createContextV100RC2 implements the MuSig2CreateContext logic for the MuSig2
404
// BIP draft version 1.0.0rc2.
405
func createContextV100RC2(privKey *btcec.PrivateKey,
406
        allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
407
        localNonces *musig2.Nonces,
408
) (*musig2.Context, *musig2.Session, error) {
219✔
409

219✔
410
        // The context keeps track of all signing keys and our local key.
219✔
411
        allOpts := append(
219✔
412
                []musig2.ContextOption{
219✔
413
                        musig2.WithKnownSigners(allSignerPubKeys),
219✔
414
                },
219✔
415
                tweaks.ToContextOptions()...,
219✔
416
        )
219✔
417
        muSigContext, err := musig2.NewContext(privKey, true, allOpts...)
219✔
418
        if err != nil {
219✔
419
                return nil, nil, fmt.Errorf("error creating MuSig2 signing "+
×
420
                        "context: %v", err)
×
421
        }
×
422

423
        var sessionOpts []musig2.SessionOption
219✔
424
        if localNonces != nil {
438✔
425
                sessionOpts = append(
219✔
426
                        sessionOpts, musig2.WithPreGeneratedNonce(localNonces),
219✔
427
                )
219✔
428
        }
219✔
429

430
        muSigSession, err := muSigContext.NewSession(sessionOpts...)
219✔
431
        if err != nil {
219✔
432
                return nil, nil, fmt.Errorf("error creating MuSig2 signing "+
×
433
                        "session: %v", err)
×
434
        }
×
435

436
        return muSigContext, muSigSession, nil
219✔
437
}
438

439
// createContextV040 implements the MuSig2CreateContext logic for the MuSig2 BIP
440
// draft version 0.4.0.
441
func createContextV040(privKey *btcec.PrivateKey,
442
        allSignerPubKeys []*btcec.PublicKey, tweaks *MuSig2Tweaks,
443
        _ *musig2.Nonces,
UNCOV
444
) (*musig2v040.Context, *musig2v040.Session, error) {
×
UNCOV
445

×
UNCOV
446
        // The context keeps track of all signing keys and our local key.
×
UNCOV
447
        allOpts := append(
×
UNCOV
448
                []musig2v040.ContextOption{
×
UNCOV
449
                        musig2v040.WithKnownSigners(allSignerPubKeys),
×
UNCOV
450
                },
×
UNCOV
451
                tweaks.ToV040ContextOptions()...,
×
UNCOV
452
        )
×
UNCOV
453
        muSigContext, err := musig2v040.NewContext(privKey, true, allOpts...)
×
UNCOV
454
        if err != nil {
×
455
                return nil, nil, fmt.Errorf("error creating MuSig2 signing "+
×
456
                        "context: %v", err)
×
457
        }
×
458

UNCOV
459
        muSigSession, err := muSigContext.NewSession()
×
UNCOV
460
        if err != nil {
×
461
                return nil, nil, fmt.Errorf("error creating MuSig2 signing "+
×
462
                        "session: %v", err)
×
463
        }
×
464

UNCOV
465
        return muSigContext, muSigSession, nil
×
466
}
467

468
// MuSig2Sign calls the Sign() method on the given versioned signing session and
469
// returns the result in the most recent version of the MuSig2 API.
470
func MuSig2Sign(session MuSig2Session, msg [32]byte,
471
        withSortedKeys bool) (*musig2.PartialSignature, error) {
119✔
472

119✔
473
        switch s := session.(type) {
119✔
474
        case *musig2.Session:
119✔
475
                var opts []musig2.SignOption
119✔
476
                if withSortedKeys {
238✔
477
                        opts = append(opts, musig2.WithSortedKeys())
119✔
478
                }
119✔
479
                partialSig, err := s.Sign(msg, opts...)
119✔
480
                if err != nil {
119✔
481
                        return nil, fmt.Errorf("error signing with local key: "+
×
482
                                "%v", err)
×
483
                }
×
484

485
                return partialSig, nil
119✔
486

UNCOV
487
        case *musig2v040.Session:
×
UNCOV
488
                var opts []musig2v040.SignOption
×
UNCOV
489
                if withSortedKeys {
×
UNCOV
490
                        opts = append(opts, musig2v040.WithSortedKeys())
×
UNCOV
491
                }
×
UNCOV
492
                partialSig, err := s.Sign(msg, opts...)
×
UNCOV
493
                if err != nil {
×
494
                        return nil, fmt.Errorf("error signing with local key: "+
×
495
                                "%v", err)
×
496
                }
×
497

UNCOV
498
                return &musig2.PartialSignature{
×
UNCOV
499
                        S: partialSig.S,
×
UNCOV
500
                        R: partialSig.R,
×
UNCOV
501
                }, nil
×
502

503
        default:
×
504
                return nil, fmt.Errorf("invalid session type <%T>", s)
×
505
        }
506
}
507

508
// MuSig2CombineSig calls the CombineSig() method on the given versioned signing
509
// session and returns the result in the most recent version of the MuSig2 API.
510
func MuSig2CombineSig(session MuSig2Session,
511
        otherPartialSig *musig2.PartialSignature) (bool, error) {
8✔
512

8✔
513
        switch s := session.(type) {
8✔
514
        case *musig2.Session:
8✔
515
                haveAllSigs, err := s.CombineSig(otherPartialSig)
8✔
516
                if err != nil {
8✔
517
                        return false, fmt.Errorf("error combining partial "+
×
518
                                "signature: %v", err)
×
519
                }
×
520

521
                return haveAllSigs, nil
8✔
522

UNCOV
523
        case *musig2v040.Session:
×
UNCOV
524
                haveAllSigs, err := s.CombineSig(&musig2v040.PartialSignature{
×
UNCOV
525
                        S: otherPartialSig.S,
×
UNCOV
526
                        R: otherPartialSig.R,
×
UNCOV
527
                })
×
UNCOV
528
                if err != nil {
×
529
                        return false, fmt.Errorf("error combining partial "+
×
530
                                "signature: %v", err)
×
531
                }
×
532

UNCOV
533
                return haveAllSigs, nil
×
534

535
        default:
×
536
                return false, fmt.Errorf("invalid session type <%T>", s)
×
537
        }
538
}
539

540
// NewMuSig2SessionID returns the unique ID of a MuSig2 session by using the
541
// combined key and the local public nonces and hashing that data.
542
func NewMuSig2SessionID(combinedKey *btcec.PublicKey,
543
        publicNonces [musig2.PubNonceSize]byte) MuSig2SessionID {
219✔
544

219✔
545
        // We hash the data to save some bytes in memory.
219✔
546
        hash := sha256.New()
219✔
547
        _, _ = hash.Write(combinedKey.SerializeCompressed())
219✔
548
        _, _ = hash.Write(publicNonces[:])
219✔
549

219✔
550
        id := MuSig2SessionID{}
219✔
551
        copy(id[:], hash.Sum(nil))
219✔
552
        return id
219✔
553
}
219✔
554

555
// SerializePartialSignature encodes the partial signature to a fixed size byte
556
// array.
557
func SerializePartialSignature(
UNCOV
558
        sig *musig2.PartialSignature) ([MuSig2PartialSigSize]byte, error) {
×
UNCOV
559

×
UNCOV
560
        var (
×
UNCOV
561
                buf    bytes.Buffer
×
UNCOV
562
                result [MuSig2PartialSigSize]byte
×
UNCOV
563
        )
×
UNCOV
564
        if err := sig.Encode(&buf); err != nil {
×
565
                return result, fmt.Errorf("error encoding partial signature: "+
×
566
                        "%v", err)
×
567
        }
×
568

UNCOV
569
        if buf.Len() != MuSig2PartialSigSize {
×
570
                return result, fmt.Errorf("invalid partial signature length, "+
×
571
                        "got %d wanted %d", buf.Len(), MuSig2PartialSigSize)
×
572
        }
×
573

UNCOV
574
        copy(result[:], buf.Bytes())
×
UNCOV
575

×
UNCOV
576
        return result, nil
×
577
}
578

579
// DeserializePartialSignature decodes a partial signature from a byte slice.
580
func DeserializePartialSignature(scalarBytes []byte) (*musig2.PartialSignature,
UNCOV
581
        error) {
×
UNCOV
582

×
UNCOV
583
        if len(scalarBytes) != MuSig2PartialSigSize {
×
584
                return nil, fmt.Errorf("invalid partial signature length, got "+
×
585
                        "%d wanted %d", len(scalarBytes), MuSig2PartialSigSize)
×
586
        }
×
587

UNCOV
588
        sig := &musig2.PartialSignature{}
×
UNCOV
589
        if err := sig.Decode(bytes.NewReader(scalarBytes)); err != nil {
×
590
                return nil, fmt.Errorf("error decoding partial signature: %w",
×
591
                        err)
×
592
        }
×
593

UNCOV
594
        return sig, nil
×
595
}
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