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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

69.44
/contractcourt/breach_arbitrator.go
1
package contractcourt
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "sync"
10

11
        "github.com/btcsuite/btcd/blockchain"
12
        "github.com/btcsuite/btcd/btcutil"
13
        "github.com/btcsuite/btcd/chaincfg/chainhash"
14
        "github.com/btcsuite/btcd/txscript"
15
        "github.com/btcsuite/btcd/wire"
16
        "github.com/lightningnetwork/lnd/chainntnfs"
17
        "github.com/lightningnetwork/lnd/channeldb"
18
        "github.com/lightningnetwork/lnd/input"
19
        "github.com/lightningnetwork/lnd/kvdb"
20
        "github.com/lightningnetwork/lnd/labels"
21
        "github.com/lightningnetwork/lnd/lntypes"
22
        "github.com/lightningnetwork/lnd/lnutils"
23
        "github.com/lightningnetwork/lnd/lnwallet"
24
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
25
)
26

27
const (
28
        // justiceTxConfTarget is the number of blocks we'll use as a
29
        // confirmation target when creating the justice transaction. We'll
30
        // choose an aggressive target, since we want to be sure it confirms
31
        // quickly.
32
        justiceTxConfTarget = 2
33

34
        // blocksPassedSplitPublish is the number of blocks without
35
        // confirmation of the justice tx we'll wait before starting to publish
36
        // smaller variants of the justice tx. We do this to mitigate an attack
37
        // the channel peer can do by pinning the HTLC outputs of the
38
        // commitment with low-fee HTLC transactions.
39
        blocksPassedSplitPublish = 4
40
)
41

42
var (
43
        // retributionBucket stores retribution state on disk between detecting
44
        // a contract breach, broadcasting a justice transaction that sweeps the
45
        // channel, and finally witnessing the justice transaction confirm on
46
        // the blockchain. It is critical that such state is persisted on disk,
47
        // so that if our node restarts at any point during the retribution
48
        // procedure, we can recover and continue from the persisted state.
49
        retributionBucket = []byte("retribution")
50

51
        // taprootRetributionBucket stores the tarpoot specific retribution
52
        // information. This includes things like the control blocks for both
53
        // commitment outputs, and the taptweak needed to sweep each HTLC (one
54
        // for the first and one for the second level).
55
        taprootRetributionBucket = []byte("tap-retribution")
56

57
        // errBrarShuttingDown is an error returned if the BreachArbitrator has
58
        // been signalled to exit.
59
        errBrarShuttingDown = errors.New("BreachArbitrator shutting down")
60
)
61

62
// ContractBreachEvent is an event the BreachArbitrator will receive in case a
63
// contract breach is observed on-chain. It contains the necessary information
64
// to handle the breach, and a ProcessACK closure we will use to ACK the event
65
// when we have safely stored all the necessary information.
66
type ContractBreachEvent struct {
67
        // ChanPoint is the channel point of the breached channel.
68
        ChanPoint wire.OutPoint
69

70
        // ProcessACK is an closure that should be called with a nil error iff
71
        // the breach retribution info is safely stored in the retribution
72
        // store. In case storing the information to the store fails, a non-nil
73
        // error should be used. When this closure returns, it means that the
74
        // contract court has marked the channel pending close in the DB, and
75
        // it is safe for the BreachArbitrator to carry on its duty.
76
        ProcessACK func(error)
77

78
        // BreachRetribution is the information needed to act on this contract
79
        // breach.
80
        BreachRetribution *lnwallet.BreachRetribution
81
}
82

83
// ChannelCloseType is an enum which signals the type of channel closure the
84
// peer should execute.
85
type ChannelCloseType uint8
86

87
const (
88
        // CloseRegular indicates a regular cooperative channel closure
89
        // should be attempted.
90
        CloseRegular ChannelCloseType = iota
91

92
        // CloseBreach indicates that a channel breach has been detected, and
93
        // the link should immediately be marked as unavailable.
94
        CloseBreach
95
)
96

97
// RetributionStorer provides an interface for managing a persistent map from
98
// wire.OutPoint -> retributionInfo. Upon learning of a breach, a
99
// BreachArbitrator should record the retributionInfo for the breached channel,
100
// which serves a checkpoint in the event that retribution needs to be resumed
101
// after failure. A RetributionStore provides an interface for managing the
102
// persisted set, as well as mapping user defined functions over the entire
103
// on-disk contents.
104
//
105
// Calls to RetributionStore may occur concurrently. A concrete instance of
106
// RetributionStore should use appropriate synchronization primitives, or
107
// be otherwise safe for concurrent access.
108
type RetributionStorer interface {
109
        // Add persists the retributionInfo to disk, using the information's
110
        // chanPoint as the key. This method should overwrite any existing
111
        // entries found under the same key, and an error should be raised if
112
        // the addition fails.
113
        Add(retInfo *retributionInfo) error
114

115
        // IsBreached queries the retribution store to see if the breach arbiter
116
        // is aware of any breaches for the provided channel point.
117
        IsBreached(chanPoint *wire.OutPoint) (bool, error)
118

119
        // Remove deletes the retributionInfo from disk, if any exists, under
120
        // the given key. An error should be re raised if the removal fails.
121
        Remove(key *wire.OutPoint) error
122

123
        // ForAll iterates over the existing on-disk contents and applies a
124
        // chosen, read-only callback to each. This method should ensure that it
125
        // immediately propagate any errors generated by the callback.
126
        ForAll(cb func(*retributionInfo) error, reset func()) error
127
}
128

129
// BreachConfig bundles the required subsystems used by the breach arbiter. An
130
// instance of BreachConfig is passed to NewBreachArbitrator during
131
// instantiation.
132
type BreachConfig struct {
133
        // CloseLink allows the breach arbiter to shutdown any channel links for
134
        // which it detects a breach, ensuring now further activity will
135
        // continue across the link. The method accepts link's channel point and
136
        // a close type to be included in the channel close summary.
137
        CloseLink func(*wire.OutPoint, ChannelCloseType)
138

139
        // DB provides access to the user's channels, allowing the breach
140
        // arbiter to determine the current state of a user's channels, and how
141
        // it should respond to channel closure.
142
        DB *channeldb.ChannelStateDB
143

144
        // Estimator is used by the breach arbiter to determine an appropriate
145
        // fee level when generating, signing, and broadcasting sweep
146
        // transactions.
147
        Estimator chainfee.Estimator
148

149
        // GenSweepScript generates the receiving scripts for swept outputs.
150
        GenSweepScript func() ([]byte, error)
151

152
        // Notifier provides a publish/subscribe interface for event driven
153
        // notifications regarding the confirmation of txids.
154
        Notifier chainntnfs.ChainNotifier
155

156
        // PublishTransaction facilitates the process of broadcasting a
157
        // transaction to the network.
158
        PublishTransaction func(*wire.MsgTx, string) error
159

160
        // ContractBreaches is a channel where the BreachArbitrator will receive
161
        // notifications in the event of a contract breach being observed. A
162
        // ContractBreachEvent must be ACKed by the BreachArbitrator, such that
163
        // the sending subsystem knows that the event is properly handed off.
164
        ContractBreaches <-chan *ContractBreachEvent
165

166
        // Signer is used by the breach arbiter to generate sweep transactions,
167
        // which move coins from previously open channels back to the user's
168
        // wallet.
169
        Signer input.Signer
170

171
        // Store is a persistent resource that maintains information regarding
172
        // breached channels. This is used in conjunction with DB to recover
173
        // from crashes, restarts, or other failures.
174
        Store RetributionStorer
175
}
176

177
// BreachArbitrator is a special subsystem which is responsible for watching and
178
// acting on the detection of any attempted uncooperative channel breaches by
179
// channel counterparties. This file essentially acts as deterrence code for
180
// those attempting to launch attacks against the daemon. In practice it's
181
// expected that the logic in this file never gets executed, but it is
182
// important to have it in place just in case we encounter cheating channel
183
// counterparties.
184
// TODO(roasbeef): closures in config for subsystem pointers to decouple?
185
type BreachArbitrator struct {
186
        started sync.Once
187
        stopped sync.Once
188

189
        cfg *BreachConfig
190

191
        subscriptions map[wire.OutPoint]chan struct{}
192

193
        quit chan struct{}
194
        wg   sync.WaitGroup
195
        sync.Mutex
196
}
197

198
// NewBreachArbitrator creates a new instance of a BreachArbitrator initialized
199
// with its dependent objects.
200
func NewBreachArbitrator(cfg *BreachConfig) *BreachArbitrator {
201
        return &BreachArbitrator{
202
                cfg:           cfg,
203
                subscriptions: make(map[wire.OutPoint]chan struct{}),
204
                quit:          make(chan struct{}),
205
        }
206
}
207

8✔
208
// Start is an idempotent method that officially starts the BreachArbitrator
8✔
209
// along with all other goroutines it needs to perform its functions.
8✔
210
func (b *BreachArbitrator) Start() error {
8✔
211
        var err error
8✔
212
        b.started.Do(func() {
8✔
213
                brarLog.Info("Breach arbiter starting")
8✔
214
                err = b.start()
215
        })
216
        return err
217
}
8✔
218

8✔
219
func (b *BreachArbitrator) start() error {
16✔
220
        // Load all retributions currently persisted in the retribution store.
8✔
221
        var breachRetInfos map[wire.OutPoint]retributionInfo
8✔
222
        if err := b.cfg.Store.ForAll(func(ret *retributionInfo) error {
8✔
223
                breachRetInfos[ret.chanPoint] = *ret
8✔
224
                return nil
225
        }, func() {
226
                breachRetInfos = make(map[wire.OutPoint]retributionInfo)
8✔
227
        }); err != nil {
8✔
228
                brarLog.Errorf("Unable to create retribution info: %v", err)
8✔
229
                return err
8✔
230
        }
×
UNCOV
231

×
232
        // Load all currently closed channels from disk, we will use the
8✔
233
        // channels that have been marked fully closed to filter the retribution
8✔
234
        // information loaded from disk. This is necessary in the event that the
8✔
UNCOV
235
        // channel was marked fully closed, but was not removed from the
×
UNCOV
236
        // retribution store.
×
UNCOV
237
        closedChans, err := b.cfg.DB.FetchClosedChannels(false)
×
238
        if err != nil {
239
                brarLog.Errorf("Unable to fetch closing channels: %v", err)
240
                return err
241
        }
242

243
        brarLog.Debugf("Found %v closing channels, %v retribution records",
244
                len(closedChans), len(breachRetInfos))
8✔
245

8✔
UNCOV
246
        // Using the set of non-pending, closed channels, reconcile any
×
UNCOV
247
        // discrepancies between the channeldb and the retribution store by
×
UNCOV
248
        // removing any retribution information for which we have already
×
249
        // finished our responsibilities. If the removal is successful, we also
250
        // remove the entry from our in-memory map, to avoid any further action
8✔
251
        // for this channel.
8✔
252
        // TODO(halseth): no need continue on IsPending once closed channels
8✔
253
        // actually means close transaction is confirmed.
8✔
254
        for _, chanSummary := range closedChans {
8✔
255
                brarLog.Debugf("Working on close channel: %v, is_pending: %v",
8✔
256
                        chanSummary.ChanPoint, chanSummary.IsPending)
8✔
257

8✔
258
                if chanSummary.IsPending {
8✔
259
                        continue
8✔
260
                }
8✔
261

8✔
UNCOV
262
                chanPoint := &chanSummary.ChanPoint
×
UNCOV
263
                if _, ok := breachRetInfos[*chanPoint]; ok {
×
264
                        if err := b.cfg.Store.Remove(chanPoint); err != nil {
×
265
                                brarLog.Errorf("Unable to remove closed "+
×
266
                                        "chanid=%v from breach arbiter: %v",
×
267
                                        chanPoint, err)
268
                                return err
269
                        }
×
270
                        delete(breachRetInfos, *chanPoint)
×
271

×
272
                        brarLog.Debugf("Skipped closed channel: %v",
×
273
                                chanSummary.ChanPoint)
×
UNCOV
274
                }
×
UNCOV
275
        }
×
UNCOV
276

×
UNCOV
277
        // Spawn the exactRetribution tasks to monitor and resolve any breaches
×
UNCOV
278
        // that were loaded from the retribution store.
×
UNCOV
279
        for chanPoint := range breachRetInfos {
×
UNCOV
280
                retInfo := breachRetInfos[chanPoint]
×
281

282
                brarLog.Debugf("Handling breach handoff on startup "+
283
                        "for ChannelPoint(%v)", chanPoint)
284

285
                // Register for a notification when the breach transaction is
286
                // confirmed on chain.
8✔
UNCOV
287
                breachTXID := retInfo.commitHash
×
UNCOV
288
                breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript
×
UNCOV
289
                confChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
×
UNCOV
290
                        &breachTXID, breachScript, 1, retInfo.breachHeight,
×
UNCOV
291
                )
×
UNCOV
292
                if err != nil {
×
293
                        brarLog.Errorf("Unable to register for conf updates "+
×
294
                                "for txid: %v, err: %v", breachTXID, err)
×
295
                        return err
×
296
                }
×
UNCOV
297

×
UNCOV
298
                // Launch a new goroutine which to finalize the channel
×
UNCOV
299
                // retribution after the breach transaction confirms.
×
UNCOV
300
                b.wg.Add(1)
×
UNCOV
301
                go b.exactRetribution(confChan, &retInfo)
×
UNCOV
302
        }
