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

lightningnetwork / lnd / 12293074449

12 Dec 2024 09:03AM UTC coverage: 49.538% (-0.002%) from 49.54%
12293074449

push

github

web-flow
Merge pull request #9341 from ellemouton/fnContext

fn: Remove ctx from GoroutineManager constructor

100372 of 202617 relevant lines covered (49.54%)

2.06 hits per line

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

76.43
/watchtower/blob/justice_kit.go
1
package blob
2

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

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/txscript"
11
        "github.com/btcsuite/btcd/wire"
12
        secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
13
        "github.com/lightningnetwork/lnd/fn/v2"
14
        "github.com/lightningnetwork/lnd/input"
15
        "github.com/lightningnetwork/lnd/lnwallet"
16
        "github.com/lightningnetwork/lnd/lnwire"
17
)
18

19
// JusticeKit is an interface that describes lé Blob of Justice. An
20
// implementation of the JusticeKit contains information required to construct
21
// a justice transaction, that sweeps a remote party's revoked commitment
22
// transaction. It supports encryption and decryption using chacha20poly1305,
23
// allowing the client to encrypt the contents of the blob, and for a
24
// watchtower to later decrypt if action must be taken.
25
type JusticeKit interface {
26
        // ToLocalOutputSpendInfo returns the info required to send the to-local
27
        // output. It returns the output pub key script and the witness required
28
        // to spend the output.
29
        ToLocalOutputSpendInfo() (*txscript.PkScript, wire.TxWitness, error)
30

31
        // ToRemoteOutputSpendInfo returns the info required to send the
32
        // to-remote output. It returns the output pub key script, the witness
33
        // required to spend the output and the sequence to apply.
34
        ToRemoteOutputSpendInfo() (*txscript.PkScript, wire.TxWitness, uint32,
35
                error)
36

37
        // HasCommitToRemoteOutput returns true if the kit does include the
38
        // information required to sweep the to-remote output.
39
        HasCommitToRemoteOutput() bool
40

41
        // AddToLocalSig adds the to-local signature to the kit.
42
        AddToLocalSig(sig lnwire.Sig)
43

44
        // AddToRemoteSig adds the to-remote signature to the kit.
45
        AddToRemoteSig(sig lnwire.Sig)
46

47
        // SweepAddress returns the sweep address to be used on the justice tx
48
        // output.
49
        SweepAddress() []byte
50

51
        // PlainTextSize is the size of the encoded-but-unencrypted blob in
52
        // bytes.
53
        PlainTextSize() int
54

55
        encode(w io.Writer) error
56
        decode(r io.Reader) error
57
}
58

59
// legacyJusticeKit is an implementation of the JusticeKit interface which can
60
// be used for backing up commitments of legacy (pre-anchor) channels.
61
type legacyJusticeKit struct {
62
        justiceKitPacketV0
63
}
64

65
// A compile-time check to ensure that legacyJusticeKit implements the
66
// JusticeKit interface.
67
var _ JusticeKit = (*legacyJusticeKit)(nil)
68

69
// newLegacyJusticeKit constructs a new legacyJusticeKit.
70
func newLegacyJusticeKit(sweepScript []byte,
71
        breachInfo *lnwallet.BreachRetribution,
72
        withToRemote bool) *legacyJusticeKit {
4✔
73

4✔
74
        keyRing := breachInfo.KeyRing
4✔
75

4✔
76
        packet := justiceKitPacketV0{
4✔
77
                sweepAddress:     sweepScript,
4✔
78
                revocationPubKey: toBlobPubKey(keyRing.RevocationKey),
4✔
79
                localDelayPubKey: toBlobPubKey(keyRing.ToLocalKey),
4✔
80
                csvDelay:         breachInfo.RemoteDelay,
4✔
81
        }
4✔
82

4✔
83
        if withToRemote {
8✔
84
                packet.commitToRemotePubKey = toBlobPubKey(
4✔
85
                        keyRing.ToRemoteKey,
4✔
86
                )
4✔
87
        }
4✔
88

89
        return &legacyJusticeKit{packet}
4✔
90
}
91