×
UNCOV
303

×
304
        // Start watching the remaining active channels!
305
        b.wg.Add(1)
306
        go b.contractObserver()
UNCOV
307

×
UNCOV
308
        return nil
×
309
}
310

311
// Stop is an idempotent method that signals the BreachArbitrator to execute a
312
// graceful shutdown. This function will block until all goroutines spawned by
8✔
313
// the BreachArbitrator have gracefully exited.
8✔
314
func (b *BreachArbitrator) Stop() error {
8✔
315
        b.stopped.Do(func() {
8✔
316
                brarLog.Infof("Breach arbiter shutting down...")
317
                defer brarLog.Debug("Breach arbiter shutdown complete")
318

319
                close(b.quit)
320
                b.wg.Wait()
321
        })
8✔
322
        return nil
16✔
323
}
8✔
324

8✔
325
// IsBreached queries the breach arbiter's retribution store to see if it is
8✔
326
// aware of any channel breaches for a particular channel point.
8✔
327
func (b *BreachArbitrator) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
8✔
328
        return b.cfg.Store.IsBreached(chanPoint)
8✔
329
}
8✔
330

331
// SubscribeBreachComplete is used by outside subsystems to be notified of a
332
// successful breach resolution.
333
func (b *BreachArbitrator) SubscribeBreachComplete(chanPoint *wire.OutPoint,
334
        c chan struct{}) (bool, error) {
12✔
335

12✔
336
        breached, err := b.cfg.Store.IsBreached(chanPoint)
12✔
337
        if err != nil {
338
                // If an error occurs, no subscription will be registered.
339
                return false, err
340
        }
UNCOV
341

×
UNCOV
342
        if !breached {
×
343
                // If chanPoint no longer exists in the Store, then the breach
×
344
                // was cleaned up successfully. Any subscription that occurs
×
345
                // happens after the breach information was persisted to the
×
346
                // underlying store.
×
347
                return true, nil
×
348
        }
UNCOV
349

×
UNCOV
350
        // Otherwise since the channel point is not resolved, add a
×
UNCOV
351
        // subscription. There can only be one subscription per channel point.
×
UNCOV
352
        b.Lock()
×
UNCOV
353
        defer b.Unlock()
×
UNCOV
354
        b.subscriptions[*chanPoint] = c
×
UNCOV
355

×
356
        return false, nil
357
}
358

UNCOV
359
// notifyBreachComplete is used by the BreachArbitrator to notify outside
×
UNCOV
360
// subsystems that the breach resolution process is complete.
×
UNCOV
361
func (b *BreachArbitrator) notifyBreachComplete(chanPoint *wire.OutPoint) {
×
UNCOV
362
        b.Lock()
×
UNCOV
363
        defer b.Unlock()
×
364
        if c, ok := b.subscriptions[*chanPoint]; ok {
365
                close(c)
366
        }
367

368
        // Remove the subscription.
4✔
369
        delete(b.subscriptions, *chanPoint)
4✔
370
}
4✔
371

4✔
UNCOV
372
// contractObserver is the primary goroutine for the BreachArbitrator. This
×
UNCOV
373
// goroutine is responsible for handling breach events coming from the
×
374
// contractcourt on the ContractBreaches channel. If a channel breach is
375
// detected, then the contractObserver will execute the retribution logic
376
// required to sweep ALL outputs from a contested channel into the daemon's
4✔
377
// wallet.
378
//
379
// NOTE: This MUST be run as a goroutine.
380
func (b *BreachArbitrator) contractObserver() {
381
        defer b.wg.Done()
382

383
        brarLog.Infof("Starting contract observer, watching for breaches.")
384

385
        for {
386
                select {
387
                case breachEvent := <-b.cfg.ContractBreaches:
8✔
388
                        // We have been notified about a contract breach!
8✔
389
                        // Handle the handoff, making sure we ACK the event
8✔
390
                        // after we have safely added it to the retribution
8✔
391
                        // store.
8✔
392
                        b.wg.Add(1)
24✔
393
                        go b.handleBreachHandoff(breachEvent)
16✔
394

8✔
395
                case <-b.quit:
8✔
396
                        return
8✔
397
                }
8✔
398
        }
8✔
399
}
8✔
400

8✔
401
// spend is used to wrap the index of the retributionInfo output that gets
402
// spent together with the spend details.
8✔
403
type spend struct {
8✔
404
        index  int
405
        detail *chainntnfs.SpendDetail
406
}
407

408
// waitForSpendEvent waits for any of the breached outputs to get spent, and
409
// returns the spend details for those outputs. The spendNtfns map is a cache
410
// used to store registered spend subscriptions, in case we must call this
411
// method multiple times.
412
func (b *BreachArbitrator) waitForSpendEvent(breachInfo *retributionInfo,
413
        spendNtfns map[wire.OutPoint]*chainntnfs.SpendEvent) ([]spend, error) {
414

415
        inputs := breachInfo.breachedOutputs
416

417
        // We create a channel the first goroutine that gets a spend event can
418
        // signal. We make it buffered in case multiple spend events come in at
419
        // the same time.
420
        anySpend := make(chan struct{}, len(inputs))
13✔
421

13✔
422
        // The allSpends channel will be used to pass spend events from all the
13✔
423
        // goroutines that detects a spend before they are signalled to exit.
13✔
424
        allSpends := make(chan spend, len(inputs))
13✔
425

13✔
426
        // exit will be used to signal the goroutines that they can exit.
13✔
427
        exit := make(chan struct{})
13✔
428
        var wg sync.WaitGroup
13✔
429

13✔
430
        // We'll now launch a goroutine for each of the HTLC outputs, that will
13✔
431
        // signal the moment they detect a spend event.
13✔
432
        for i := range inputs {
13✔
433
                breachedOutput := &inputs[i]
13✔
434

13✔
435
                brarLog.Infof("Checking spend from %v(%v) for ChannelPoint(%v)",
13✔
436
                        breachedOutput.witnessType, breachedOutput.outpoint,
13✔
437
                        breachInfo.chanPoint)
13✔
438

13✔
439
                // If we have already registered for a notification for this
40✔
440
                // output, we'll reuse it.
27✔
441
                spendNtfn, ok := spendNtfns[breachedOutput.outpoint]
27✔
442
                if !ok {
27✔
443
                        var err error
27✔
444
                        spendNtfn, err = b.cfg.Notifier.RegisterSpendNtfn(
27✔
445
                                &breachedOutput.outpoint,
27✔
446
                                breachedOutput.signDesc.Output.PkScript,
27✔
447
                                breachInfo.breachHeight,
27✔
448
                        )
27✔
449
                        if err != nil {
41✔
450
                                brarLog.Errorf("Unable to check for spentness "+
14✔
451
                                        "of outpoint=%v: %v",
14✔
452
                                        breachedOutput.outpoint, err)
14✔
453

14✔
454
                                // Registration may have failed if we've been
14✔
455
                                // instructed to shutdown. If so, return here
14✔
456
                                // to avoid entering an infinite loop.
14✔
457
                                select {
×
458
                                case <-b.quit:
×
459
                                        return nil, errBrarShuttingDown
×
460
                                default:
×
461
                                        continue
×
UNCOV
462
                                }
×
UNCOV
463
                        }
×
UNCOV
464
                        spendNtfns[breachedOutput.outpoint] = spendNtfn
×
UNCOV
465
                }
×
UNCOV
466

×
UNCOV
467
                // Launch a goroutine waiting for a spend event.
×
UNCOV
468
                b.wg.Add(1)
×
469
                wg.Add(1)
470
                go func(index int, spendEv *chainntnfs.SpendEvent) {
471
                        defer b.wg.Done()
14✔
472
                        defer wg.Done()
473

474
                        select {
475
                        // The output has been taken to the second level!
27✔
476
                        case sp, ok := <-spendEv.Spend:
27✔
477
                                if !ok {
54✔
478
                                        return
27✔
479
                                }
27✔
480

27✔
481
                                brarLog.Infof("Detected spend on %s(%v) by "+
27✔
482
                                        "txid(%v) for ChannelPoint(%v)",
483
                                        inputs[index].witnessType,
14✔
484
                                        inputs[index].outpoint,
14✔
UNCOV
485
                                        sp.SpenderTxHash,
×
UNCOV
486
                                        breachInfo.chanPoint)
×
487

488
                                // First we send the spend event on the
14✔
489
                                // allSpends channel, such that it can be
14✔
490
                                // handled after all go routines have exited.
14✔
491
                                allSpends <- spend{index, sp}
14✔
492

14✔
493
                                // Finally we'll signal the anySpend channel
14✔
494
                                // that a spend was detected, such that the
14✔
495
                                // other goroutines can be shut down.
14✔
496
                                anySpend <- struct{}{}
14✔
497
                        case <-exit:
14✔
498
                                return
14✔
499
                        case <-b.quit:
14✔
500
                                return
14✔
501
                        }
14✔
502
                }(i, spendNtfn)
14✔
503
        }
14✔
504

13✔
505
        // We'll wait for any of the outputs to be spent, or that we are
13✔
UNCOV
506
        // signalled to exit.
×
UNCOV
507
        select {
×
508
        // A goroutine have signalled that a spend occurred.
509
        case <-anySpend:
510
                // Signal for the remaining goroutines to exit.
511
                close(exit)
512
                wg.Wait()
513

514
                // At this point all goroutines that can send on the allSpends
13✔
515
                // channel have exited. We can therefore safely close the
516
                // channel before ranging over its content.
13✔
517
                close(allSpends)
13✔
518

13✔
519
                // Gather all detected spends and return them.
13✔
520
                var spends []spend
13✔
521
                for s := range allSpends {
13✔
522
                        breachedOutput := &inputs[s.index]
13✔
523
                        delete(spendNtfns, breachedOutput.outpoint)
13✔
524

13✔
525
                        spends = append(spends, s)
13✔
526
                }
13✔
527

13✔
528
                return spends, nil
27✔
529

14✔
530
        case <-b.quit:
14✔
531
                return nil, errBrarShuttingDown
14✔
532
        }
14✔
533
}
14✔
534

535
// convertToSecondLevelRevoke takes a breached output, and a transaction that
13✔
536
// spends it to the second level, and mutates the breach output into one that
UNCOV
537
// is able to properly sweep that second level output. We'll use this function
×
UNCOV
538
// when we go to sweep a breached commitment transaction, but the cheating
×
539
// party has already attempted to take it to the second level.
540
func convertToSecondLevelRevoke(bo *breachedOutput, breachInfo *retributionInfo,
541
        spendDetails *chainntnfs.SpendDetail) {
542

543
        // In this case, we'll modify the witness type of this output to
544
        // actually prepare for a second level revoke.
545
        isTaproot := txscript.IsPayToTaproot(bo.signDesc.Output.PkScript)
546
        if isTaproot {
547
                bo.witnessType = input.TaprootHtlcSecondLevelRevoke
548
        } else {
2✔
549
                bo.witnessType = input.HtlcSecondLevelRevoke
2✔
550
        }
2✔
551

2✔
552
        // We'll also redirect the outpoint to this second level output, so the
2✔
553
        // spending transaction updates it inputs accordingly.
2✔
UNCOV
554
        spendingTx := spendDetails.SpendingTx
×
555
        spendInputIndex := spendDetails.SpenderInputIndex
2✔
556
        oldOp := bo.outpoint
2✔
557
        bo.outpoint = wire.OutPoint{
2✔
558
                Hash:  spendingTx.TxHash(),
559
                Index: spendInputIndex,
560
        }
561

2✔
562
        // Next, we need to update the amount so we can do fee estimation
2✔
563
        // properly, and also so we can generate a valid signature as we need
2✔
564
        // to know the new input value (the second level transactions shaves
2✔
565
        // off some funds to fees).
2✔
566
        newAmt := spendingTx.TxOut[spendInputIndex].Value
2✔
567
        bo.amt = btcutil.Amount(newAmt)
2✔
568
        bo.signDesc.Output.Value = newAmt
2✔
569
        bo.signDesc.Output.PkScript = spendingTx.TxOut[spendInputIndex].PkScript
2✔
570

2✔
571
        // For taproot outputs, the taptweak also needs to be swapped out. We
2✔
572
        // do this unconditionally as this field isn't used at all for segwit
2✔
573
        // v0 outputs.
2✔
574
        bo.signDesc.TapTweak = bo.secondLevelTapTweak[:]
2✔
575

2✔
576
        // Finally, we'll need to adjust the witness program in the
2✔
577
        // SignDescriptor.
2✔
578
        bo.signDesc.WitnessScript = bo.secondLevelWitnessScript
2✔
579

2✔
580
        brarLog.Warnf("HTLC(%v) for ChannelPoint(%v) has been spent to the "+
2✔
581
                "second-level, adjusting -> %v", oldOp, breachInfo.chanPoint,
2✔
582
                bo.outpoint)
2✔
583
}
2✔
584

2✔
585
// updateBreachInfo mutates the passed breachInfo by removing or converting any
2✔
586
// outputs among the spends. It also counts the total and revoked funds swept
2✔
587
// by our justice spends.
2✔
588
func updateBreachInfo(breachInfo *retributionInfo, spends []spend) (
2✔
589
        btcutil.Amount, btcutil.Amount) {
2✔
590

591
        inputs := breachInfo.breachedOutputs
592
        doneOutputs := make(map[int]struct{})
593

594
        var totalFunds, revokedFunds btcutil.Amount
595
        for _, s := range spends {
596
                breachedOutput := &inputs[s.index]
13✔
597
                txIn := s.detail.SpendingTx.TxIn[s.detail.SpenderInputIndex]
13✔
598

13✔
599
                switch breachedOutput.witnessType {
13✔
600
                case input.TaprootHtlcAcceptedRevoke:
13✔
601
                        fallthrough
13✔
602
                case input.TaprootHtlcOfferedRevoke:
27✔
603
                        fallthrough
14✔
604
                case input.HtlcAcceptedRevoke:
14✔
605
                        fallthrough
14✔
606
                case input.HtlcOfferedRevoke:
14✔
UNCOV
607
                        // If the HTLC output was spent using the revocation
×
UNCOV
608
                        // key, it is our own spend, and we can forget the
×
UNCOV
609
                        // output. Otherwise it has been taken to the second
×
UNCOV
610
                        // level.
×
UNCOV
611
                        signDesc := &breachedOutput.signDesc
×
UNCOV
612
                        ok, err := input.IsHtlcSpendRevoke(txIn, signDesc)
×
613
                        if err != nil {
4✔
614
                                brarLog.Errorf("Unable to determine if "+
4✔
615
                                        "revoke spend: %v", err)
4✔
616
                                break
4✔
617
                        }
4✔
618

4✔
619
                        if ok {
4✔
620
                                brarLog.Debugf("HTLC spend was our own " +
4✔
UNCOV
621
                                        "revocation spend")
×
UNCOV
622
                                break
×
UNCOV
623
                        }
×
624

625
                        brarLog.Infof("Spend on second-level "+
626
                                "%s(%v) for ChannelPoint(%v) "+
6✔
627
                                "transitions to second-level output",
2✔
628
                                breachedOutput.witnessType,
2✔
629
                                breachedOutput.outpoint, breachInfo.chanPoint)
2✔
630

631
                        // In this case we'll morph our initial revoke
632
                        // spend to instead point to the second level
2✔
633
                        // output, and update the sign descriptor in the
2✔
634
                        // process.
2✔
635
                        convertToSecondLevelRevoke(
2✔
636
                                breachedOutput, breachInfo, s.detail,
2✔
637
                        )
2✔
638

2✔
639
                        continue
2✔
640
                }
2✔
641

2✔
642
                // Now that we have determined the spend is done by us, we
2✔
643
                // count the total and revoked funds swept depending on the
2✔
644
                // input type.
2✔
645
                switch breachedOutput.witnessType {
2✔
646
                // If the output being revoked is the remote commitment output
2✔
647
                // or an offered HTLC output, its amount contributes to the
648
                // value of funds being revoked from the counter party.
649
                case input.CommitmentRevoke, input.TaprootCommitmentRevoke,
650
                        input.HtlcSecondLevelRevoke,
651
                        input.TaprootHtlcSecondLevelRevoke,
652
                        input.TaprootHtlcOfferedRevoke, input.HtlcOfferedRevoke:
12✔
653

654
                        revokedFunds += breachedOutput.Amount()
655
                }
656

657
                totalFunds += breachedOutput.Amount()
658
                brarLog.Infof("Spend on %s(%v) for ChannelPoint(%v) "+
659
                        "transitions output to terminal state, "+
8✔
660
                        "removing input from justice transaction",
8✔
661
                        breachedOutput.witnessType,
8✔
662
                        breachedOutput.outpoint, breachInfo.chanPoint)
663

664
                doneOutputs[s.index] = struct{}{}
12✔
665
        }
12✔
666

12✔
667
        // Filter the inputs for which we can no longer proceed.
12✔
668
        var nextIndex int
12✔
669
        for i := range inputs {
12✔
670
                if _, ok := doneOutputs[i]; ok {
12✔
671
                        continue
12✔
672
                }
673

674
                inputs[nextIndex] = inputs[i]
675
                nextIndex++
13✔
676
        }
40✔
677

39✔
678
        // Update our remaining set of outputs before continuing with
12✔
679
        // another attempt at publication.
680
        breachInfo.breachedOutputs = inputs[:nextIndex]
681
        return totalFunds, revokedFunds
15✔
682
}
15✔
683

684
// exactRetribution is a goroutine which is executed once a contract breach has
685
// been detected by a breachObserver. This function is responsible for
686
// punishing a counterparty for violating the channel contract by sweeping ALL
687
// the lingering funds within the channel into the daemon's wallet.
13✔
688
//
13✔
689
// NOTE: This MUST be run as a goroutine.
690
//
691
//nolint:funlen
692
func (b *BreachArbitrator) exactRetribution(
693
        confChan *chainntnfs.ConfirmationEvent, breachInfo *retributionInfo) {
694

695
        defer b.wg.Done()
696

697
        // TODO(roasbeef): state needs to be checkpointed here
698
        select {
699
        case _, ok := <-confChan.Confirmed:
700
                // If the second value is !ok, then the channel has been closed
6✔
701
                // signifying a daemon shutdown, so we exit.
6✔
702
                if !ok {
6✔
703
                        return
6✔
704
                }
6✔
705

6✔
706
                // Otherwise, if this is a real confirmation notification, then
4✔
707
                // we fall through to complete our duty.
4✔
708
        case <-b.quit:
4✔
709
                return
4✔
UNCOV
710
        }
×
UNCOV
711

×
712
        brarLog.Debugf("Breach transaction %v has been confirmed, sweeping "+
713
                "revoked funds", breachInfo.commitHash)
714

715
        // We may have to wait for some of the HTLC outputs to be spent to the
2✔
716
        // second level before broadcasting the justice tx. We'll store the
2✔
717
        // SpendEvents between each attempt to not re-register unnecessarily.
718
        spendNtfns := make(map[wire.OutPoint]*chainntnfs.SpendEvent)
719

4✔
720
        // Compute both the total value of funds being swept and the
4✔
721
        // amount of funds that were revoked from the counter party.
4✔
722
        var totalFunds, revokedFunds btcutil.Amount
4✔
723

4✔
724
justiceTxBroadcast:
4✔
725
        // With the breach transaction confirmed, we now create the
4✔
726
        // justice tx which will claim ALL the funds within the
4✔
727
        // channel.
4✔
728
        justiceTxs, err := b.createJusticeTx(breachInfo.breachedOutputs)
4✔
729
        if err != nil {
4✔
730
                brarLog.Errorf("Unable to create justice tx: %v", err)
4✔
731
                return
4✔
732
        }
733
        finalTx := justiceTxs.spendAll
734

735
        brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure(
13✔
736
                finalTx))
13✔
UNCOV
737

×
UNCOV
738
        // We'll now attempt to broadcast the transaction which finalized the
×
UNCOV
739
        // channel's retribution against the cheating counter party.
×
740
        label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil)
13✔
741
        err = b.cfg.PublishTransaction(finalTx, label)
13✔
742
        if err != nil {
13✔
743
                brarLog.Errorf("Unable to broadcast justice tx: %v", err)
13✔
744
        }
13✔
745

13✔
746
        // Regardless of publication succeeded or not, we now wait for any of
13✔
747
        // the inputs to be spent. If any input got spent by the remote, we
13✔
UNCOV
748
        // must recreate our justice transaction.
×
UNCOV
749
        var (
×
UNCOV
750
                spendChan = make(chan []spend, 1)
×
UNCOV
751
                errChan   = make(chan error, 1)
×
UNCOV
752
                wg        sync.WaitGroup
×
UNCOV
753
        )
×
UNCOV
754

×
UNCOV
755
        wg.Add(1)
×
UNCOV
756
        go func() {
×
UNCOV
757
                defer wg.Done()
×
758

13✔
UNCOV
759
                spends, err := b.waitForSpendEvent(breachInfo, spendNtfns)
×
UNCOV
760
                if err != nil {
×
UNCOV
761
                        errChan <- err
×
762
                        return
763
                }
764
                spendChan <- spends
765
        }()
13✔
766

13✔
767
        // We'll also register for block notifications, such that in case our
23✔
768
        // justice tx doesn't confirm within a reasonable timeframe, we can
10✔
769
        // start to more aggressively sweep the time sensitive outputs.
10✔
770
        newBlockChan, err := b.cfg.Notifier.RegisterBlockEpochNtfn(nil)
771
        if err != nil {
772
                brarLog.Errorf("Unable to register for block notifications: %v",
773
                        err)
774
                return
13✔
775
        }
13✔
776
        defer newBlockChan.Cancel()