92
// ToLocalOutputSpendInfo returns the info required to send the to-local output.
93
// It returns the output pub key script and the witness required to spend the
94
// output.
95
//
96
// NOTE: This is part of the JusticeKit interface.
97
func (l *legacyJusticeKit) ToLocalOutputSpendInfo() (*txscript.PkScript,
98
        wire.TxWitness, error) {
4✔
99

4✔
100
        revocationPubKey, err := btcec.ParsePubKey(l.revocationPubKey[:])
4✔
101
        if err != nil {
4✔
102
                return nil, nil, err
×
103
        }
×
104

105
        localDelayedPubKey, err := btcec.ParsePubKey(l.localDelayPubKey[:])
4✔
106
        if err != nil {
4✔
107
                return nil, nil, err
×
108
        }
×
109

110
        script, err := input.CommitScriptToSelf(
4✔
111
                l.csvDelay, localDelayedPubKey, revocationPubKey,
4✔
112
        )
4✔
113
        if err != nil {
4✔
114
                return nil, nil, err
×
115
        }
×
116

117
        scriptPubKey, err := input.WitnessScriptHash(script)
4✔
118
        if err != nil {
4✔
119
                return nil, nil, err
×
120
        }
×
121

122
        toLocalSig, err := l.commitToLocalSig.ToSignature()
4✔
123
        if err != nil {
4✔
124
                return nil, nil, err
×
125
        }
×
126

127
        witness := make(wire.TxWitness, 3)
4✔
128
        witness[0] = append(toLocalSig.Serialize(), byte(txscript.SigHashAll))
4✔
129
        witness[1] = []byte{1}
4✔
130
        witness[2] = script
4✔
131

4✔
132
        pkScript, err := txscript.ParsePkScript(scriptPubKey)
4✔
133
        if err != nil {
4✔
134
                return nil, nil, err
×
135
        }
×
136

137
        return &pkScript, witness, nil
4✔
138
}
139

140
// ToRemoteOutputSpendInfo returns the info required to spend the to-remote
141
// output. It returns the output pub key script, the witness required to spend
142
// the output and the sequence to apply.
143
//
144
// NOTE: This is part of the JusticeKit interface.
145
func (l *legacyJusticeKit) ToRemoteOutputSpendInfo() (*txscript.PkScript,
146
        wire.TxWitness, uint32, error) {
4✔
147

4✔
148
        if !btcec.IsCompressedPubKey(l.commitToRemotePubKey[:]) {
4✔
149
                return nil, nil, 0, ErrNoCommitToRemoteOutput
×
150
        }
×
151

152
        toRemoteScript := l.commitToRemotePubKey[:]
4✔
153

4✔
154
        // Since the to-remote witness script should just be a regular p2wkh
4✔
155
        // output, we'll parse it to retrieve the public key.
4✔
156
        toRemotePubKey, err := btcec.ParsePubKey(toRemoteScript)
4✔
157
        if err != nil {
4✔
158
                return nil, nil, 0, err
×
159
        }
×
160

161
        // Compute the witness script hash from the to-remote pubkey, which will
162
        // be used to locate the output on the breach commitment transaction.
163
        toRemoteScriptHash, err := input.CommitScriptUnencumbered(
4✔
164
                toRemotePubKey,
4✔
165
        )
4✔
166
        if err != nil {
4✔
167
                return nil, nil, 0, err
×
168
        }
×
169

170
        toRemoteSig, err := l.commitToRemoteSig.ToSignature()
4✔
171
        if err != nil {
4✔
172
                return nil, nil, 0, err
×
173
        }
×
174

175
        witness := make(wire.TxWitness, 2)
4✔
176
        witness[0] = append(toRemoteSig.Serialize(), byte(txscript.SigHashAll))
4✔
177
        witness[1] = toRemoteScript
4✔
178

4✔
179
        pkScript, err := txscript.ParsePkScript(toRemoteScriptHash)
4✔
180
        if err != nil {
4✔
181
                return nil, nil, 0, err
×
182
        }
×
183

184
        return &pkScript, witness, 0, nil
4✔
185
}
186

187
// HasCommitToRemoteOutput returns true if the blob contains a to-remote p2wkh
188
// pubkey.
189
//
190
// NOTE: This is part of the JusticeKit interface.
191
func (l *legacyJusticeKit) HasCommitToRemoteOutput() bool {
4✔
192
        return btcec.IsCompressedPubKey(l.commitToRemotePubKey[:])
4✔
193
}
4✔
194

195
// SweepAddress returns the sweep address to be used on the justice tx
196
// output.
197
//
198
// NOTE: This is part of the JusticeKit interface.
199
func (l *legacyJusticeKit) SweepAddress() []byte {
4✔
200
        return l.sweepAddress
4✔
201
}
4✔
202