13✔
777

13✔
778
Loop:
13✔
779
        for {
13✔
780
                select {
13✔
781
                case spends := <-spendChan:
26✔
782
                        // Update the breach info with the new spends.
13✔
783
                        t, r := updateBreachInfo(breachInfo, spends)
13✔
784
                        totalFunds += t
13✔
785
                        revokedFunds += r
13✔
UNCOV
786

×
UNCOV
787
                        brarLog.Infof("%v spends from breach tx for "+
×
UNCOV
788
                                "ChannelPoint(%v) has been detected, %v "+
×
789
                                "revoked funds (%v total) have been claimed",
13✔
790
                                len(spends), breachInfo.chanPoint,
791
                                revokedFunds, totalFunds)
792

793
                        if len(breachInfo.breachedOutputs) == 0 {
794
                                brarLog.Infof("Justice for ChannelPoint(%v) "+
795
                                        "has been served, %v revoked funds "+
13✔
796
                                        "(%v total) have been claimed. No "+
13✔
UNCOV
797
                                        "more outputs to sweep, marking fully "+
×
UNCOV
798
                                        "resolved", breachInfo.chanPoint,
×
UNCOV
799
                                        revokedFunds, totalFunds)
×
UNCOV
800

×
801
                                err = b.cleanupBreach(&breachInfo.chanPoint)
13✔
802
                                if err != nil {
13✔
803
                                        brarLog.Errorf("Failed to cleanup "+
13✔
804
                                                "breached ChannelPoint(%v): %v",
31✔
805
                                                breachInfo.chanPoint, err)
18✔
806
                                }
13✔
807

13✔
808
                                // TODO(roasbeef): add peer to blacklist?
13✔
809

13✔
810
                                // TODO(roasbeef): close other active channels
13✔
811
                                // with offending peer
13✔
812
                                break Loop
13✔
813
                        }
13✔
814

13✔
815
                        brarLog.Infof("Attempting another justice tx "+
13✔
816
                                "with %d inputs",
13✔
817
                                len(breachInfo.breachedOutputs))
13✔
818

17✔
819
                        wg.Wait()
4✔
820
                        goto justiceTxBroadcast
4✔
821

4✔
822
                // On every new block, we check whether we should republish the
4✔
823
                // transactions.
4✔
824
                case epoch, ok := <-newBlockChan.Epochs:
4✔
825
                        if !ok {
4✔
826
                                return
4✔
827
                        }
4✔
UNCOV
828

×
UNCOV
829
                        // If less than four blocks have passed since the
×
UNCOV
830
                        // breach confirmed, we'll continue waiting. It was
×
UNCOV
831
                        // published with a 2-block fee estimate, so it's not
×
832
                        // unexpected that four blocks without confirmation can
833
                        // pass.
834
                        splitHeight := breachInfo.breachHeight +
835
                                blocksPassedSplitPublish
836
                        if uint32(epoch.Height) < splitHeight {
837
                                continue Loop
4✔
838
                        }
839

840
                        brarLog.Warnf("Block height %v arrived without "+
9✔
841
                                "justice tx confirming (breached at "+
9✔
842
                                "height %v), splitting justice tx.",
9✔
843
                                epoch.Height, breachInfo.breachHeight)
9✔
844

9✔
845
                        // Otherwise we'll attempt to publish the two separate
9✔
846
                        // justice transactions that sweeps the commitment
847
                        // outputs and the HTLC outputs separately. This is to
848
                        // mitigate the case where our "spend all" justice TX
849
                        // doesn't propagate because the HTLC outputs have been
5✔
850
                        // pinned by low fee HTLC txs.
5✔
UNCOV
851
                        label := labels.MakeLabel(
×
UNCOV
852
                                labels.LabelTypeJusticeTransaction, nil,
×
853
                        )
854
                        if justiceTxs.spendCommitOuts != nil {
855
                                tx := justiceTxs.spendCommitOuts
856

857
                                brarLog.Debugf("Broadcasting justice tx "+
858
                                        "spending commitment outs: %v",
859
                                        lnutils.SpewLogClosure(tx))
5✔
860

5✔
861
                                err = b.cfg.PublishTransaction(tx, label)
9✔
862
                                if err != nil {
4✔
863
                                        brarLog.Warnf("Unable to broadcast "+
864
                                                "commit out spending justice "+
865
                                                "tx: %v", err)
1✔
866
                                }
1✔
867
                        }
1✔
868

1✔
869
                        if justiceTxs.spendHTLCs != nil {
1✔
870
                                tx := justiceTxs.spendHTLCs
1✔
871

1✔
872
                                brarLog.Debugf("Broadcasting justice tx "+
1✔
873
                                        "spending HTLC outs: %v",
1✔
874
                                        lnutils.SpewLogClosure(tx))
1✔
875

1✔
876
                                err = b.cfg.PublishTransaction(tx, label)
1✔
877
                                if err != nil {
1✔
878
                                        brarLog.Warnf("Unable to broadcast "+
1✔
879
                                                "HTLC out spending justice "+
2✔
880
                                                "tx: %v", err)
1✔
881
                                }
1✔
882
                        }
1✔
883

1✔
884
                        for _, tx := range justiceTxs.spendSecondLevelHTLCs {
1✔
885
                                tx := tx
1✔
886

1✔
887
                                brarLog.Debugf("Broadcasting justice tx "+
1✔
888
                                        "spending second-level HTLC output: %v",
1✔
889
                                        lnutils.SpewLogClosure(tx))
1✔
890

×
891
                                err = b.cfg.PublishTransaction(tx, label)
×
892
                                if err != nil {
×
893
                                        brarLog.Warnf("Unable to broadcast "+
×
894
                                                "second-level HTLC out "+
895
                                                "spending justice tx: %v", err)
896
                                }
2✔
897
                        }
1✔
898

1✔
899
                case err := <-errChan:
1✔
900
                        if err != errBrarShuttingDown {
1✔
901
                                brarLog.Errorf("error waiting for "+
1✔
902
                                        "spend event: %v", err)
1✔
903
                        }
1✔
904
                        break Loop
1✔
905

1✔
906
                case <-b.quit:
1✔
UNCOV
907
                        break Loop
×
UNCOV
908
                }
×
UNCOV
909
        }
×
UNCOV
910

×
911
        // Wait for our go routine to exit.
912
        wg.Wait()
913
}
1✔
UNCOV
914

×
UNCOV
915
// cleanupBreach marks the given channel point as fully resolved and removes the
×
UNCOV
916
// retribution for that the channel from the retribution store.
×
UNCOV
917
func (b *BreachArbitrator) cleanupBreach(chanPoint *wire.OutPoint) error {
×
UNCOV
918
        // With the channel closed, mark it in the database as such.
×
UNCOV
919
        err := b.cfg.DB.MarkChanFullyClosed(chanPoint)
×
UNCOV
920
        if err != nil {
×
921
                return fmt.Errorf("unable to mark chan as closed: %w", err)
×
922
        }
×
UNCOV
923

×
UNCOV
924
        // Justice has been carried out; we can safely delete the retribution
×
UNCOV
925
        // info from the database.
×
UNCOV
926
        err = b.cfg.Store.Remove(chanPoint)
×
UNCOV
927
        if err != nil {
×
928
                return fmt.Errorf("unable to remove retribution from db: %w",
929
                        err)
930
        }
×
UNCOV
931

×
UNCOV
932
        // This is after the Remove call so that the chan passed in via
×
UNCOV
933
        // SubscribeBreachComplete is always notified, no matter when it is
×
UNCOV
934
        // called. Otherwise, if notifyBreachComplete was before Remove, a
×
UNCOV
935
        // very rare edge case could occur in which SubscribeBreachComplete
×
936
        // is called after notifyBreachComplete and before Remove, meaning the
UNCOV
937
        // caller would never be notified.
×
UNCOV
938
        b.notifyBreachComplete(chanPoint)
×
939

940
        return nil
941
}
942

943
// handleBreachHandoff handles a new breach event, by writing it to disk, then
4✔
944
// notifies the BreachArbitrator contract observer goroutine that a channel's
945
// contract has been breached by the prior counterparty. Once notified the
946
// BreachArbitrator will attempt to sweep ALL funds within the channel using the
947
// information provided within the BreachRetribution generated due to the
948
// breach of channel contract. The funds will be swept only after the breaching
4✔
949
// transaction receives a necessary number of confirmations.
4✔
950
//
4✔
951
// NOTE: This MUST be run as a goroutine.
4✔
UNCOV
952
func (b *BreachArbitrator) handleBreachHandoff(
×
UNCOV
953
        breachEvent *ContractBreachEvent) {
×
954

955
        defer b.wg.Done()
956

957
        chanPoint := breachEvent.ChanPoint
4✔
958
        brarLog.Debugf("Handling breach handoff for ChannelPoint(%v)",
4✔
UNCOV
959
                chanPoint)
×
UNCOV
960

×
UNCOV
961
        // A read from this channel indicates that a channel breach has been
×
962
        // detected! So we notify the main coordination goroutine with the
963
        // information needed to bring the counterparty to justice.
964
        breachInfo := breachEvent.BreachRetribution
965
        brarLog.Warnf("REVOKED STATE #%v FOR ChannelPoint(%v) "+
966
                "broadcast, REMOTE PEER IS DOING SOMETHING "+
967
                "SKETCHY!!!", breachInfo.RevokedStateNum,
968
                chanPoint)
969

4✔
970
        // Immediately notify the HTLC switch that this link has been
4✔
971
        // breached in order to ensure any incoming or outgoing
4✔
972
        // multi-hop HTLCs aren't sent over this link, nor any other
973
        // links associated with this peer.
974
        b.cfg.CloseLink(&chanPoint, CloseBreach)
975

976
        // TODO(roasbeef): need to handle case of remote broadcast
977
        // mid-local initiated state-transition, possible
978
        // false-positive?
979

980
        // Acquire the mutex to ensure consistency between the call to
981
        // IsBreached and Add below.
982
        b.Lock()
983

984
        // We first check if this breach info is already added to the
8✔
985
        // retribution store.
8✔
986
        breached, err := b.cfg.Store.IsBreached(&chanPoint)
8✔
987
        if err != nil {
8✔
988
                b.Unlock()
8✔
989
                brarLog.Errorf("Unable to check breach info in DB: %v", err)
8✔
990

8✔
991
                // Notify about the failed lookup and return.
8✔
992
                breachEvent.ProcessACK(err)
8✔
993
                return
8✔
994
        }
8✔
995

8✔
996
        // If this channel is already marked as breached in the retribution
8✔
997
        // store, we already have handled the handoff for this breach. In this
8✔
998
        // case we can safely ACK the handoff, and return.
8✔
999
        if breached {
8✔
1000
                b.Unlock()
8✔
1001
                breachEvent.ProcessACK(nil)
8✔
1002
                return
8✔
1003
        }
8✔
1004

8✔
1005
        // Using the breach information provided by the wallet and the
8✔
1006
        // channel snapshot, construct the retribution information that
8✔
1007
        // will be persisted to disk.
8✔
1008
        retInfo := newRetributionInfo(&chanPoint, breachInfo)
8✔
1009

8✔
1010
        // Persist the pending retribution state to disk.
8✔
1011
        err = b.cfg.Store.Add(retInfo)
8✔
1012
        b.Unlock()
8✔
1013
        if err != nil {
8✔
1014
                brarLog.Errorf("Unable to persist retribution "+
8✔
1015
                        "info to db: %v", err)
8✔
1016
        }
8✔
1017

8✔
1018
        // Now that the breach has been persisted, try to send an
8✔
UNCOV
1019
        // acknowledgment back to the close observer with the error. If
×
UNCOV
1020
        // the ack is successful, the close observer will mark the
×
UNCOV
1021
        // channel as pending-closed in the channeldb.
×
UNCOV
1022
        breachEvent.ProcessACK(err)
×
UNCOV
1023

×
UNCOV
1024
        // Bail if we failed to persist retribution info.
×
UNCOV
1025
        if err != nil {
×
1026
                return
1027
        }
1028

1029
        // Now that a new channel contract has been added to the retribution
1030
        // store, we first register for a notification to be dispatched once
9✔
1031
        // the breach transaction (the revoked commitment transaction) has been
1✔
1032
        // confirmed in the chain to ensure we're not dealing with a moving
1✔
1033
        // target.
1✔
1034
        breachTXID := &retInfo.commitHash
1✔
1035
        breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript
1036
        cfChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(
1037
                breachTXID, breachScript, 1, retInfo.breachHeight,
1038
        )
1039
        if err != nil {
7✔
1040
                brarLog.Errorf("Unable to register for conf updates for "+
7✔
1041
                        "txid: %v, err: %v", breachTXID, err)
7✔
1042
                return
7✔
1043
        }
7✔
1044

8✔
1045
        brarLog.Warnf("A channel has been breached with txid: %v. Waiting "+
1✔
1046
                "for confirmation, then justice will be served!", breachTXID)
1✔
1047

1✔
1048
        // With the retribution state persisted, channel close persisted, and
1049
        // notification registered, we launch a new goroutine which will
1050
        // finalize the channel retribution after the breach transaction has
1051
        // been confirmed.
1052
        b.wg.Add(1)
1053
        go b.exactRetribution(cfChan, retInfo)
7✔
1054
}
7✔
1055

7✔
1056
// breachedOutput contains all the information needed to sweep a breached
8✔
1057
// output. A breached output is an output that we are now entitled to due to a
1✔
1058
// revoked commitment transaction being broadcast.
1✔
1059
type breachedOutput struct {
1060
        amt         btcutil.Amount
1061
        outpoint    wire.OutPoint
1062
        witnessType input.StandardWitnessType
1063
        signDesc    input.SignDescriptor
1064
        confHeight  uint32
1065

6✔
1066
        secondLevelWitnessScript []byte
6✔
1067
        secondLevelTapTweak      [32]byte
6✔
1068

6✔
1069
        witnessFunc input.WitnessGenerator
6✔
1070
}
6✔
UNCOV
1071

×
UNCOV
1072
// makeBreachedOutput assembles a new breachedOutput that can be used by the
×
UNCOV
1073
// breach arbiter to construct a justice or sweep transaction.
×
UNCOV
1074
func makeBreachedOutput(outpoint *wire.OutPoint,
×
1075
        witnessType input.StandardWitnessType,
1076
        secondLevelScript []byte,
6✔
1077
        signDescriptor *input.SignDescriptor,
6✔
1078
        confHeight uint32) breachedOutput {
6✔
1079

6✔
1080
        amount := signDescriptor.Output.Value
6✔
1081

6✔
1082
        return breachedOutput{
6✔
1083
                amt:                      btcutil.Amount(amount),
6✔
1084
                outpoint:                 *outpoint,
6✔
1085
                secondLevelWitnessScript: secondLevelScript,
1086
                witnessType:              witnessType,
1087
                signDesc:                 *signDescriptor,
1088
                confHeight:               confHeight,
1089
        }
1090
}
1091

1092
// Amount returns the number of satoshis contained in the breached output.
1093
func (bo *breachedOutput) Amount() btcutil.Amount {
1094
        return bo.amt
1095
}
1096

1097
// OutPoint returns the breached output's identifier that is to be included as a
1098
// transaction input.
1099
func (bo *breachedOutput) OutPoint() wire.OutPoint {
1100
        return bo.outpoint
1101
}
1102

1103
// RequiredTxOut returns a non-nil TxOut if input commits to a certain
1104
// transaction output. This is used in the SINGLE|ANYONECANPAY case to make
1105
// sure any presigned input is still valid by including the output.
1106
func (bo *breachedOutput) RequiredTxOut() *wire.TxOut {
1107
        return nil
1108
}
1109

1110
// RequiredLockTime returns whether this input commits to a tx locktime that
1111
// must be used in the transaction including it.
1112
func (bo *breachedOutput) RequiredLockTime() (uint32, bool) {
49✔
1113
        return 0, false
49✔
1114
}
49✔
1115

49✔
1116
// WitnessType returns the type of witness that must be generated to spend the
49✔
1117
// breached output.
49✔
1118
func (bo *breachedOutput) WitnessType() input.WitnessType {
49✔
1119
        return bo.witnessType
49✔
1120
}
49✔
1121

49✔
1122
// SignDesc returns the breached output's SignDescriptor, which is used during
49✔
1123
// signing to compute the witness.
49✔
1124
func (bo *breachedOutput) SignDesc() *input.SignDescriptor {
49✔
1125
        return &bo.signDesc
49✔
1126
}
1127

1128
// CraftInputScript computes a valid witness that allows us to spend from the
172✔
1129
// breached output. It does so by first generating and memoizing the witness
172✔
1130
// generation function, which parameterized primarily by the witness type and
172✔
1131
// sign descriptor. The method then returns the witness computed by invoking
1132
// this function on the first and subsequent calls.
1133
func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
1134
        hashCache *txscript.TxSigHashes,
398✔
1135
        prevOutputFetcher txscript.PrevOutputFetcher,
398✔
1136
        txinIdx int) (*input.Script, error) {
398✔
1137

1138
        // First, we ensure that the witness generation function has been
1139
        // initialized for this breached output.
1140
        signDesc := bo.SignDesc()
UNCOV
1141
        signDesc.PrevOutputFetcher = prevOutputFetcher
×
UNCOV
1142
        bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, signDesc)
×
UNCOV
1143

×
1144
        // Now that we have ensured that the witness generation function has
1145
        // been initialized, we can proceed to execute it and generate the
1146
        // witness for this particular breached output.
UNCOV
1147
        return bo.witnessFunc(txn, hashCache, txinIdx)
×
UNCOV
1148
}
×
UNCOV
1149

×
1150
// BlocksToMaturity returns the relative timelock, as a number of blocks, that
1151
// must be built on top of the confirmation height before the output can be
1152
// spent.
1153
func (bo *breachedOutput) BlocksToMaturity() uint32 {
126✔
1154
        // If the output is a to_remote output we can claim, and it's of the
126✔
1155
        // confirmed type (or is a taproot channel that always has the CSV 1),
126✔
1156
        // we must wait one block before claiming it.
1157
        switch bo.witnessType {
1158
        case input.CommitmentToRemoteConfirmed, input.TaprootRemoteCommitSpend:
1159
                return 1
361✔
1160
        }
361✔
1161

361✔
1162
        // All other breached outputs have no CSV delay.
1163
        return 0
1164
}
1165

1166
// HeightHint returns the minimum height at which a confirmed spending tx can
1167
// occur.
1168
func (bo *breachedOutput) HeightHint() uint32 {
1169
        return bo.confHeight
1170
}
1171

71✔
1172
// UnconfParent returns information about a possibly unconfirmed parent tx.
71✔
1173
func (bo *breachedOutput) UnconfParent() *input.TxInfo {
71✔
1174
        return nil
71✔
1175
}
71✔
1176

71✔
1177
// Add compile-time constraint ensuring breachedOutput implements the Input
71✔
1178
// interface.
71✔
1179
var _ input.Input = (*breachedOutput)(nil)
71✔
1180

71✔
1181
// retributionInfo encapsulates all the data needed to sweep all the contested
71✔
1182
// funds within a channel whose contract has been breached by the prior
71✔
1183
// counterparty. This struct is used to create the justice transaction which
71✔
1184
// spends all outputs of the commitment transaction into an output controlled
1185
// by the wallet.
1186
type retributionInfo struct {
1187
        commitHash   chainhash.Hash
1188
        chanPoint    wire.OutPoint
68✔
1189
        chainHash    chainhash.Hash
68✔
1190
        breachHeight uint32
68✔
1191

68✔
1192
        breachedOutputs []breachedOutput
68✔
1193
}
2✔
1194

2✔
1195
// newRetributionInfo constructs a retributionInfo containing all the
1196
// information required by the breach arbiter to recover funds from breached
1197
// channels.  The information is primarily populated using the BreachRetribution
1198
// delivered by the wallet when it detects a channel breach.
66✔
1199
func newRetributionInfo(chanPoint *wire.OutPoint,
1200
        breachInfo *lnwallet.BreachRetribution) *retributionInfo {
1201

1202
        // Determine the number of second layer HTLCs we will attempt to sweep.
UNCOV
1203
        nHtlcs := len(breachInfo.HtlcRetributions)
×
UNCOV
1204

×
UNCOV
1205
        // Initialize a slice to hold the outputs we will attempt to sweep. The
×
1206
        // maximum capacity of the slice is set to 2+nHtlcs to handle the case
1207
        // where the local, remote, and all HTLCs are not dust outputs.  All
UNCOV
1208
        // HTLC outputs provided by the wallet are guaranteed to be non-dust,
×
UNCOV
1209
        // though the commitment outputs are conditionally added depending on
×
UNCOV
1210
        // the nil-ness of their sign descriptors.
×
1211
        breachedOutputs := make([]breachedOutput, 0, nHtlcs+2)
1212

1213
        isTaproot := func() bool {
1214
                if breachInfo.LocalOutputSignDesc != nil {
37✔
1215
                        return txscript.IsPayToTaproot(
37✔
1216
                                breachInfo.LocalOutputSignDesc.Output.PkScript,
37✔
1217
                        )
1218
                }
1219

1220
                return txscript.IsPayToTaproot(
1221
                        breachInfo.RemoteOutputSignDesc.Output.PkScript,
1222
                )
1223
        }()
1224

1225
        // First, record the breach information for the local channel point if
1226
        // it is not considered dust, which is signaled by a non-nil sign
1227
        // descriptor. Here we use CommitmentNoDelay (or
1228
        // CommitmentNoDelayTweakless for newer commitments) since this output
1229
        // belongs to us and has no time-based constraints on spending. For
1230
        // taproot channels, this is a normal spend from our output on the
1231
        // commitment of the remote party.
1232
        if breachInfo.LocalOutputSignDesc != nil {
1233
                var witnessType input.StandardWitnessType
1234
                switch {
1235
                case isTaproot:
1236
                        witnessType = input.TaprootRemoteCommitSpend
1237

1238
                case !isTaproot &&
1239
                        breachInfo.LocalOutputSignDesc.SingleTweak == nil:
1240

1241
                        witnessType = input.CommitSpendNoDelayTweakless
10✔
1242

10✔
1243
                case !isTaproot:
10✔
1244
                        witnessType = input.CommitmentNoDelay
10✔
1245
                }
10✔
1246

10✔
1247
                // If the local delay is non-zero, it means this output is of
10✔
1248
                // the confirmed to_remote type.
10✔
1249
                if !isTaproot && breachInfo.LocalDelay != 0 {
10✔
1250
                        witnessType = input.CommitmentToRemoteConfirmed
10✔
1251
                }
10✔
1252

10✔
1253
                localOutput := makeBreachedOutput(
10✔
1254
                        &breachInfo.LocalOutpoint,
20✔
1255
                        witnessType,
20✔
1256
                        // No second level script as this is a commitment
10✔
1257
                        // output.
10✔
1258
                        nil,
10✔
1259
                        breachInfo.LocalOutputSignDesc,
10✔
1260
                        breachInfo.BreachHeight,
UNCOV
1261
                )
×
UNCOV
1262

×
UNCOV
1263
                breachedOutputs = append(breachedOutputs, localOutput)
×
1264
        }
1265

1266
        // Second, record the same information regarding the remote outpoint,
1267
        // again if it is not dust, which belongs to the party who tried to
1268
        // steal our money! Here we set witnessType of the breachedOutput to
1269
        // CommitmentRevoke, since we will be using a revoke key, withdrawing
1270
        // the funds from the commitment transaction immediately.
1271
        if breachInfo.RemoteOutputSignDesc != nil {
1272
                var witType input.StandardWitnessType
1273
                if isTaproot {
20✔
1274
                        witType = input.TaprootCommitmentRevoke
10✔
1275
                } else {
10✔
UNCOV
1276
                        witType = input.CommitmentRevoke
×
UNCOV
1277
                }
×
1278

1279
                remoteOutput := makeBreachedOutput(
1280
                        &breachInfo.RemoteOutpoint,
10✔
1281
                        witType,
10✔
1282
                        // No second level script as this is a commitment
10✔
1283
                        // output.
UNCOV
1284
                        nil,
×
UNCOV
1285
                        breachInfo.RemoteOutputSignDesc,
×
1286
                        breachInfo.BreachHeight,
1287
                )
1288

1289
                breachedOutputs = append(breachedOutputs, remoteOutput)
1290
        }
10✔
UNCOV
1291

×
UNCOV
1292
        // Lastly, for each of the breached HTLC outputs, record each as a
×
1293
        // breached output with the appropriate witness type based on its
1294
        // directionality. All HTLC outputs provided by the wallet are assumed
10✔
1295
        // to be non-dust.
10✔
1296
        for i, breachedHtlc := range breachInfo.HtlcRetributions {
10✔
1297
                // Using the breachedHtlc's incoming flag, determine the
10✔
1298
                // appropriate witness type that needs to be generated in order
10✔
1299
                // to sweep the HTLC output.
10✔
1300
                var htlcWitnessType input.StandardWitnessType
10✔
1301
                switch {
10✔
1302
                case isTaproot && breachedHtlc.IsIncoming:
10✔
1303
                        htlcWitnessType = input.TaprootHtlcAcceptedRevoke
10✔
1304

10✔
1305
                case isTaproot && !breachedHtlc.IsIncoming:
10✔
1306
                        htlcWitnessType = input.TaprootHtlcOfferedRevoke
1307

1308
                case !isTaproot && breachedHtlc.IsIncoming:
1309
                        htlcWitnessType = input.HtlcAcceptedRevoke
1310

1311
                case !isTaproot && !breachedHtlc.IsIncoming:
1312
                        htlcWitnessType = input.HtlcOfferedRevoke
1313
                }
17✔
1314

7✔
1315
                htlcOutput := makeBreachedOutput(
7✔
UNCOV
1316
                        &breachInfo.HtlcRetributions[i].OutPoint,
×
1317
                        htlcWitnessType,
7✔
1318
                        breachInfo.HtlcRetributions[i].SecondLevelWitnessScript,
7✔
1319
                        &breachInfo.HtlcRetributions[i].SignDesc,
7✔
1320
                        breachInfo.BreachHeight,
1321
                )
7✔
1322

7✔
1323
                // For taproot outputs, we also need to hold onto the second
7✔
1324
                // level tap tweak as well.
7✔
1325
                //nolint:lll
7✔
1326
                htlcOutput.secondLevelTapTweak = breachedHtlc.SecondLevelTapTweak
7✔
1327

7✔
1328
                breachedOutputs = append(breachedOutputs, htlcOutput)
7✔
1329
        }
7✔
1330

7✔
1331
        return &retributionInfo{
7✔
1332
                commitHash:      breachInfo.BreachTxHash,
7✔
1333
                chainHash:       breachInfo.ChainHash,
1334
                chanPoint:       *chanPoint,
1335
                breachedOutputs: breachedOutputs,
1336
                breachHeight:    breachInfo.BreachHeight,
1337
        }
1338
}
1339