203
// AddToLocalSig adds the to-local signature to the kit.
204
//
205
// NOTE: This is part of the JusticeKit interface.
206
func (l *legacyJusticeKit) AddToLocalSig(sig lnwire.Sig) {
4✔
207
        l.commitToLocalSig = sig
4✔
208
}
4✔
209

210
// AddToRemoteSig adds the to-remote signature to the kit.
211
//
212
// NOTE: This is part of the JusticeKit interface.
213
func (l *legacyJusticeKit) AddToRemoteSig(sig lnwire.Sig) {
4✔
214
        l.commitToRemoteSig = sig
4✔
215
}
4✔
216

217
// PlainTextSize is the size of the encoded-but-unencrypted blob in
218
// bytes.
219
//
220
// NOTE: This is part of the JusticeKit interface.
221
func (l *legacyJusticeKit) PlainTextSize() int {
4✔
222
        return V0PlaintextSize
4✔
223
}
4✔
224

225
// anchorJusticeKit is an implementation of the JusticeKit interface which can
226
// be used for backing up commitments of anchor channels. It inherits most of
227
// the methods from the legacyJusticeKit and overrides the
228
// ToRemoteOutputSpendInfo method since the to-remote output of an anchor
229
// output is a P2WSH instead of the P2WPKH used by the legacy channels.
230
type anchorJusticeKit struct {
231
        legacyJusticeKit
232
}
233

234
// A compile-time check to ensure that legacyJusticeKit implements the
235
// JusticeKit interface.
236
var _ JusticeKit = (*anchorJusticeKit)(nil)
237

238
// newAnchorJusticeKit constructs a new anchorJusticeKit.
239
func newAnchorJusticeKit(sweepScript []byte,
240
        breachInfo *lnwallet.BreachRetribution,
241
        withToRemote bool) *anchorJusticeKit {
4✔
242

4✔
243
        legacyKit := newLegacyJusticeKit(sweepScript, breachInfo, withToRemote)
4✔
244

4✔
245
        return &anchorJusticeKit{
4✔
246
                legacyJusticeKit: *legacyKit,
4✔
247
        }
4✔
248
}
4✔
249

250
// ToRemoteOutputSpendInfo returns the info required to send the to-remote
251
// output. It returns the output pub key script, the witness required to spend
252
// the output and the sequence to apply.
253
//
254
// NOTE: This is part of the JusticeKit interface.
255
func (a *anchorJusticeKit) ToRemoteOutputSpendInfo() (*txscript.PkScript,
256
        wire.TxWitness, uint32, error) {
4✔
257

4✔
258
        if !btcec.IsCompressedPubKey(a.commitToRemotePubKey[:]) {
4✔
259
                return nil, nil, 0, ErrNoCommitToRemoteOutput
×
260
        }
×
261

262
        pk, err := btcec.ParsePubKey(a.commitToRemotePubKey[:])
4✔
263
        if err != nil {
4✔
264
                return nil, nil, 0, err
×
265
        }
×
266

267
        toRemoteScript, err := input.CommitScriptToRemoteConfirmed(pk)
4✔
268
        if err != nil {
4✔
269
                return nil, nil, 0, err
×
270
        }
×
271

272
        toRemoteScriptHash, err := input.WitnessScriptHash(toRemoteScript)
4✔
273
        if err != nil {
4✔
274
                return nil, nil, 0, err
×
275
        }
×
276

277
        toRemoteSig, err := a.commitToRemoteSig.ToSignature()
4✔
278
        if err != nil {
4✔
279
                return nil, nil, 0, err
×
280
        }
×
281

282
        witness := make([][]byte, 2)
4✔
283
        witness[0] = append(toRemoteSig.Serialize(), byte(txscript.SigHashAll))
4✔
284
        witness[1] = toRemoteScript
4✔
285

4✔
286
        pkScript, err := txscript.ParsePkScript(toRemoteScriptHash)
4✔
287
        if err != nil {
4✔
288
                return nil, nil, 0, err
×
289
        }
×
290

291
        return &pkScript, witness, 1, nil
4✔
292
}
293

294
// taprootJusticeKit is an implementation of the JusticeKit interface which can
295
// be used for backing up commitments of taproot channels.
296
type taprootJusticeKit struct {
297
        justiceKitPacketV1
298
}
299

300
// A compile-time check to ensure that taprootJusticeKit implements the
301
// JusticeKit interface.
302
var _ JusticeKit = (*taprootJusticeKit)(nil)
303

304
// newTaprootJusticeKit constructs a new taprootJusticeKit.
305
func newTaprootJusticeKit(sweepScript []byte,
306
        breachInfo *lnwallet.BreachRetribution,
307
        withToRemote bool) (*taprootJusticeKit, error) {
4✔
308

4✔
309
        keyRing := breachInfo.KeyRing
4✔
310

4✔
311
        // TODO(roasbeef): aux leaf tower updates needed
4✔
312

4✔
313
        tree, err := input.NewLocalCommitScriptTree(
4✔
314
                breachInfo.RemoteDelay, keyRing.ToLocalKey,
4✔
315
                keyRing.RevocationKey, fn.None[txscript.TapLeaf](),
4✔
316
        )
4✔
317
        if err != nil {
4✔
318
                return nil, err
×
319
        }
×
320

321
        packet := justiceKitPacketV1{
4✔
322
                sweepAddress: sweepScript,
4✔
323
                revocationPubKey: toBlobSchnorrPubKey(
4✔
324
                        keyRing.RevocationKey,
4✔
325
                ),
4✔
326
                localDelayPubKey: toBlobSchnorrPubKey(keyRing.ToLocalKey),
4✔
327
                delayScriptHash:  tree.SettleLeaf.TapHash(),
4✔
328
        }
4✔
329

4✔
330
        if withToRemote {
8✔
331
                packet.commitToRemotePubKey = toBlobPubKey(keyRing.ToRemoteKey)
4✔
332
        }
4✔
333

334
        return &taprootJusticeKit{packet}, nil
4✔
335
}
336

337
// ToLocalOutputSpendInfo returns the info required to send the to-local
338
// output. It returns the output pubkey script and the witness required
339
// to spend the output.
340
//
341
// NOTE: This is part of the JusticeKit interface.
342
func (t *taprootJusticeKit) ToLocalOutputSpendInfo() (*txscript.PkScript,
343
        wire.TxWitness, error) {
4✔
344

4✔
345
        revocationPubKey, err := schnorr.ParsePubKey(t.revocationPubKey[:])
4✔
346
        if err != nil {
4✔
347
                return nil, nil, err
×
348
        }
×
349

350
        localDelayedPubKey, err := schnorr.ParsePubKey(t.localDelayPubKey[:])
4✔
351
        if err != nil {
4✔
352
                return nil, nil, err
×
353
        }
×
354

355
        revokeScript, err := input.TaprootLocalCommitRevokeScript(
4✔
356
                localDelayedPubKey, revocationPubKey,
4✔
357
        )
4✔
358
        if err != nil {
4✔
359
                return nil, nil, err
×
360
        }
×
361

362
        revokeLeaf := txscript.NewBaseTapLeaf(revokeScript)
4✔
363
        revokeLeafHash := revokeLeaf.TapHash()
4✔
364
        rootHash := tapBranchHash(revokeLeafHash[:], t.delayScriptHash[:])
4✔
365

4✔
366
        outputKey := txscript.ComputeTaprootOutputKey(
4✔
367
                &input.TaprootNUMSKey, rootHash[:],
4✔
368
        )
4✔
369

4✔
370
        scriptPk, err := input.PayToTaprootScript(outputKey)
4✔
371
        if err != nil {
4✔
372
                return nil, nil, err
×
373
        }
×
374

375
        ctrlBlock := txscript.ControlBlock{
4✔
376
                InternalKey:     &input.TaprootNUMSKey,
4✔
377
                OutputKeyYIsOdd: isOddPub(outputKey),
4✔
378
                LeafVersion:     revokeLeaf.LeafVersion,
4✔
379
                InclusionProof:  t.delayScriptHash[:],
4✔
380
        }
4✔
381

4✔
382
        ctrlBytes, err := ctrlBlock.ToBytes()
4✔
383
        if err != nil {
4✔
384
                return nil, nil, err
×
385
        }
×
386

387
        toLocalSig, err := t.commitToLocalSig.ToSignature()
4✔
388
        if err != nil {
4✔
389
                return nil, nil, err
×
390
        }
×
391

392
        witness := make([][]byte, 3)
4✔
393
        witness[0] = toLocalSig.Serialize()
4✔
394
        witness[1] = revokeScript
4✔
395
        witness[2] = ctrlBytes
4✔
396

4✔
397
        pkScript, err := txscript.ParsePkScript(scriptPk)
4✔
398
        if err != nil {
4✔
399
                return nil, nil, err
×
400
        }
×
401

402
        return &pkScript, witness, nil
4✔
403
}
404