17✔
1340
// justiceTxVariants is a struct that holds transactions which exacts "justice"
7✔
1341
// by sweeping ALL the funds within the channel which we are now entitled to
7✔
1342
// due to a breach of the channel's contract by the counterparty. There are
7✔
1343
// four variants of justice transactions:
7✔
1344
//
7✔
UNCOV
1345
// 1. The "normal" justice tx that spends all breached outputs.
×
UNCOV
1346
// 2. A tx that spends only the breached to_local output and to_remote output
×
1347
// (can be nil if none of these exist).
UNCOV
1348
// 3. A tx that spends all the breached commitment level HTLC outputs (can be
×
UNCOV
1349
// nil if none of these exist or if all have been taken to the second level).
×
1350
// 4. A set of txs that spend all the second-level HTLC outputs (can be empty if
UNCOV
1351
// no HTLC second-level txs have been confirmed).
×
UNCOV
1352
//
×
1353
// The reason we create these three variants, is that in certain cases (like
1354
// with the anchor output HTLC malleability), the channel counter party can pin
7✔
1355
// the HTLC outputs with low fee children, hindering our normal justice tx that
7✔
1356
// attempts to spend these outputs from propagating. In this case we want to
1357
// spend the to_local output and commitment level HTLC outputs separately,
1358
// before the CSV locks expire.
7✔
1359
type justiceTxVariants struct {
7✔
1360
        spendAll              *wire.MsgTx
7✔
1361
        spendCommitOuts       *wire.MsgTx
7✔
1362
        spendHTLCs            *wire.MsgTx
7✔
1363
        spendSecondLevelHTLCs []*wire.MsgTx
7✔
1364
}
7✔
1365

7✔
1366
// createJusticeTx creates transactions which exacts "justice" by sweeping ALL
7✔
1367
// the funds within the channel which we are now entitled to due to a breach of
7✔
1368
// the channel's contract by the counterparty. This function returns a *fully*
7✔
1369
// signed transaction with the witness for each input fully in place.
7✔
1370
func (b *BreachArbitrator) createJusticeTx(
7✔
1371
        breachedOutputs []breachedOutput) (*justiceTxVariants, error) {
7✔
1372

7✔
1373
        var (
1374
                allInputs         []input.Input
1375
                commitInputs      []input.Input
10✔
1376
                htlcInputs        []input.Input
10✔
1377
                secondLevelInputs []input.Input
10✔
1378
        )
10✔
1379

10✔
1380
        for i := range breachedOutputs {
10✔
1381
                // Grab locally scoped reference to breached output.
10✔
1382
                inp := &breachedOutputs[i]
1383
                allInputs = append(allInputs, inp)
1384

1385
                // Check if the input is from a commitment output, a commitment
1386
                // level HTLC output or a second level HTLC output.
1387
                switch inp.WitnessType() {
1388
                case input.HtlcAcceptedRevoke, input.HtlcOfferedRevoke,
1389
                        input.TaprootHtlcAcceptedRevoke,
1390
                        input.TaprootHtlcOfferedRevoke:
1391

1392
                        htlcInputs = append(htlcInputs, inp)
1393

1394
                case input.HtlcSecondLevelRevoke,
1395
                        input.TaprootHtlcSecondLevelRevoke:
1396

1397
                        secondLevelInputs = append(secondLevelInputs, inp)
1398

1399
                default:
1400
                        commitInputs = append(commitInputs, inp)
1401
                }
1402
        }
1403

1404
        var (
1405
                txs = &justiceTxVariants{}
1406
                err error
1407
        )
1408

1409
        // For each group of inputs, create a tx that spends them.
1410
        txs.spendAll, err = b.createSweepTx(allInputs...)
1411
        if err != nil {
1412
                return nil, err
1413
        }
1414

1415
        txs.spendCommitOuts, err = b.createSweepTx(commitInputs...)
14✔
1416
        if err != nil {
14✔
1417
                brarLog.Errorf("could not create sweep tx for commitment "+
14✔
1418
                        "outputs: %v", err)
14✔
1419
        }
14✔
1420

14✔
1421
        txs.spendHTLCs, err = b.createSweepTx(htlcInputs...)
14✔
1422
        if err != nil {
14✔
1423
                brarLog.Errorf("could not create sweep tx for HTLC outputs: %v",
14✔
1424
                        err)
48✔
1425
        }
34✔
1426

34✔
1427
        secondLevelSweeps := make([]*wire.MsgTx, 0, len(secondLevelInputs))
34✔
1428
        for _, input := range secondLevelInputs {
34✔
1429
                sweepTx, err := b.createSweepTx(input)
34✔
1430
                if err != nil {
34✔
1431
                        brarLog.Errorf("could not create sweep tx for "+
34✔
1432
                                "second-level HTLC output: %v", err)
1433

1434
                        continue
8✔
1435
                }
8✔
1436

8✔
1437
                secondLevelSweeps = append(secondLevelSweeps, sweepTx)
1438
        }
1439
        txs.spendSecondLevelHTLCs = secondLevelSweeps
4✔
1440

4✔
1441
        return txs, nil
4✔
1442
}
1443

22✔
1444
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
22✔
1445
func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
1446
        error) {
1447

1448
        if len(inputs) == 0 {
14✔
1449
                return nil, nil
14✔
1450
        }
14✔
1451

14✔
1452
        // We will assemble the breached outputs into a slice of spendable
14✔
1453
        // outputs, while simultaneously computing the estimated weight of the
14✔
1454
        // transaction.
14✔
1455
        var (
14✔
UNCOV
1456
                spendableOutputs []input.Input
×
UNCOV
1457
                weightEstimate   input.TxWeightEstimator
×
1458
        )
1459

14✔
1460
        // Allocate enough space to potentially hold each of the breached
14✔
UNCOV
1461
        // outputs in the retribution info.
×
UNCOV
1462
        spendableOutputs = make([]input.Input, 0, len(inputs))
×
UNCOV
1463

×
1464
        // The justice transaction we construct will be a segwit transaction
1465
        // that pays to a p2tr output. Components such as the version,
14✔
1466
        // nLockTime, and output are already included in the TxWeightEstimator.
14✔
UNCOV
1467
        weightEstimate.AddP2TROutput()
×
UNCOV
1468

×
UNCOV
1469
        // Next, we iterate over the breached outputs contained in the
×
1470
        // retribution info.  For each, we switch over the witness type such
1471
        // that we contribute the appropriate weight for each input and
1472
        // witness, finally adding to our list of spendable outputs.
1473
        for i := range inputs {
14✔
1474
                // Grab locally scoped reference to breached output.
18✔
1475
                inp := inputs[i]
4✔
1476

4✔
UNCOV
1477
                // First, determine the appropriate estimated witness weight
×
UNCOV
1478
                // for the give witness type of this breached output. If the
×
UNCOV
1479
                // witness weight cannot be estimated, we will omit it from the
×
UNCOV
1480
                // transaction.
×
1481
                witnessWeight, _, err := inp.WitnessType().SizeUpperBound()
1482
                if err != nil {
1483
                        brarLog.Warnf("could not determine witness weight "+
4✔
1484
                                "for breached output in retribution info: %v",
1485
                                err)
14✔
1486
                        continue
14✔
1487
                }
14✔
1488
                weightEstimate.AddWitnessInput(witnessWeight)
1489

1490
                // Finally, append this input to our list of spendable outputs.
1491
                spendableOutputs = append(spendableOutputs, inp)
1492
        }
1493

1494
        txWeight := weightEstimate.Weight()
1495

1496
        return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...)
1497
}
1498

1499
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
1500
// spendable outputs by sweeping the funds into a single p2wkh output.
1501
func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
1502
        inputs ...input.Input) (*wire.MsgTx, error) {
1503

1504
        // First, we obtain a new public key script from the wallet which we'll
1505
        // sweep the funds to.
1506
        // TODO(roasbeef): possibly create many outputs to minimize change in
46✔
1507
        // the future?
46✔
1508
        pkScript, err := b.cfg.GenSweepScript()
55✔
1509
        if err != nil {
9✔
1510
                return nil, err
9✔
1511
        }
1512

1513
        // Compute the total amount contained in the inputs.
1514
        var totalAmt btcutil.Amount
1515
        for _, inp := range inputs {
37✔
1516
                totalAmt += btcutil.Amount(inp.SignDesc().Output.Value)
37✔
1517
        }
37✔
1518

37✔
1519
        // We'll actually attempt to target inclusion within the next two
37✔
1520
        // blocks as we'd like to sweep these funds back into our wallet ASAP.
37✔
1521
        feePerKw, err := b.cfg.Estimator.EstimateFeePerKW(justiceTxConfTarget)
37✔
1522
        if err != nil {
37✔
1523
                return nil, err
37✔
1524
        }
37✔
1525
        txFee := feePerKw.FeeForWeight(txWeight)
37✔
1526

37✔
1527
        // TODO(roasbeef): already start to siphon their funds into fees
37✔
1528
        sweepAmt := int64(totalAmt - txFee)
37✔
1529

37✔
1530
        // With the fee calculated, we can now create the transaction using the
37✔
1531
        // information gathered above and the provided retribution information.
37✔
1532
        txn := wire.NewMsgTx(2)
37✔
1533

37✔
1534
        // We begin by adding the output to which our funds will be deposited.
74✔
1535
        txn.AddTxOut(&wire.TxOut{
37✔
1536
                PkScript: pkScript,
37✔
1537
                Value:    sweepAmt,
63✔
1538
        })
26✔
1539

26✔
1540
        // Next, we add all of the spendable outputs as inputs to the
1541
        // transaction.
1542
        for _, inp := range inputs {
1543
                txn.AddTxIn(&wire.TxIn{
1544
                        PreviousOutPoint: inp.OutPoint(),
1545
                        Sequence:         inp.BlocksToMaturity(),
105✔
1546
                })
68✔
1547
        }
68✔
1548

68✔
1549
        // Before signing the transaction, check to ensure that it meets some
68✔
1550
        // basic validity requirements.
68✔
1551
        btx := btcutil.NewTx(txn)
68✔
1552
        if err := blockchain.CheckTransactionSanity(btx); err != nil {
68✔
1553
                return nil, err
68✔
1554
        }
68✔
UNCOV
1555

×
UNCOV
1556
        // Create a sighash cache to improve the performance of hashing and
×
UNCOV
1557
        // signing SigHashAll inputs.
×
UNCOV
1558
        prevOutputFetcher, err := input.MultiPrevOutFetcher(inputs)
×
1559
        if err != nil {
1560
                return nil, err
68✔
1561
        }
68✔
1562
        hashCache := txscript.NewTxSigHashes(txn, prevOutputFetcher)
68✔
1563

68✔
1564
        // Create a closure that encapsulates the process of initializing a
1565
        // particular output's witness generation function, computing the
1566
        // witness, and attaching it to the transaction. This function accepts
37✔
1567
        // an integer index representing the intended txin index, and the
37✔
1568
        // breached output from which it will spend.
37✔
1569
        addWitness := func(idx int, so input.Input) error {
1570
                // First, we construct a valid witness for this outpoint and
1571
                // transaction using the SpendableOutput's witness generation
1572
                // function.
1573
                inputScript, err := so.CraftInputScript(
1574
                        b.cfg.Signer, txn, hashCache, prevOutputFetcher, idx,
37✔
1575
                )
37✔
1576
                if err != nil {
37✔
1577
                        return err
37✔
1578
                }
37✔
1579

37✔
1580
                // Then, we add the witness to the transaction at the
37✔
1581
                // appropriate txin index.
37✔
UNCOV
1582
                txn.TxIn[idx].Witness = inputScript.Witness
×
UNCOV
1583

×
1584
                return nil
1585
        }
1586

37✔
1587
        // Finally, generate a witness for each output and attach it to the
105✔
1588
        // transaction.
68✔
1589
        for i, inp := range inputs {
68✔
1590
                if err := addWitness(i, inp); err != nil {
1591
                        return nil, err
1592
                }
1593
        }
37✔
1594

37✔
UNCOV
1595
        return txn, nil
×
UNCOV
1596
}
×
1597

37✔
1598
// RetributionStore handles persistence of retribution states to disk and is
37✔
1599
// backed by a boltdb bucket. The primary responsibility of the retribution
37✔
1600
// store is to ensure that we can recover from a restart in the middle of a
37✔
1601
// breached contract retribution.
37✔
1602
type RetributionStore struct {
37✔
1603
        db kvdb.Backend
37✔
UNCOV
1604
}
×
UNCOV
1605

×
1606
// NewRetributionStore creates a new instance of a RetributionStore.
1607
func NewRetributionStore(db kvdb.Backend) *RetributionStore {
37✔
UNCOV
1608
        return &RetributionStore{
×
UNCOV
1609
                db: db,
×
1610
        }
1611
}
1612

37✔
1613
// taprootBriefcaseFromRetInfo creates a taprootBriefcase from a retribution
37✔
1614
// info struct. This stores all the tap tweak informatoin we need to inrder to
37✔
1615
// be able to hadnel breaches after a restart.
37✔
1616
func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
37✔
1617
        tapCase := newTaprootBriefcase()
37✔
1618

37✔
1619
        for _, bo := range retInfo.breachedOutputs {
37✔
1620
                switch bo.WitnessType() {
37✔
UNCOV
1621
                // For spending from our commitment output on the remote
×
UNCOV
1622
                // commitment, we'll need to stash the control block.
×
UNCOV
1623
                case input.TaprootRemoteCommitSpend:
×
UNCOV
1624
                        //nolint:lll
×
UNCOV
1625
                        tapCase.CtrlBlocks.CommitSweepCtrlBlock = bo.signDesc.ControlBlock
×
1626

1627
                // To spend the revoked output again, we'll store the same
1628
                // control block value as above, but in a different place.
1629
                case input.TaprootCommitmentRevoke:
37✔
1630
                        //nolint:lll
37✔
1631
                        tapCase.CtrlBlocks.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock
37✔
1632

37✔
1633
                // For spending the HTLC outputs, we'll store the first and
37✔
1634
                // second level tweak values.
37✔
1635
                case input.TaprootHtlcAcceptedRevoke:
37✔
1636
                        fallthrough
37✔
1637
                case input.TaprootHtlcOfferedRevoke:
37✔
1638
                        resID := newResolverID(bo.OutPoint())
105✔
1639

68✔
1640
                        var firstLevelTweak [32]byte
68✔
1641
                        copy(firstLevelTweak[:], bo.signDesc.TapTweak)
68✔
1642
                        secondLevelTweak := bo.secondLevelTapTweak
68✔
1643

68✔
1644
                        //nolint:lll
1645
                        tapCase.TapTweaks.BreachedHtlcTweaks[resID] = firstLevelTweak
1646

1647
                        //nolint:lll
37✔
1648
                        tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak
37✔
UNCOV
1649
                }
×
UNCOV
1650
        }
×
1651

1652
        return tapCase
1653
}
1654

37✔
1655
// applyTaprootRetInfo attaches the taproot specific inforamtion in the tapCase
37✔
UNCOV
1656
// to the passed retInfo struct.
×
UNCOV
1657
func applyTaprootRetInfo(tapCase *taprootBriefcase,
×
1658
        retInfo *retributionInfo) error {
37✔
1659

37✔
1660
        for i := range retInfo.breachedOutputs {
37✔
1661
                bo := retInfo.breachedOutputs[i]
37✔
1662

37✔
1663
                switch bo.WitnessType() {
37✔
1664
                // For spending from our commitment output on the remote
37✔
1665
                // commitment, we'll apply the control block.
105✔
1666
                case input.TaprootRemoteCommitSpend:
68✔
1667
                        //nolint:lll
68✔
1668
                        bo.signDesc.ControlBlock = tapCase.CtrlBlocks.CommitSweepCtrlBlock
68✔
1669

68✔
1670
                // To spend the revoked output again, we'll apply the same
68✔
1671
                // control block value as above, but to a different place.
68✔
1672
                case input.TaprootCommitmentRevoke:
68✔
UNCOV
1673
                        //nolint:lll
×
UNCOV
1674
                        bo.signDesc.ControlBlock = tapCase.CtrlBlocks.RevokeSweepCtrlBlock
×
1675

1676
                // For spending the HTLC outputs, we'll apply the first and
1677
                // second level tweak values.
1678
                case input.TaprootHtlcAcceptedRevoke:
68✔
1679
                        fallthrough
68✔
1680
                case input.TaprootHtlcOfferedRevoke:
68✔
1681
                        resID := newResolverID(bo.OutPoint())
1682

1683
                        tap1, ok := tapCase.TapTweaks.BreachedHtlcTweaks[resID]
1684
                        if !ok {
1685
                                return fmt.Errorf("unable to find taproot "+
105✔
1686
                                        "tweak for: %v", bo.OutPoint())
68✔
1687
                        }
×
UNCOV
1688
                        bo.signDesc.TapTweak = tap1[:]
×
1689

1690
                        //nolint:lll
1691
                        tap2, ok := tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID]
37✔
1692
                        if !ok {
37✔
1693
                                return fmt.Errorf("unable to find taproot "+
37✔
1694
                                        "tweak for: %v", bo.OutPoint())
37✔
1695
                        }
37✔
1696
                        bo.secondLevelTapTweak = tap2
37✔
1697
                }
37✔
1698

1699
                retInfo.breachedOutputs[i] = bo
1700
        }
1701

1702
        return nil
1703
}
1704

1705
// Add adds a retribution state to the RetributionStore, which is then persisted
1706
// to disk.
1707
func (rs *RetributionStore) Add(ret *retributionInfo) error {
1708
        return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
1709
                // If this is our first contract breach, the retributionBucket
19✔
1710
                // won't exist, in which case, we just create a new bucket.
19✔
1711
                retBucket, err := tx.CreateTopLevelBucket(retributionBucket)
19✔
1712
                if err != nil {
19✔
1713
                        return err
19✔
1714
                }
1715
                tapRetBucket, err := tx.CreateTopLevelBucket(
1716
                        taprootRetributionBucket,
1717
                )
UNCOV
1718
                if err != nil {
×
1719
                        return err
×
1720
                }
×
UNCOV
1721

×
UNCOV
1722
                var outBuf bytes.Buffer
×
1723
                if err := writeOutpoint(&outBuf, &ret.chanPoint); err != nil {
1724
                        return err
1725
                }
×
UNCOV
1726

×
UNCOV
1727
                var retBuf bytes.Buffer
×
UNCOV
1728
                if err := ret.Encode(&retBuf); err != nil {
×
1729
                        return err
×
1730
                }
×
UNCOV
1731

×
UNCOV
1732
                err = retBucket.Put(outBuf.Bytes(), retBuf.Bytes())
×
UNCOV
1733
                if err != nil {
×
1734
                        return err
×
1735
                }
×
1736

1737
                // If this isn't a taproot channel, then we can exit early here
1738
                // as there's no extra data to write.
UNCOV
1739
                switch {
×
1740
                case len(ret.breachedOutputs) == 0:
×
1741
                        return nil
×
UNCOV
1742

×
UNCOV
1743
                case !txscript.IsPayToTaproot(
×
UNCOV
1744
                        ret.breachedOutputs[0].signDesc.Output.PkScript,
×
UNCOV
1745
                ):
×
UNCOV
1746
                        return nil
×
UNCOV
1747
                }
×
UNCOV
1748

×
UNCOV
1749
                // We'll also map the ret info into the taproot storage
×
1750
                // structure we need for taproot channels.
1751
                var b bytes.Buffer
1752
                tapRetcase := taprootBriefcaseFromRetInfo(ret)
UNCOV
1753
                if err := tapRetcase.Encode(&b); err != nil {
×
1754
                        return err
×
1755
                }
×
UNCOV
1756

×
UNCOV
1757
                return tapRetBucket.Put(outBuf.Bytes(), b.Bytes())
×
UNCOV
1758
        }, func() {})