405
// ToRemoteOutputSpendInfo returns the info required to send the to-remote
406
// output. It returns the output pubkey script, the witness required to spend
407
// the output and the sequence to apply.
408
//
409
// NOTE: This is part of the JusticeKit interface.
410
func (t *taprootJusticeKit) ToRemoteOutputSpendInfo() (*txscript.PkScript,
411
        wire.TxWitness, uint32, error) {
4✔
412

4✔
413
        if len(t.commitToRemotePubKey[:]) == 0 {
4✔
414
                return nil, nil, 0, ErrNoCommitToRemoteOutput
×
415
        }
×
416

417
        toRemotePk, err := btcec.ParsePubKey(t.commitToRemotePubKey[:])
4✔
418
        if err != nil {
4✔
419
                return nil, nil, 0, err
×
420
        }
×
421

422
        scriptTree, err := input.NewRemoteCommitScriptTree(
4✔
423
                toRemotePk, fn.None[txscript.TapLeaf](),
4✔
424
        )
4✔
425
        if err != nil {
4✔
426
                return nil, nil, 0, err
×
427
        }
×
428

429
        script, err := input.PayToTaprootScript(scriptTree.TaprootKey)
4✔
430
        if err != nil {
4✔
431
                return nil, nil, 0, err
×
432
        }
×
433

434
        settleControlBlock := input.MakeTaprootCtrlBlock(
4✔
435
                scriptTree.SettleLeaf.Script, &input.TaprootNUMSKey,
4✔
436
                scriptTree.TapscriptTree,
4✔
437
        )
4✔
438

4✔
439
        ctrl, err := settleControlBlock.ToBytes()
4✔
440
        if err != nil {
4✔
441
                return nil, nil, 0, err
×
442
        }
×
443

444
        toRemoteSig, err := t.commitToRemoteSig.ToSignature()
4✔
445
        if err != nil {
4✔
446
                return nil, nil, 0, err
×
447
        }
×
448

449
        witness := make([][]byte, 3)
4✔
450
        witness[0] = toRemoteSig.Serialize()
4✔
451
        witness[1] = scriptTree.SettleLeaf.Script
4✔
452
        witness[2] = ctrl
4✔
453

4✔
454
        pkScript, err := txscript.ParsePkScript(script)
4✔
455
        if err != nil {
4✔
456
                return nil, nil, 0, err
×
457
        }
×
458

459
        return &pkScript, witness, 1, nil
4✔
460
}
461

462
// HasCommitToRemoteOutput returns true if the blob contains a to-remote pubkey.
463
//
464
// NOTE: This is part of the JusticeKit interface.
465
func (t *taprootJusticeKit) HasCommitToRemoteOutput() bool {
4✔
466
        return btcec.IsCompressedPubKey(t.commitToRemotePubKey[:])
4✔
467
}
4✔
468

469
// AddToLocalSig adds the to-local signature to the kit.
470
//
471
// NOTE: This is part of the JusticeKit interface.
472
func (t *taprootJusticeKit) AddToLocalSig(sig lnwire.Sig) {
4✔
473
        t.commitToLocalSig = sig
4✔
474
}
4✔
475

476
// AddToRemoteSig adds the to-remote signature to the kit.
477
//
478
// NOTE: This is part of the JusticeKit interface.
479
func (t *taprootJusticeKit) AddToRemoteSig(sig lnwire.Sig) {
4✔
480
        t.commitToRemoteSig = sig
4✔
481
}
4✔
482

483
// SweepAddress returns the sweep address to be used on the justice tx output.
484
//
485
// NOTE: This is part of the JusticeKit interface.
486
func (t *taprootJusticeKit) SweepAddress() []byte {
4✔
487
        return t.sweepAddress
4✔
488
}
4✔
489

490
// PlainTextSize is the size of the encoded-but-unencrypted blob in bytes.
491
//
492
// NOTE: This is part of the JusticeKit interface.
493
func (t *taprootJusticeKit) PlainTextSize() int {
4✔
494
        return V1PlaintextSize
4✔
495
}
4✔
496

497
func tapBranchHash(l, r []byte) chainhash.Hash {
4✔
498
        if bytes.Compare(l, r) > 0 {
4✔
499
                l, r = r, l
×
500
        }
×
501

502
        return *chainhash.TaggedHash(chainhash.TagTapBranch, l, r)
4✔
503
}
504

505
func isOddPub(key *btcec.PublicKey) bool {
4✔
506
        return key.SerializeCompressed()[0] == secp.PubKeyFormatCompressedOdd
4✔
507
}
4✔
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