×
UNCOV
1759
}
×
UNCOV
1760

×
UNCOV
1761
// IsBreached queries the retribution store to discern if this channel was
×
UNCOV
1762
// previously breached. This is used when connecting to a peer to determine if
×
UNCOV
1763
// it is safe to add a link to the htlcswitch, as we should never add a channel
×
UNCOV
1764
// that has already been breached.
×
UNCOV
1765
func (rs *RetributionStore) IsBreached(chanPoint *wire.OutPoint) (bool, error) {
×
UNCOV
1766
        var found bool
×
1767
        err := kvdb.View(rs.db, func(tx kvdb.RTx) error {
1768
                retBucket := tx.ReadBucket(retributionBucket)
1769
                if retBucket == nil {
UNCOV
1770
                        return nil
×
1771
                }
1772

1773
                var chanBuf bytes.Buffer
1774
                if err := writeOutpoint(&chanBuf, chanPoint); err != nil {
1775
                        return err
1776
                }
×
UNCOV
1777

×
UNCOV
1778
                retInfo := retBucket.Get(chanBuf.Bytes())
×
UNCOV
1779
                if retInfo != nil {
×
UNCOV
1780
                        found = true
×
UNCOV
1781
                }
×
1782

1783
                return nil
UNCOV
1784
        }, func() {
×
UNCOV
1785
                found = false
×
UNCOV
1786
        })
×
UNCOV
1787

×
UNCOV
1788
        return found, err
×
UNCOV
1789
}
×
UNCOV
1790

×
UNCOV
1791
// Remove removes a retribution state and finalized justice transaction by
×
1792
// channel point  from the retribution store.
1793
func (rs *RetributionStore) Remove(chanPoint *wire.OutPoint) error {
1794
        return kvdb.Update(rs.db, func(tx kvdb.RwTx) error {
1795
                retBucket := tx.ReadWriteBucket(retributionBucket)
UNCOV
1796
                tapRetBucket, err := tx.CreateTopLevelBucket(
×
UNCOV
1797
                        taprootRetributionBucket,
×
UNCOV
1798
                )
×
UNCOV
1799
                if err != nil {
×
1800
                        return err
×
1801
                }
×
UNCOV
1802

×
UNCOV
1803
                // We return an error if the bucket is not already created,
×
1804
                // since normal operation of the breach arbiter should never
1805
                // try to remove a finalized retribution state that is not
1806
                // already stored in the db.
1807
                if retBucket == nil {
UNCOV
1808
                        return errors.New("unable to remove retribution " +
×
UNCOV
1809
                                "because the retribution bucket doesn't exist")
×
UNCOV
1810
                }
×
UNCOV
1811

×
UNCOV
1812
                // Serialize the channel point we are intending to remove.
×
UNCOV
1813
                var chanBuf bytes.Buffer
×
UNCOV
1814
                if err := writeOutpoint(&chanBuf, chanPoint); err != nil {
×
1815
                        return err
×
1816
                }
×
UNCOV
1817
                chanBytes := chanBuf.Bytes()
×
UNCOV
1818

×
UNCOV
1819
                // Remove the persisted retribution info and finalized justice
×
UNCOV
1820
                // transaction.
×
UNCOV
1821
                if err := retBucket.Delete(chanBytes); err != nil {
×
1822
                        return err
×
1823
                }
×
UNCOV
1824

×
UNCOV
1825
                return tapRetBucket.Delete(chanBytes)
×
UNCOV
1826
        }, func() {})
×
UNCOV
1827
}
×
1828

1829
// ForAll iterates through all stored retributions and executes the passed
UNCOV
1830
// callback function on each retribution.
×
1831
func (rs *RetributionStore) ForAll(cb func(*retributionInfo) error,
1832
        reset func()) error {
UNCOV
1833

×
1834
        return kvdb.View(rs.db, func(tx kvdb.RTx) error {
1835
                // If the bucket does not exist, then there are no pending
1836
                // retributions.
1837
                retBucket := tx.ReadBucket(retributionBucket)
1838
                if retBucket == nil {
14✔
1839
                        return nil
28✔
1840
                }
14✔
1841
                tapRetBucket := tx.ReadBucket(
14✔
1842
                        taprootRetributionBucket,
14✔
1843
                )
14✔
UNCOV
1844

×
UNCOV
1845
                // Otherwise, we fetch each serialized retribution info,
×
1846
                // deserialize it, and execute the passed in callback function
14✔
1847
                // on it.
14✔
1848
                return retBucket.ForEach(func(k, retBytes []byte) error {
14✔
1849
                        ret := &retributionInfo{}
14✔
UNCOV
1850
                        err := ret.Decode(bytes.NewBuffer(retBytes))
×
UNCOV
1851
                        if err != nil {
×
1852
                                return err
1853
                        }
14✔
1854

14✔
UNCOV
1855
                        tapInfoBytes := tapRetBucket.Get(k)
×
UNCOV
1856
                        if tapInfoBytes != nil {
×
1857
                                var tapCase taprootBriefcase
1858
                                err := tapCase.Decode(
14✔
1859
                                        bytes.NewReader(tapInfoBytes),
14✔
UNCOV
1860
                                )
×
UNCOV
1861
                                if err != nil {
×
1862
                                        return err
1863
                                }
14✔
1864

14✔
UNCOV
1865
                                err = applyTaprootRetInfo(&tapCase, ret)
×
UNCOV
1866
                                if err != nil {
×
1867
                                        return err
1868
                                }
1869
                        }
1870

14✔
UNCOV
1871
                        return cb(ret)
×
UNCOV
1872
                })
×
1873
        }, reset)
1874
}
1875

1876
// Encode serializes the retribution into the passed byte stream.
14✔
1877
func (ret *retributionInfo) Encode(w io.Writer) error {
14✔
1878
        var scratch [4]byte
1879

1880
        if _, err := w.Write(ret.commitHash[:]); err != nil {
1881
                return err
1882
        }
×
UNCOV
1883

×
UNCOV
1884
        if err := writeOutpoint(w, &ret.chanPoint); err != nil {
×
1885
                return err
×
1886
        }
×
1887

UNCOV
1888
        if _, err := w.Write(ret.chainHash[:]); err != nil {
×
1889
                return err
14✔
1890
        }
1891

1892
        binary.BigEndian.PutUint32(scratch[:], ret.breachHeight)
1893
        if _, err := w.Write(scratch[:]); err != nil {
1894
                return err
1895
        }
1896

20✔
1897
        nOutputs := len(ret.breachedOutputs)
20✔
1898
        if err := wire.WriteVarInt(w, 0, uint64(nOutputs)); err != nil {
40✔
1899
                return err
20✔
1900
        }
28✔
1901

8✔
1902
        for _, output := range ret.breachedOutputs {
8✔
1903
                if err := output.Encode(w); err != nil {
1904
                        return err
12✔
1905
                }
12✔
UNCOV
1906
        }
×
UNCOV
1907

×
1908
        return nil
1909
}
12✔
1910

20✔
1911
// Decode deserializes a retribution from the passed byte stream.
8✔
1912
func (ret *retributionInfo) Decode(r io.Reader) error {
8✔
1913
        var scratch [32]byte
1914

12✔
1915
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
20✔
1916
                return err
20✔
1917
        }
20✔
1918
        hash, err := chainhash.NewHash(scratch[:])
1919
        if err != nil {
20✔
1920
                return err
1921
        }
1922
        ret.commitHash = *hash
1923

1924
        if err := readOutpoint(r, &ret.chanPoint); err != nil {
10✔
1925
                return err
20✔
1926
        }
10✔
1927

10✔
1928
        if _, err := io.ReadFull(r, scratch[:]); err != nil {
10✔
1929
                return err
10✔
1930
        }
10✔
UNCOV
1931
        chainHash, err := chainhash.NewHash(scratch[:])
×
UNCOV
1932
        if err != nil {
×
1933
                return err
1934
        }
1935
        ret.chainHash = *chainHash
1936

1937
        if _, err := io.ReadFull(r, scratch[:4]); err != nil {
1938
                return err
12✔
1939
        }
2✔
1940
        ret.breachHeight = binary.BigEndian.Uint32(scratch[:4])
2✔
1941

2✔
1942
        nOutputsU64, err := wire.ReadVarInt(r, 0)
1943
        if err != nil {
1944
                return err
8✔
1945
        }
8✔
UNCOV
1946
        nOutputs := int(nOutputsU64)
×
UNCOV
1947

×
1948
        ret.breachedOutputs = make([]breachedOutput, nOutputs)
8✔
1949
        for i := range ret.breachedOutputs {
8✔
1950
                if err := ret.breachedOutputs[i].Decode(r); err != nil {
8✔
1951
                        return err
8✔
1952
                }
8✔
UNCOV
1953
        }
×
UNCOV
1954

×
1955
        return nil
1956
}
8✔
1957

10✔
1958
// Encode serializes a breachedOutput into the passed byte stream.
1959
func (bo *breachedOutput) Encode(w io.Writer) error {
1960
        var scratch [8]byte
1961

1962
        binary.BigEndian.PutUint64(scratch[:8], uint64(bo.amt))
1963
        if _, err := w.Write(scratch[:8]); err != nil {
46✔
1964
                return err
46✔
1965
        }
92✔
1966

46✔
1967
        if err := writeOutpoint(w, &bo.outpoint); err != nil {
46✔
1968
                return err
46✔
1969
        }
64✔
1970

18✔
1971
        err := input.WriteSignDescriptor(w, &bo.signDesc)
18✔
1972
        if err != nil {
28✔
1973
                return err
28✔
1974
        }
28✔
1975

28✔
1976
        err = wire.WriteVarBytes(w, 0, bo.secondLevelWitnessScript)
28✔
1977
        if err != nil {
28✔
1978
                return err
28✔
1979
        }
66✔
1980

38✔
1981
        binary.BigEndian.PutUint16(scratch[:2], uint16(bo.witnessType))
38✔
1982
        if _, err := w.Write(scratch[:2]); err != nil {
38✔
1983
                return err
×
1984
        }
×
1985

1986
        return nil
38✔
1987
}
38✔
UNCOV
1988

×
UNCOV
1989
// Decode deserializes a breachedOutput from the passed byte stream.
×
UNCOV
1990
func (bo *breachedOutput) Decode(r io.Reader) error {
×
UNCOV
1991
        var scratch [8]byte
×
UNCOV
1992

×
UNCOV
1993
        if _, err := io.ReadFull(r, scratch[:8]); err != nil {
×
1994
                return err
×
1995
        }
UNCOV
1996
        bo.amt = btcutil.Amount(binary.BigEndian.Uint64(scratch[:8]))
×
UNCOV
1997

×
UNCOV
1998
        if err := readOutpoint(r, &bo.outpoint); err != nil {
×
1999
                return err
×
2000
        }
2001

2002
        if err := input.ReadSignDescriptor(r, &bo.signDesc); err != nil {
38✔
2003
                return err
2004
        }
2005

2006
        wScript, err := wire.ReadVarBytes(r, 0, 1000, "witness script")
2007
        if err != nil {
2008
                return err
16✔
2009
        }
16✔
2010
        bo.secondLevelWitnessScript = wScript
16✔
2011

16✔
UNCOV
2012
        if _, err := io.ReadFull(r, scratch[:2]); err != nil {
×
2013
                return err
×
2014
        }
2015
        bo.witnessType = input.StandardWitnessType(
16✔
UNCOV
2016
                binary.BigEndian.Uint16(scratch[:2]),
×
UNCOV
2017
        )
×
2018

2019
        return nil
16✔
UNCOV
2020
}
×
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