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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

70.57
/contractcourt/briefcase.go
1
package contractcourt
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "fmt"
7
        "io"
8

9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/txscript"
11
        "github.com/btcsuite/btcd/wire"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/fn/v2"
14
        "github.com/lightningnetwork/lnd/input"
15
        "github.com/lightningnetwork/lnd/kvdb"
16
        "github.com/lightningnetwork/lnd/lnwallet"
17
        "github.com/lightningnetwork/lnd/tlv"
18
)
19

20
// ContractResolutions is a wrapper struct around the two forms of resolutions
21
// we may need to carry out once a contract is closing: resolving the
22
// commitment output, and resolving any incoming+outgoing HTLC's still present
23
// in the commitment.
24
type ContractResolutions struct {
25
        // CommitHash is the txid of the commitment transaction.
26
        CommitHash chainhash.Hash
27

28
        // CommitResolution contains all data required to fully resolve a
29
        // commitment output.
30
        CommitResolution *lnwallet.CommitOutputResolution
31

32
        // HtlcResolutions contains all data required to fully resolve any
33
        // incoming+outgoing HTLC's present within the commitment transaction.
34
        HtlcResolutions lnwallet.HtlcResolutions
35

36
        // AnchorResolution contains the data required to sweep the anchor
37
        // output. If the channel type doesn't include anchors, the value of
38
        // this field will be nil.
39
        AnchorResolution *lnwallet.AnchorResolution
40

41
        // BreachResolution contains the data required to manage the lifecycle
42
        // of a breach in the ChannelArbitrator.
43
        BreachResolution *BreachResolution
44
}
45

46
// IsEmpty returns true if the set of resolutions is "empty". A resolution is
47
// empty if: our commitment output has been trimmed, we don't have any
48
// incoming or outgoing HTLC's active, there is no anchor output to sweep, or
49
// there are no breached outputs to resolve.
50
func (c *ContractResolutions) IsEmpty() bool {
3✔
51
        return c.CommitResolution == nil &&
3✔
52
                len(c.HtlcResolutions.IncomingHTLCs) == 0 &&
3✔
53
                len(c.HtlcResolutions.OutgoingHTLCs) == 0 &&
3✔
54
                c.AnchorResolution == nil && c.BreachResolution == nil
3✔
55
}
3✔
56

57
// ArbitratorLog is the primary source of persistent storage for the
58
// ChannelArbitrator. The log stores the current state of the
59
// ChannelArbitrator's internal state machine, any items that are required to
60
// properly make a state transition, and any unresolved contracts.
61
type ArbitratorLog interface {
62
        // TODO(roasbeef): document on interface the errors expected to be
63
        // returned
64

65
        // CurrentState returns the current state of the ChannelArbitrator. It
66
        // takes an optional database transaction, which will be used if it is
67
        // non-nil, otherwise the lookup will be done in its own transaction.
68
        CurrentState(tx kvdb.RTx) (ArbitratorState, error)
69

70
        // CommitState persists, the current state of the chain attendant.
71
        CommitState(ArbitratorState) error
72

73
        // InsertUnresolvedContracts inserts a set of unresolved contracts into
74
        // the log. The log will then persistently store each contract until
75
        // they've been swapped out, or resolved. It takes a set of report which
76
        // should be written to disk if as well if it is non-nil.
77
        InsertUnresolvedContracts(reports []*channeldb.ResolverReport,
78
                resolvers ...ContractResolver) error
79

80
        // FetchUnresolvedContracts returns all unresolved contracts that have
81
        // been previously written to the log.
82
        FetchUnresolvedContracts() ([]ContractResolver, error)
83

84
        // SwapContract performs an atomic swap of the old contract for the new
85
        // contract. This method is used when after a contract has been fully
86
        // resolved, it produces another contract that needs to be resolved.
87
        SwapContract(old ContractResolver, new ContractResolver) error
88

89
        // ResolveContract marks a contract as fully resolved. Once a contract
90
        // has been fully resolved, it is deleted from persistent storage.
91
        ResolveContract(ContractResolver) error
92

93
        // LogContractResolutions stores a complete contract resolution for the
94
        // contract under watch. This method will be called once the
95
        // ChannelArbitrator either force closes a channel, or detects that the
96
        // remote party has broadcast their commitment on chain.
97
        LogContractResolutions(*ContractResolutions) error
98

99
        // FetchContractResolutions fetches the set of previously stored
100
        // contract resolutions from persistent storage.
101
        FetchContractResolutions() (*ContractResolutions, error)
102

103
        // InsertConfirmedCommitSet stores the known set of active HTLCs at the
104
        // time channel closure. We'll use this to reconstruct our set of chain
105
        // actions anew based on the confirmed and pending commitment state.
106
        InsertConfirmedCommitSet(c *CommitSet) error
107

108
        // FetchConfirmedCommitSet fetches the known confirmed active HTLC set
109
        // from the database. It takes an optional database transaction, which
110
        // will be used if it is non-nil, otherwise the lookup will be done in
111
        // its own transaction.
112
        FetchConfirmedCommitSet(tx kvdb.RTx) (*CommitSet, error)
113

114
        // FetchChainActions attempts to fetch the set of previously stored
115
        // chain actions. We'll use this upon restart to properly advance our
116
        // state machine forward.
117
        //
118
        // NOTE: This method only exists in order to be able to serve nodes had
119
        // channels in the process of closing before the CommitSet struct was
120
        // introduced.
121
        FetchChainActions() (ChainActionMap, error)
122

123
        // WipeHistory is to be called ONLY once *all* contracts have been
124
        // fully resolved, and the channel closure if finalized. This method
125
        // will delete all on-disk state within the persistent log.
126
        WipeHistory() error
127
}
128

129
// ArbitratorState is an enum that details the current state of the
130
// ChannelArbitrator's state machine.
131
type ArbitratorState uint8
132

133
const (
134
        // While some state transition is allowed, certain transitions are not
135
        // possible. Listed below is the full state transition map which
136
        // contains all possible paths. We start at StateDefault and end at
137
        // StateFullyResolved, or StateError(not listed as its a possible state
138
        // in every path). The format is,
139
        //         -> state: conditions we switch to this state.
140
        //
141
        // StateDefault
142
        // |
143
        // |-> StateDefault: no actions and chain trigger
144
        // |
145
        // |-> StateBroadcastCommit: chain/user trigger
146
        // |   |
147
        // |   |-> StateCommitmentBroadcasted: chain/user trigger
148
        // |   |   |
149
        // |   |   |-> StateCommitmentBroadcasted: chain/user trigger
150
        // |   |   |
151
        // |   |   |-> StateContractClosed: local/remote/breach close trigger
152
        // |   |   |   |
153
        // |   |   |   |-> StateWaitingFullResolution: contract resolutions not empty
154
        // |   |   |   |   |
155
        // |   |   |   |   |-> StateWaitingFullResolution: contract resolutions not empty
156
        // |   |   |   |   |
157
        // |   |   |   |   |-> StateFullyResolved: contract resolutions empty
158
        // |   |   |   |
159
        // |   |   |   |-> StateFullyResolved: contract resolutions empty
160
        // |   |   |
161
        // |   |   |-> StateFullyResolved: coop/breach(legacy) close trigger
162
        // |   |
163
        // |   |-> StateContractClosed: local/remote/breach close trigger
164
        // |   |   |
165
        // |   |   |-> StateWaitingFullResolution: contract resolutions not empty
166
        // |   |   |   |
167
        // |   |   |   |-> StateWaitingFullResolution: contract resolutions not empty
168
        // |   |   |   |
169
        // |   |   |   |-> StateFullyResolved: contract resolutions empty
170
        // |   |   |
171
        // |   |   |-> StateFullyResolved: contract resolutions empty
172
        // |   |
173
        // |   |-> StateFullyResolved: coop/breach(legacy) close trigger
174
        // |
175
        // |-> StateContractClosed: local/remote/breach close trigger
176
        // |   |
177
        // |   |-> StateWaitingFullResolution: contract resolutions not empty
178
        // |   |   |
179
        // |   |   |-> StateWaitingFullResolution: contract resolutions not empty
180
        // |   |   |
181
        // |   |   |-> StateFullyResolved: contract resolutions empty
182
        // |   |
183
        // |   |-> StateFullyResolved: contract resolutions empty
184
        // |
185
        // |-> StateFullyResolved: coop/breach(legacy) close trigger.
186

187
        // StateDefault is the default state. In this state, no major actions
188
        // need to be executed.
189
        StateDefault ArbitratorState = 0
190

191
        // StateBroadcastCommit is a state that indicates that the attendant
192
        // has decided to broadcast the commitment transaction, but hasn't done
193
        // so yet.
194
        StateBroadcastCommit ArbitratorState = 1
195

196
        // StateCommitmentBroadcasted is a state that indicates that the
197
        // attendant has broadcasted the commitment transaction, and is now
198
        // waiting for it to confirm.
199
        StateCommitmentBroadcasted ArbitratorState = 6
200

201
        // StateContractClosed is a state that indicates the contract has
202
        // already been "closed", meaning the commitment is confirmed on chain.
203
        // At this point, we can now examine our active contracts, in order to
204
        // create the proper resolver for each one.
205
        StateContractClosed ArbitratorState = 2
206

207
        // StateWaitingFullResolution is a state that indicates that the
208
        // commitment transaction has been confirmed, and the attendant is now
209
        // waiting for all unresolved contracts to be fully resolved.
210
        StateWaitingFullResolution ArbitratorState = 3
211

212
        // StateFullyResolved is the final state of the attendant. In this
213
        // state, all related contracts have been resolved, and the attendant
214
        // can now be garbage collected.
215
        StateFullyResolved ArbitratorState = 4
216

217
        // StateError is the only error state of the resolver. If we enter this
218
        // state, then we cannot proceed with manual intervention as a state
219
        // transition failed.
220
        StateError ArbitratorState = 5
221
)
222

223
// String returns a human readable string describing the ArbitratorState.
224
func (a ArbitratorState) String() string {
3✔
225
        switch a {
3✔
226
        case StateDefault:
3✔
227
                return "StateDefault"
3✔
228

229
        case StateBroadcastCommit:
3✔
230
                return "StateBroadcastCommit"
3✔
231

232
        case StateCommitmentBroadcasted:
3✔
233
                return "StateCommitmentBroadcasted"
3✔
234

235
        case StateContractClosed:
3✔
236
                return "StateContractClosed"
3✔
237

238
        case StateWaitingFullResolution:
3✔
239
                return "StateWaitingFullResolution"
3✔
240

241
        case StateFullyResolved:
3✔
242
                return "StateFullyResolved"
3✔
243

244
        case StateError:
×
245
                return "StateError"
×
246

247
        default:
×
248
                return "unknown state"
×
249
        }
250
}
251

252
// IsContractClosed returns a bool to indicate whether the closing/breaching tx
253
// has been confirmed onchain. If the state is StateContractClosed,
254
// StateWaitingFullResolution, or StateFullyResolved, it means the contract has
255
// been closed and all related contracts have been launched.
256
func (a ArbitratorState) IsContractClosed() bool {
3✔
257
        return a == StateContractClosed || a == StateWaitingFullResolution ||
3✔
258
                a == StateFullyResolved
3✔
259
}
3✔
260

261
// resolverType is an enum that enumerates the various types of resolvers. When
262
// writing resolvers to disk, we prepend this to the raw bytes stored. This
263
// allows us to properly decode the resolver into the proper type.
264
type resolverType uint8
265

266
const (
267
        // resolverTimeout is the type of a resolver that's tasked with
268
        // resolving an outgoing HTLC that is very close to timing out.
269
        resolverTimeout resolverType = 0
270

271
        // resolverSuccess is the type of a resolver that's tasked with
272
        // resolving an incoming HTLC that we already know the preimage of.
273
        resolverSuccess resolverType = 1
274

275
        // resolverOutgoingContest is the type of a resolver that's tasked with
276
        // resolving an outgoing HTLC that hasn't yet timed out.
277
        resolverOutgoingContest resolverType = 2
278

279
        // resolverIncomingContest is the type of a resolver that's tasked with
280
        // resolving an incoming HTLC that we don't yet know the preimage to.
281
        resolverIncomingContest resolverType = 3
282

283
        // resolverUnilateralSweep is the type of resolver that's tasked with
284
        // sweeping out direct commitment output form the remote party's
285
        // commitment transaction.
286
        resolverUnilateralSweep resolverType = 4
287

288
        // resolverBreach is the type of resolver that manages a contract
289
        // breach on-chain.
290
        resolverBreach resolverType = 5
291
)
292

293
// resolverIDLen is the size of the resolver ID key. This is 36 bytes as we get
294
// 32 bytes from the hash of the prev tx, and 4 bytes for the output index.
295
const resolverIDLen = 36
296

297
// resolverID is a key that uniquely identifies a resolver within a particular
298
// chain. For this value we use the full outpoint of the resolver.
299
type resolverID [resolverIDLen]byte
300

301
// newResolverID returns a resolverID given the outpoint of a contract.
302
func newResolverID(op wire.OutPoint) resolverID {
3✔
303
        var r resolverID
3✔
304

3✔
305
        copy(r[:], op.Hash[:])
3✔
306

3✔
307
        endian.PutUint32(r[32:], op.Index)
3✔
308

3✔
309
        return r
3✔
310
}
3✔
311

312
// logScope is a key that we use to scope the storage of a ChannelArbitrator
313
// within the global log. We use this key to create a unique bucket within the
314
// database and ensure that we don't have any key collisions. The log's scope
315
// is define as: chainHash || chanPoint, where chanPoint is the chan point of
316
// the original channel.
317
type logScope [32 + 36]byte
318

319
// newLogScope creates a new logScope key from the passed chainhash and
320
// chanPoint.
321
func newLogScope(chain chainhash.Hash, op wire.OutPoint) (*logScope, error) {
3✔
322
        var l logScope
3✔
323
        b := bytes.NewBuffer(l[0:0])
3✔
324

3✔
325
        if _, err := b.Write(chain[:]); err != nil {
3✔
326
                return nil, err
×
327
        }
×
328
        if _, err := b.Write(op.Hash[:]); err != nil {
3✔
329
                return nil, err
×
330
        }
×
331

332
        if err := binary.Write(b, endian, op.Index); err != nil {
3✔
333
                return nil, err
×
334
        }
×
335

336
        return &l, nil
3✔
337
}
338

339
var (
340
        // stateKey is the key that we use to store the current state of the
341
        // arbitrator.
342
        stateKey = []byte("state")
343

344
        // contractsBucketKey is the bucket within the logScope that will store
345
        // all the active unresolved contracts.
346
        contractsBucketKey = []byte("contractkey")
347

348
        // resolutionsKey is the key under the logScope that we'll use to store
349
        // the full set of resolutions for a channel.
350
        resolutionsKey = []byte("resolutions")
351

352
        // resolutionsSignDetailsKey is the key under the logScope where we
353
        // will store input.SignDetails for each HTLC resolution. If this is
354
        // not found under the logScope, it means it was written before
355
        // SignDetails was introduced, and should be set nil for each HTLC
356
        // resolution.
357
        resolutionsSignDetailsKey = []byte("resolutions-sign-details")
358

359
        // anchorResolutionKey is the key under the logScope that we'll use to
360
        // store the anchor resolution, if any.
361
        anchorResolutionKey = []byte("anchor-resolution")
362

363
        // breachResolutionKey is the key under the logScope that we'll use to
364
        // store the breach resolution, if any. This is used rather than the
365
        // resolutionsKey.
366
        breachResolutionKey = []byte("breach-resolution")
367

368
        // actionsBucketKey is the key under the logScope that we'll use to
369
        // store all chain actions once they're determined.
370
        actionsBucketKey = []byte("chain-actions")
371

372
        // commitSetKey is the primary key under the logScope that we'll use to
373
        // store the confirmed active HTLC sets once we learn that a channel
374
        // has closed out on chain.
375
        commitSetKey = []byte("commit-set")
376

377
        // taprootDataKey is the key we'll use to store taproot specific data
378
        // for the set of channels we'll need to sweep/claim.
379
        taprootDataKey = []byte("taproot-data")
380
)
381

382
var (
383
        // errScopeBucketNoExist is returned when we can't find the proper
384
        // bucket for an arbitrator's scope.
385
        errScopeBucketNoExist = fmt.Errorf("scope bucket not found")
386

387
        // errNoContracts is returned when no contracts are found within the
388
        // log.
389
        errNoContracts = fmt.Errorf("no stored contracts")
390

391
        // errNoResolutions is returned when the log doesn't contain any active
392
        // chain resolutions.
393
        errNoResolutions = fmt.Errorf("no contract resolutions exist")
394

395
        // errNoActions is returned when the log doesn't contain any stored
396
        // chain actions.
397
        errNoActions = fmt.Errorf("no chain actions exist")
398

399
        // errNoCommitSet is return when the log doesn't contained a CommitSet.
400
        // This can happen if the channel hasn't closed yet, or a client is
401
        // running an older version that didn't yet write this state.
402
        errNoCommitSet = fmt.Errorf("no commit set exists")
403
)
404

405
// boltArbitratorLog is an implementation of the ArbitratorLog interface backed
406
// by a bolt DB instance.
407
type boltArbitratorLog struct {
408
        db kvdb.Backend
409

410
        cfg ChannelArbitratorConfig
411

412
        scopeKey logScope
413
}
414

415
// newBoltArbitratorLog returns a new instance of the boltArbitratorLog given
416
// an arbitrator config, and the items needed to create its log scope.
417
func newBoltArbitratorLog(db kvdb.Backend, cfg ChannelArbitratorConfig,
418
        chainHash chainhash.Hash, chanPoint wire.OutPoint) (*boltArbitratorLog, error) {
3✔
419

3✔
420
        scope, err := newLogScope(chainHash, chanPoint)
3✔
421
        if err != nil {
3✔
422
                return nil, err
×
423
        }
×
424

425
        return &boltArbitratorLog{
3✔
426
                db:       db,
3✔
427
                cfg:      cfg,
3✔
428
                scopeKey: *scope,
3✔
429
        }, nil
3✔
430
}
431

432
// A compile time check to ensure boltArbitratorLog meets the ArbitratorLog
433
// interface.
434
var _ ArbitratorLog = (*boltArbitratorLog)(nil)
435

436
func fetchContractReadBucket(tx kvdb.RTx, scopeKey []byte) (kvdb.RBucket, error) {
3✔
437
        scopeBucket := tx.ReadBucket(scopeKey)
3✔
438
        if scopeBucket == nil {
3✔
UNCOV
439
                return nil, errScopeBucketNoExist
×
UNCOV
440
        }
×
441

442
        contractBucket := scopeBucket.NestedReadBucket(contractsBucketKey)
3✔
443
        if contractBucket == nil {
3✔
444
                return nil, errNoContracts
×
445
        }
×
446

447
        return contractBucket, nil
3✔
448
}
449

450
func fetchContractWriteBucket(tx kvdb.RwTx, scopeKey []byte) (kvdb.RwBucket, error) {
3✔
451
        scopeBucket, err := tx.CreateTopLevelBucket(scopeKey)
3✔
452
        if err != nil {
3✔
453
                return nil, err
×
454
        }
×
455

456
        contractBucket, err := scopeBucket.CreateBucketIfNotExists(
3✔
457
                contractsBucketKey,
3✔
458
        )
3✔
459
        if err != nil {
3✔
460
                return nil, err
×
461
        }
×
462

463
        return contractBucket, nil
3✔
464
}
465

466
// writeResolver is a helper method that writes a contract resolver and stores
467
// it it within the passed contractBucket using its unique resolutionsKey key.
468
func (b *boltArbitratorLog) writeResolver(contractBucket kvdb.RwBucket,
469
        res ContractResolver) error {
3✔
470

3✔
471
        // Only persist resolvers that are stateful. Stateless resolvers don't
3✔
472
        // expose a resolver key.
3✔
473
        resKey := res.ResolverKey()
3✔
474
        if resKey == nil {
6✔
475
                return nil
3✔
476
        }
3✔
477

478
        // First, we'll write to the buffer the type of this resolver. Using
479
        // this byte, we can later properly deserialize the resolver properly.
480
        var (
3✔
481
                buf   bytes.Buffer
3✔
482
                rType resolverType
3✔
483
        )
3✔
484
        switch res.(type) {
3✔
485
        case *htlcTimeoutResolver:
3✔
486
                rType = resolverTimeout
3✔
487
        case *htlcSuccessResolver:
3✔
488
                rType = resolverSuccess
3✔
489
        case *htlcOutgoingContestResolver:
3✔
490
                rType = resolverOutgoingContest
3✔
491
        case *htlcIncomingContestResolver:
3✔
492
                rType = resolverIncomingContest
3✔
493
        case *commitSweepResolver:
3✔
494
                rType = resolverUnilateralSweep
3✔
495
        case *breachResolver:
3✔
496
                rType = resolverBreach
3✔
497
        }
498
        if _, err := buf.Write([]byte{byte(rType)}); err != nil {
3✔
499
                return err
×
500
        }
×
501

502
        // With the type of the resolver written, we can then write out the raw
503
        // bytes of the resolver itself.
504
        if err := res.Encode(&buf); err != nil {
3✔
505
                return err
×
506
        }
×
507

508
        return contractBucket.Put(resKey, buf.Bytes())
3✔
509
}
510

511
// CurrentState returns the current state of the ChannelArbitrator. It takes an
512
// optional database transaction, which will be used if it is non-nil, otherwise
513
// the lookup will be done in its own transaction.
514
//
515
// NOTE: Part of the ContractResolver interface.
516
func (b *boltArbitratorLog) CurrentState(tx kvdb.RTx) (ArbitratorState, error) {
3✔
517
        var (
3✔
518
                s   ArbitratorState
3✔
519
                err error
3✔
520
        )
3✔
521

3✔
522
        if tx != nil {
6✔
523
                s, err = b.currentState(tx)
3✔
524
        } else {
6✔
525
                err = kvdb.View(b.db, func(tx kvdb.RTx) error {
6✔
526
                        s, err = b.currentState(tx)
3✔
527
                        return err
3✔
528
                }, func() {
6✔
529
                        s = 0
3✔
530
                })
3✔
531
        }
532

533
        if err != nil && err != errScopeBucketNoExist {
3✔
534
                return s, err
×
535
        }
×
536

537
        return s, nil
3✔
538
}
539

540
func (b *boltArbitratorLog) currentState(tx kvdb.RTx) (ArbitratorState, error) {
3✔
541
        scopeBucket := tx.ReadBucket(b.scopeKey[:])
3✔
542
        if scopeBucket == nil {
6✔
543
                return 0, errScopeBucketNoExist
3✔
544
        }
3✔
545

546
        stateBytes := scopeBucket.Get(stateKey)
3✔
547
        if stateBytes == nil {
3✔
548
                return 0, nil
×
549
        }
×
550

551
        return ArbitratorState(stateBytes[0]), nil
3✔
552
}
553

554
// CommitState persists, the current state of the chain attendant.
555
//
556
// NOTE: Part of the ContractResolver interface.
557
func (b *boltArbitratorLog) CommitState(s ArbitratorState) error {
3✔
558
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
559
                scopeBucket, err := tx.CreateTopLevelBucket(b.scopeKey[:])
3✔
560
                if err != nil {
3✔
561
                        return err
×
562
                }
×
563

564
                return scopeBucket.Put(stateKey[:], []byte{uint8(s)})
3✔
565
        })
566
}
567

568
// FetchUnresolvedContracts returns all unresolved contracts that have been
569
// previously written to the log.
570
//
571
// NOTE: Part of the ContractResolver interface.
572
func (b *boltArbitratorLog) FetchUnresolvedContracts() ([]ContractResolver, error) {
3✔
573
        resolverCfg := ResolverConfig{
3✔
574
                ChannelArbitratorConfig: b.cfg,
3✔
575
                Checkpoint:              b.checkpointContract,
3✔
576
        }
3✔
577
        var contracts []ContractResolver
3✔
578
        err := kvdb.View(b.db, func(tx kvdb.RTx) error {
6✔
579
                contractBucket, err := fetchContractReadBucket(tx, b.scopeKey[:])
3✔
580
                if err != nil {
3✔
UNCOV
581
                        return err
×
UNCOV
582
                }
×
583

584
                return contractBucket.ForEach(func(resKey, resBytes []byte) error {
6✔
585
                        if len(resKey) != resolverIDLen {
3✔
586
                                return nil
×
587
                        }
×
588

589
                        var res ContractResolver
3✔
590

3✔
591
                        // We'll snip off the first byte of the raw resolver
3✔
592
                        // bytes in order to extract what type of resolver
3✔
593
                        // we're about to encode.
3✔
594
                        resType := resolverType(resBytes[0])
3✔
595

3✔
596
                        // Then we'll create a reader using the remaining
3✔
597
                        // bytes.
3✔
598
                        resReader := bytes.NewReader(resBytes[1:])
3✔
599

3✔
600
                        switch resType {
3✔
601
                        case resolverTimeout:
3✔
602
                                res, err = newTimeoutResolverFromReader(
3✔
603
                                        resReader, resolverCfg,
3✔
604
                                )
3✔
605

606
                        case resolverSuccess:
3✔
607
                                res, err = newSuccessResolverFromReader(
3✔
608
                                        resReader, resolverCfg,
3✔
609
                                )
3✔
610

611
                        case resolverOutgoingContest:
3✔
612
                                res, err = newOutgoingContestResolverFromReader(
3✔
613
                                        resReader, resolverCfg,
3✔
614
                                )
3✔
615

616
                        case resolverIncomingContest:
3✔
617
                                res, err = newIncomingContestResolverFromReader(
3✔
618
                                        resReader, resolverCfg,
3✔
619
                                )
3✔
620

621
                        case resolverUnilateralSweep:
3✔
622
                                res, err = newCommitSweepResolverFromReader(
3✔
623
                                        resReader, resolverCfg,
3✔
624
                                )
3✔
625

626
                        case resolverBreach:
3✔
627
                                res, err = newBreachResolverFromReader(
3✔
628
                                        resReader, resolverCfg,
3✔
629
                                )
3✔
630

631
                        default:
×
632
                                return fmt.Errorf("unknown resolver type: %v", resType)
×
633
                        }
634

635
                        if err != nil {
3✔
636
                                return err
×
637
                        }
×
638

639
                        contracts = append(contracts, res)
3✔
640
                        return nil
3✔
641
                })
642
        }, func() {
3✔
643
                contracts = nil
3✔
644
        })
3✔
645
        if err != nil && err != errScopeBucketNoExist && err != errNoContracts {
3✔
646
                return nil, err
×
647
        }
×
648

649
        return contracts, nil
3✔
650
}
651

652
// InsertUnresolvedContracts inserts a set of unresolved contracts into the
653
// log. The log will then persistently store each contract until they've been
654
// swapped out, or resolved.
655
//
656
// NOTE: Part of the ContractResolver interface.
657
func (b *boltArbitratorLog) InsertUnresolvedContracts(reports []*channeldb.ResolverReport,
658
        resolvers ...ContractResolver) error {
3✔
659

3✔
660
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
661
                contractBucket, err := fetchContractWriteBucket(tx, b.scopeKey[:])
3✔
662
                if err != nil {
3✔
663
                        return err
×
664
                }
×
665

666
                for _, resolver := range resolvers {
6✔
667
                        err = b.writeResolver(contractBucket, resolver)
3✔
668
                        if err != nil {
3✔
669
                                return err
×
670
                        }
×
671
                }
672

673
                // Persist any reports that are present.
674
                for _, report := range reports {
6✔
675
                        err := b.cfg.PutResolverReport(tx, report)
3✔
676
                        if err != nil {
3✔
677
                                return err
×
678
                        }
×
679
                }
680

681
                return nil
3✔
682
        })
683
}
684

685
// SwapContract performs an atomic swap of the old contract for the new
686
// contract. This method is used when after a contract has been fully resolved,
687
// it produces another contract that needs to be resolved.
688
//
689
// NOTE: Part of the ContractResolver interface.
690
func (b *boltArbitratorLog) SwapContract(oldContract, newContract ContractResolver) error {
3✔
691
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
692
                contractBucket, err := fetchContractWriteBucket(tx, b.scopeKey[:])
3✔
693
                if err != nil {
3✔
694
                        return err
×
695
                }
×
696

697
                oldContractkey := oldContract.ResolverKey()
3✔
698
                if err := contractBucket.Delete(oldContractkey); err != nil {
3✔
699
                        return err
×
700
                }
×
701

702
                return b.writeResolver(contractBucket, newContract)
3✔
703
        })
704
}
705

706
// ResolveContract marks a contract as fully resolved. Once a contract has been
707
// fully resolved, it is deleted from persistent storage.
708
//
709
// NOTE: Part of the ContractResolver interface.
710
func (b *boltArbitratorLog) ResolveContract(res ContractResolver) error {
3✔
711
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
712
                contractBucket, err := fetchContractWriteBucket(tx, b.scopeKey[:])
3✔
713
                if err != nil {
3✔
714
                        return err
×
715
                }
×
716

717
                resKey := res.ResolverKey()
3✔
718
                return contractBucket.Delete(resKey)
3✔
719
        })
720
}
721

722
// LogContractResolutions stores a set of chain actions which are derived from
723
// our set of active contracts, and the on-chain state. We'll write this et of
724
// cations when: we decide to go on-chain to resolve a contract, or we detect
725
// that the remote party has gone on-chain.
726
//
727
// NOTE: Part of the ContractResolver interface.
728
func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error {
3✔
729
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
730
                scopeBucket, err := tx.CreateTopLevelBucket(b.scopeKey[:])
3✔
731
                if err != nil {
3✔
732
                        return err
×
733
                }
×
734

735
                var b bytes.Buffer
3✔
736

3✔
737
                if _, err := b.Write(c.CommitHash[:]); err != nil {
3✔
738
                        return err
×
739
                }
×
740

741
                // First, we'll write out the commit output's resolution.
742
                if c.CommitResolution == nil {
6✔
743
                        if err := binary.Write(&b, endian, false); err != nil {
3✔
744
                                return err
×
745
                        }
×
746
                } else {
3✔
747
                        if err := binary.Write(&b, endian, true); err != nil {
3✔
748
                                return err
×
749
                        }
×
750
                        err = encodeCommitResolution(&b, c.CommitResolution)
3✔
751
                        if err != nil {
3✔
752
                                return err
×
753
                        }
×
754
                }
755

756
                // As we write the HTLC resolutions, we'll serialize the sign
757
                // details for each, to store under a new key.
758
                var signDetailsBuf bytes.Buffer
3✔
759

3✔
760
                // With the output for the commitment transaction written, we
3✔
761
                // can now write out the resolutions for the incoming and
3✔
762
                // outgoing HTLC's.
3✔
763
                numIncoming := uint32(len(c.HtlcResolutions.IncomingHTLCs))
3✔
764
                if err := binary.Write(&b, endian, numIncoming); err != nil {
3✔
765
                        return err
×
766
                }
×
767
                for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
6✔
768
                        err := encodeIncomingResolution(&b, &htlc)
3✔
769
                        if err != nil {
3✔
770
                                return err
×
771
                        }
×
772

773
                        err = encodeSignDetails(&signDetailsBuf, htlc.SignDetails)
3✔
774
                        if err != nil {
3✔
775
                                return err
×
776
                        }
×
777
                }
778
                numOutgoing := uint32(len(c.HtlcResolutions.OutgoingHTLCs))
3✔
779
                if err := binary.Write(&b, endian, numOutgoing); err != nil {
3✔
780
                        return err
×
781
                }
×
782
                for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
6✔
783
                        err := encodeOutgoingResolution(&b, &htlc)
3✔
784
                        if err != nil {
3✔
785
                                return err
×
786
                        }
×
787

788
                        err = encodeSignDetails(&signDetailsBuf, htlc.SignDetails)
3✔
789
                        if err != nil {
3✔
790
                                return err
×
791
                        }
×
792
                }
793

794
                // Put the resolutions under the resolutionsKey.
795
                err = scopeBucket.Put(resolutionsKey, b.Bytes())
3✔
796
                if err != nil {
3✔
797
                        return err
×
798
                }
×
799

800
                // We'll put the serialized sign details under its own key to
801
                // stay backwards compatible.
802
                err = scopeBucket.Put(
3✔
803
                        resolutionsSignDetailsKey, signDetailsBuf.Bytes(),
3✔
804
                )
3✔
805
                if err != nil {
3✔
806
                        return err
×
807
                }
×
808

809
                // Write out the anchor resolution if present.
810
                if c.AnchorResolution != nil {
6✔
811
                        var b bytes.Buffer
3✔
812
                        err := encodeAnchorResolution(&b, c.AnchorResolution)
3✔
813
                        if err != nil {
3✔
814
                                return err
×
815
                        }
×
816

817
                        err = scopeBucket.Put(anchorResolutionKey, b.Bytes())
3✔
818
                        if err != nil {
3✔
819
                                return err
×
820
                        }
×
821
                }
822

823
                // Write out the breach resolution if present.
824
                if c.BreachResolution != nil {
6✔
825
                        var b bytes.Buffer
3✔
826
                        err := encodeBreachResolution(&b, c.BreachResolution)
3✔
827
                        if err != nil {
3✔
828
                                return err
×
829
                        }
×
830

831
                        err = scopeBucket.Put(breachResolutionKey, b.Bytes())
3✔
832
                        if err != nil {
3✔
833
                                return err
×
834
                        }
×
835
                }
836

837
                // If this isn't a taproot channel, then we can exit early here
838
                // as there's no extra data to write.
839
                switch {
3✔
840
                case c.AnchorResolution == nil:
3✔
841
                        return nil
3✔
842
                case !txscript.IsPayToTaproot(
843
                        c.AnchorResolution.AnchorSignDescriptor.Output.PkScript,
844
                ):
3✔
845
                        return nil
3✔
846
                }
847

848
                // With everything else encoded, we'll now populate the taproot
849
                // specific items we need to store for the musig2 channels.
850
                var tb bytes.Buffer
3✔
851
                err = encodeTaprootAuxData(&tb, c)
3✔
852
                if err != nil {
3✔
853
                        return err
×
854
                }
×
855

856
                return scopeBucket.Put(taprootDataKey, tb.Bytes())
3✔
857
        })
858
}
859

860
// FetchContractResolutions fetches the set of previously stored contract
861
// resolutions from persistent storage.
862
//
863
// NOTE: Part of the ContractResolver interface.
864
func (b *boltArbitratorLog) FetchContractResolutions() (*ContractResolutions, error) {
3✔
865
        var c *ContractResolutions
3✔
866
        err := kvdb.View(b.db, func(tx kvdb.RTx) error {
6✔
867
                scopeBucket := tx.ReadBucket(b.scopeKey[:])
3✔
868
                if scopeBucket == nil {
3✔
UNCOV
869
                        return errScopeBucketNoExist
×
UNCOV
870
                }
×
871

872
                resolutionBytes := scopeBucket.Get(resolutionsKey)
3✔
873
                if resolutionBytes == nil {
3✔
874
                        return errNoResolutions
×
875
                }
×
876

877
                resReader := bytes.NewReader(resolutionBytes)
3✔
878

3✔
879
                _, err := io.ReadFull(resReader, c.CommitHash[:])
3✔
880
                if err != nil {
3✔
881
                        return err
×
882
                }
×
883

884
                // First, we'll attempt to read out the commit resolution (if
885
                // it exists).
886
                var haveCommitRes bool
3✔
887
                err = binary.Read(resReader, endian, &haveCommitRes)
3✔
888
                if err != nil {
3✔
889
                        return err
×
890
                }
×
891
                if haveCommitRes {
6✔
892
                        c.CommitResolution = &lnwallet.CommitOutputResolution{}
3✔
893
                        err = decodeCommitResolution(
3✔
894
                                resReader, c.CommitResolution,
3✔
895
                        )
3✔
896
                        if err != nil {
3✔
897
                                return fmt.Errorf("unable to decode "+
×
898
                                        "commit res: %w", err)
×
899
                        }
×
900
                }
901

902
                var (
3✔
903
                        numIncoming uint32
3✔
904
                        numOutgoing uint32
3✔
905
                )
3✔
906

3✔
907
                // Next, we'll read out the incoming and outgoing HTLC
3✔
908
                // resolutions.
3✔
909
                err = binary.Read(resReader, endian, &numIncoming)
3✔
910
                if err != nil {
3✔
911
                        return err
×
912
                }
×
913
                c.HtlcResolutions.IncomingHTLCs = make([]lnwallet.IncomingHtlcResolution, numIncoming)
3✔
914
                for i := uint32(0); i < numIncoming; i++ {
6✔
915
                        err := decodeIncomingResolution(
3✔
916
                                resReader, &c.HtlcResolutions.IncomingHTLCs[i],
3✔
917
                        )
3✔
918
                        if err != nil {
3✔
919
                                return fmt.Errorf("unable to decode "+
×
920
                                        "incoming res: %w", err)
×
921
                        }
×
922
                }
923

924
                err = binary.Read(resReader, endian, &numOutgoing)
3✔
925
                if err != nil {
3✔
926
                        return err
×
927
                }
×
928
                c.HtlcResolutions.OutgoingHTLCs = make([]lnwallet.OutgoingHtlcResolution, numOutgoing)
3✔
929
                for i := uint32(0); i < numOutgoing; i++ {
6✔
930
                        err := decodeOutgoingResolution(
3✔
931
                                resReader, &c.HtlcResolutions.OutgoingHTLCs[i],
3✔
932
                        )
3✔
933
                        if err != nil {
3✔
934
                                return fmt.Errorf("unable to decode "+
×
935
                                        "outgoing res: %w", err)
×
936
                        }
×
937
                }
938

939
                // Now we attempt to get the sign details for our HTLC
940
                // resolutions. If not present the channel is of a type that
941
                // doesn't need them. If present there will be SignDetails
942
                // encoded for each HTLC resolution.
943
                signDetailsBytes := scopeBucket.Get(resolutionsSignDetailsKey)
3✔
944
                if signDetailsBytes != nil {
6✔
945
                        r := bytes.NewReader(signDetailsBytes)
3✔
946

3✔
947
                        // They will be encoded in the same order as the
3✔
948
                        // resolutions: firs incoming HTLCs, then outgoing.
3✔
949
                        for i := uint32(0); i < numIncoming; i++ {
6✔
950
                                htlc := &c.HtlcResolutions.IncomingHTLCs[i]
3✔
951
                                htlc.SignDetails, err = decodeSignDetails(r)
3✔
952
                                if err != nil {
3✔
953
                                        return fmt.Errorf("unable to decode "+
×
954
                                                "incoming sign desc: %w", err)
×
955
                                }
×
956
                        }
957

958
                        for i := uint32(0); i < numOutgoing; i++ {
6✔
959
                                htlc := &c.HtlcResolutions.OutgoingHTLCs[i]
3✔
960
                                htlc.SignDetails, err = decodeSignDetails(r)
3✔
961
                                if err != nil {
3✔
962
                                        return fmt.Errorf("unable to decode "+
×
963
                                                "outgoing sign desc: %w", err)
×
964
                                }
×
965
                        }
966
                }
967

968
                anchorResBytes := scopeBucket.Get(anchorResolutionKey)
3✔
969
                if anchorResBytes != nil {
6✔
970
                        c.AnchorResolution = &lnwallet.AnchorResolution{}
3✔
971
                        resReader := bytes.NewReader(anchorResBytes)
3✔
972
                        err := decodeAnchorResolution(
3✔
973
                                resReader, c.AnchorResolution,
3✔
974
                        )
3✔
975
                        if err != nil {
3✔
976
                                return fmt.Errorf("unable to read anchor "+
×
977
                                        "data: %w", err)
×
978
                        }
×
979
                }
980

981
                breachResBytes := scopeBucket.Get(breachResolutionKey)
3✔
982
                if breachResBytes != nil {
6✔
983
                        c.BreachResolution = &BreachResolution{}
3✔
984
                        resReader := bytes.NewReader(breachResBytes)
3✔
985
                        err := decodeBreachResolution(
3✔
986
                                resReader, c.BreachResolution,
3✔
987
                        )
3✔
988
                        if err != nil {
3✔
989
                                return fmt.Errorf("unable to read breach "+
×
990
                                        "data: %w", err)
×
991
                        }
×
992
                }
993

994
                tapCaseBytes := scopeBucket.Get(taprootDataKey)
3✔
995
                if tapCaseBytes != nil {
6✔
996
                        err = decodeTapRootAuxData(
3✔
997
                                bytes.NewReader(tapCaseBytes), c,
3✔
998
                        )
3✔
999
                        if err != nil {
3✔
1000
                                return fmt.Errorf("unable to read taproot "+
×
1001
                                        "data: %w", err)
×
1002
                        }
×
1003
                }
1004
                return nil
3✔
1005
        }, func() {
3✔
1006
                c = &ContractResolutions{}
3✔
1007
        })
3✔
1008
        if err != nil {
3✔
UNCOV
1009
                return nil, err
×
UNCOV
1010
        }
×
1011

1012
        return c, err
3✔
1013
}
1014

1015
// FetchChainActions attempts to fetch the set of previously stored chain
1016
// actions. We'll use this upon restart to properly advance our state machine
1017
// forward.
1018
//
1019
// NOTE: Part of the ContractResolver interface.
1020
func (b *boltArbitratorLog) FetchChainActions() (ChainActionMap, error) {
×
1021
        var actionsMap ChainActionMap
×
1022

×
1023
        err := kvdb.View(b.db, func(tx kvdb.RTx) error {
×
1024
                scopeBucket := tx.ReadBucket(b.scopeKey[:])
×
1025
                if scopeBucket == nil {
×
1026
                        return errScopeBucketNoExist
×
1027
                }
×
1028

1029
                actionsBucket := scopeBucket.NestedReadBucket(actionsBucketKey)
×
1030
                if actionsBucket == nil {
×
1031
                        return errNoActions
×
1032
                }
×
1033

1034
                return actionsBucket.ForEach(func(action, htlcBytes []byte) error {
×
1035
                        if htlcBytes == nil {
×
1036
                                return nil
×
1037
                        }
×
1038

1039
                        chainAction := ChainAction(action[0])
×
1040

×
1041
                        htlcReader := bytes.NewReader(htlcBytes)
×
1042
                        htlcs, err := channeldb.DeserializeHtlcs(htlcReader)
×
1043
                        if err != nil {
×
1044
                                return err
×
1045
                        }
×
1046

1047
                        actionsMap[chainAction] = htlcs
×
1048

×
1049
                        return nil
×
1050
                })
1051
        }, func() {
×
1052
                actionsMap = make(ChainActionMap)
×
1053
        })
×
1054
        if err != nil {
×
1055
                return nil, err
×
1056
        }
×
1057

1058
        return actionsMap, nil
×
1059
}
1060

1061
// InsertConfirmedCommitSet stores the known set of active HTLCs at the time
1062
// channel closure. We'll use this to reconstruct our set of chain actions anew
1063
// based on the confirmed and pending commitment state.
1064
//
1065
// NOTE: Part of the ContractResolver interface.
1066
func (b *boltArbitratorLog) InsertConfirmedCommitSet(c *CommitSet) error {
3✔
1067
        return kvdb.Batch(b.db, func(tx kvdb.RwTx) error {
6✔
1068
                scopeBucket, err := tx.CreateTopLevelBucket(b.scopeKey[:])
3✔
1069
                if err != nil {
3✔
1070
                        return err
×
1071
                }
×
1072

1073
                var b bytes.Buffer
3✔
1074
                if err := encodeCommitSet(&b, c); err != nil {
3✔
1075
                        return err
×
1076
                }
×
1077

1078
                return scopeBucket.Put(commitSetKey, b.Bytes())
3✔
1079
        })
1080
}
1081

1082
// FetchConfirmedCommitSet fetches the known confirmed active HTLC set from the
1083
// database. It takes an optional database transaction, which will be used if it
1084
// is non-nil, otherwise the lookup will be done in its own transaction.
1085
//
1086
// NOTE: Part of the ContractResolver interface.
1087
func (b *boltArbitratorLog) FetchConfirmedCommitSet(tx kvdb.RTx) (*CommitSet, error) {
3✔
1088
        if tx != nil {
6✔
1089
                return b.fetchConfirmedCommitSet(tx)
3✔
1090
        }
3✔
1091

1092
        var c *CommitSet
3✔
1093
        err := kvdb.View(b.db, func(tx kvdb.RTx) error {
6✔
1094
                var err error
3✔
1095
                c, err = b.fetchConfirmedCommitSet(tx)
3✔
1096
                return err
3✔
1097
        }, func() {
6✔
1098
                c = nil
3✔
1099
        })
3✔
1100
        if err != nil {
6✔
1101
                return nil, err
3✔
1102
        }
3✔
1103

UNCOV
1104
        return c, nil
×
1105
}
1106

1107
func (b *boltArbitratorLog) fetchConfirmedCommitSet(tx kvdb.RTx) (*CommitSet,
1108
        error) {
3✔
1109

3✔
1110
        scopeBucket := tx.ReadBucket(b.scopeKey[:])
3✔
1111
        if scopeBucket == nil {
6✔
1112
                return nil, errScopeBucketNoExist
3✔
1113
        }
3✔
1114

1115
        commitSetBytes := scopeBucket.Get(commitSetKey)
3✔
1116
        if commitSetBytes == nil {
6✔
1117
                return nil, errNoCommitSet
3✔
1118
        }
3✔
1119

1120
        return decodeCommitSet(bytes.NewReader(commitSetBytes))
3✔
1121
}
1122

1123
// WipeHistory is to be called ONLY once *all* contracts have been fully
1124
// resolved, and the channel closure if finalized. This method will delete all
1125
// on-disk state within the persistent log.
1126
//
1127
// NOTE: Part of the ContractResolver interface.
1128
func (b *boltArbitratorLog) WipeHistory() error {
3✔
1129
        return kvdb.Update(b.db, func(tx kvdb.RwTx) error {
6✔
1130
                scopeBucket, err := tx.CreateTopLevelBucket(b.scopeKey[:])
3✔
1131
                if err != nil {
3✔
1132
                        return err
×
1133
                }
×
1134

1135
                // Once we have the main top-level bucket, we'll delete the key
1136
                // that stores the state of the arbitrator.
1137
                if err := scopeBucket.Delete(stateKey[:]); err != nil {
3✔
1138
                        return err
×
1139
                }
×
1140

1141
                // Next, we'll delete any lingering contract state within the
1142
                // contracts bucket by removing the bucket itself.
1143
                err = scopeBucket.DeleteNestedBucket(contractsBucketKey)
3✔
1144
                if err != nil && err != kvdb.ErrBucketNotFound {
3✔
1145
                        return err
×
1146
                }
×
1147

1148
                // Next, we'll delete storage of any lingering contract
1149
                // resolutions.
1150
                if err := scopeBucket.Delete(resolutionsKey); err != nil {
3✔
1151
                        return err
×
1152
                }
×
1153

1154
                err = scopeBucket.Delete(resolutionsSignDetailsKey)
3✔
1155
                if err != nil {
3✔
1156
                        return err
×
1157
                }
×
1158

1159
                // We'll delete any chain actions that are still stored by
1160
                // removing the enclosing bucket.
1161
                err = scopeBucket.DeleteNestedBucket(actionsBucketKey)
3✔
1162
                if err != nil && err != kvdb.ErrBucketNotFound {
3✔
1163
                        return err
×
1164
                }
×
1165

1166
                // Finally, we'll delete the enclosing bucket itself.
1167
                return tx.DeleteTopLevelBucket(b.scopeKey[:])
3✔
1168
        }, func() {})
3✔
1169
}
1170

1171
// checkpointContract is a private method that will be fed into
1172
// ContractResolver instances to checkpoint their state once they reach
1173
// milestones during contract resolution. If the report provided is non-nil,
1174
// it should also be recorded.
1175
func (b *boltArbitratorLog) checkpointContract(c ContractResolver,
1176
        reports ...*channeldb.ResolverReport) error {
3✔
1177

3✔
1178
        return kvdb.Update(b.db, func(tx kvdb.RwTx) error {
6✔
1179
                contractBucket, err := fetchContractWriteBucket(tx, b.scopeKey[:])
3✔
1180
                if err != nil {
3✔
1181
                        return err
×
1182
                }
×
1183

1184
                if err := b.writeResolver(contractBucket, c); err != nil {
3✔
1185
                        return err
×
1186
                }
×
1187

1188
                for _, report := range reports {
6✔
1189
                        if err := b.cfg.PutResolverReport(tx, report); err != nil {
3✔
1190
                                return err
×
1191
                        }
×
1192
                }
1193

1194
                return nil
3✔
1195
        }, func() {})
3✔
1196
}
1197

1198
// encodeSignDetails encodes the given SignDetails struct to the writer.
1199
// SignDetails is allowed to be nil, in which we will encode that it is not
1200
// present.
1201
func encodeSignDetails(w io.Writer, s *input.SignDetails) error {
3✔
1202
        // If we don't have sign details, write false and return.
3✔
1203
        if s == nil {
6✔
1204
                return binary.Write(w, endian, false)
3✔
1205
        }
3✔
1206

1207
        // Otherwise write true, and the contents of the SignDetails.
1208
        if err := binary.Write(w, endian, true); err != nil {
3✔
1209
                return err
×
1210
        }
×
1211

1212
        err := input.WriteSignDescriptor(w, &s.SignDesc)
3✔
1213
        if err != nil {
3✔
1214
                return err
×
1215
        }
×
1216
        err = binary.Write(w, endian, uint32(s.SigHashType))
3✔
1217
        if err != nil {
3✔
1218
                return err
×
1219
        }
×
1220

1221
        // Write the DER-encoded signature.
1222
        b := s.PeerSig.Serialize()
3✔
1223
        if err := wire.WriteVarBytes(w, 0, b); err != nil {
3✔
1224
                return err
×
1225
        }
×
1226

1227
        return nil
3✔
1228
}
1229

1230
// decodeSignDetails extracts a single SignDetails from the reader. It is
1231
// allowed to return nil in case the SignDetails were empty.
1232
func decodeSignDetails(r io.Reader) (*input.SignDetails, error) {
3✔
1233
        var present bool
3✔
1234
        if err := binary.Read(r, endian, &present); err != nil {
3✔
1235
                return nil, err
×
1236
        }
×
1237

1238
        // Simply return nil if the next SignDetails was not present.
1239
        if !present {
6✔
1240
                return nil, nil
3✔
1241
        }
3✔
1242

1243
        // Otherwise decode the elements of the SignDetails.
1244
        s := input.SignDetails{}
3✔
1245
        err := input.ReadSignDescriptor(r, &s.SignDesc)
3✔
1246
        if err != nil {
3✔
1247
                return nil, err
×
1248
        }
×
1249

1250
        var sigHash uint32
3✔
1251
        err = binary.Read(r, endian, &sigHash)
3✔
1252
        if err != nil {
3✔
1253
                return nil, err
×
1254
        }
×
1255
        s.SigHashType = txscript.SigHashType(sigHash)
3✔
1256

3✔
1257
        // Read DER-encoded signature.
3✔
1258
        rawSig, err := wire.ReadVarBytes(r, 0, 200, "signature")
3✔
1259
        if err != nil {
3✔
1260
                return nil, err
×
1261
        }
×
1262

1263
        s.PeerSig, err = input.ParseSignature(rawSig)
3✔
1264
        if err != nil {
3✔
1265
                return nil, err
×
1266
        }
×
1267

1268
        return &s, nil
3✔
1269
}
1270

1271
func encodeIncomingResolution(w io.Writer, i *lnwallet.IncomingHtlcResolution) error {
3✔
1272
        if _, err := w.Write(i.Preimage[:]); err != nil {
3✔
1273
                return err
×
1274
        }
×
1275

1276
        if i.SignedSuccessTx == nil {
6✔
1277
                if err := binary.Write(w, endian, false); err != nil {
3✔
1278
                        return err
×
1279
                }
×
1280
        } else {
3✔
1281
                if err := binary.Write(w, endian, true); err != nil {
3✔
1282
                        return err
×
1283
                }
×
1284

1285
                if err := i.SignedSuccessTx.Serialize(w); err != nil {
3✔
1286
                        return err
×
1287
                }
×
1288
        }
1289

1290
        if err := binary.Write(w, endian, i.CsvDelay); err != nil {
3✔
1291
                return err
×
1292
        }
×
1293
        if _, err := w.Write(i.ClaimOutpoint.Hash[:]); err != nil {
3✔
1294
                return err
×
1295
        }
×
1296
        if err := binary.Write(w, endian, i.ClaimOutpoint.Index); err != nil {
3✔
1297
                return err
×
1298
        }
×
1299
        err := input.WriteSignDescriptor(w, &i.SweepSignDesc)
3✔
1300
        if err != nil {
3✔
1301
                return err
×
1302
        }
×
1303

1304
        return nil
3✔
1305
}
1306

1307
func decodeIncomingResolution(r io.Reader, h *lnwallet.IncomingHtlcResolution) error {
3✔
1308
        if _, err := io.ReadFull(r, h.Preimage[:]); err != nil {
3✔
1309
                return err
×
1310
        }
×
1311

1312
        var txPresent bool
3✔
1313
        if err := binary.Read(r, endian, &txPresent); err != nil {
3✔
1314
                return err
×
1315
        }
×
1316
        if txPresent {
6✔
1317
                h.SignedSuccessTx = &wire.MsgTx{}
3✔
1318
                if err := h.SignedSuccessTx.Deserialize(r); err != nil {
3✔
1319
                        return err
×
1320
                }
×
1321
        }
1322

1323
        err := binary.Read(r, endian, &h.CsvDelay)
3✔
1324
        if err != nil {
3✔
1325
                return err
×
1326
        }
×
1327
        _, err = io.ReadFull(r, h.ClaimOutpoint.Hash[:])
3✔
1328
        if err != nil {
3✔
1329
                return err
×
1330
        }
×
1331
        err = binary.Read(r, endian, &h.ClaimOutpoint.Index)
3✔
1332
        if err != nil {
3✔
1333
                return err
×
1334
        }
×
1335

1336
        return input.ReadSignDescriptor(r, &h.SweepSignDesc)
3✔
1337
}
1338

1339
func encodeOutgoingResolution(w io.Writer, o *lnwallet.OutgoingHtlcResolution) error {
3✔
1340
        if err := binary.Write(w, endian, o.Expiry); err != nil {
3✔
1341
                return err
×
1342
        }
×
1343

1344
        if o.SignedTimeoutTx == nil {
6✔
1345
                if err := binary.Write(w, endian, false); err != nil {
3✔
1346
                        return err
×
1347
                }
×
1348
        } else {
3✔
1349
                if err := binary.Write(w, endian, true); err != nil {
3✔
1350
                        return err
×
1351
                }
×
1352

1353
                if err := o.SignedTimeoutTx.Serialize(w); err != nil {
3✔
1354
                        return err
×
1355
                }
×
1356
        }
1357

1358
        if err := binary.Write(w, endian, o.CsvDelay); err != nil {
3✔
1359
                return err
×
1360
        }
×
1361
        if _, err := w.Write(o.ClaimOutpoint.Hash[:]); err != nil {
3✔
1362
                return err
×
1363
        }
×
1364
        if err := binary.Write(w, endian, o.ClaimOutpoint.Index); err != nil {
3✔
1365
                return err
×
1366
        }
×
1367

1368
        return input.WriteSignDescriptor(w, &o.SweepSignDesc)
3✔
1369
}
1370

1371
func decodeOutgoingResolution(r io.Reader, o *lnwallet.OutgoingHtlcResolution) error {
3✔
1372
        err := binary.Read(r, endian, &o.Expiry)
3✔
1373
        if err != nil {
3✔
1374
                return err
×
1375
        }
×
1376

1377
        var txPresent bool
3✔
1378
        if err := binary.Read(r, endian, &txPresent); err != nil {
3✔
1379
                return err
×
1380
        }
×
1381
        if txPresent {
6✔
1382
                o.SignedTimeoutTx = &wire.MsgTx{}
3✔
1383
                if err := o.SignedTimeoutTx.Deserialize(r); err != nil {
3✔
1384
                        return err
×
1385
                }
×
1386
        }
1387

1388
        err = binary.Read(r, endian, &o.CsvDelay)
3✔
1389
        if err != nil {
3✔
1390
                return err
×
1391
        }
×
1392
        _, err = io.ReadFull(r, o.ClaimOutpoint.Hash[:])
3✔
1393
        if err != nil {
3✔
1394
                return err
×
1395
        }
×
1396
        err = binary.Read(r, endian, &o.ClaimOutpoint.Index)
3✔
1397
        if err != nil {
3✔
1398
                return err
×
1399
        }
×
1400

1401
        return input.ReadSignDescriptor(r, &o.SweepSignDesc)
3✔
1402
}
1403

1404
func encodeCommitResolution(w io.Writer,
1405
        c *lnwallet.CommitOutputResolution) error {
3✔
1406

3✔
1407
        if _, err := w.Write(c.SelfOutPoint.Hash[:]); err != nil {
3✔
1408
                return err
×
1409
        }
×
1410
        err := binary.Write(w, endian, c.SelfOutPoint.Index)
3✔
1411
        if err != nil {
3✔
1412
                return err
×
1413
        }
×
1414

1415
        err = input.WriteSignDescriptor(w, &c.SelfOutputSignDesc)
3✔
1416
        if err != nil {
3✔
1417
                return err
×
1418
        }
×
1419

1420
        return binary.Write(w, endian, c.MaturityDelay)
3✔
1421
}
1422

1423
func decodeCommitResolution(r io.Reader,
1424
        c *lnwallet.CommitOutputResolution) error {
3✔
1425

3✔
1426
        _, err := io.ReadFull(r, c.SelfOutPoint.Hash[:])
3✔
1427
        if err != nil {
3✔
1428
                return err
×
1429
        }
×
1430
        err = binary.Read(r, endian, &c.SelfOutPoint.Index)
3✔
1431
        if err != nil {
3✔
1432
                return err
×
1433
        }
×
1434

1435
        err = input.ReadSignDescriptor(r, &c.SelfOutputSignDesc)
3✔
1436
        if err != nil {
3✔
1437
                return err
×
1438
        }
×
1439

1440
        return binary.Read(r, endian, &c.MaturityDelay)
3✔
1441
}
1442

1443
func encodeAnchorResolution(w io.Writer,
1444
        a *lnwallet.AnchorResolution) error {
3✔
1445

3✔
1446
        if _, err := w.Write(a.CommitAnchor.Hash[:]); err != nil {
3✔
1447
                return err
×
1448
        }
×
1449
        err := binary.Write(w, endian, a.CommitAnchor.Index)
3✔
1450
        if err != nil {
3✔
1451
                return err
×
1452
        }
×
1453

1454
        return input.WriteSignDescriptor(w, &a.AnchorSignDescriptor)
3✔
1455
}
1456

1457
func decodeAnchorResolution(r io.Reader,
1458
        a *lnwallet.AnchorResolution) error {
3✔
1459

3✔
1460
        _, err := io.ReadFull(r, a.CommitAnchor.Hash[:])
3✔
1461
        if err != nil {
3✔
1462
                return err
×
1463
        }
×
1464
        err = binary.Read(r, endian, &a.CommitAnchor.Index)
3✔
1465
        if err != nil {
3✔
1466
                return err
×
1467
        }
×
1468

1469
        return input.ReadSignDescriptor(r, &a.AnchorSignDescriptor)
3✔
1470
}
1471

1472
func encodeBreachResolution(w io.Writer, b *BreachResolution) error {
3✔
1473
        if _, err := w.Write(b.FundingOutPoint.Hash[:]); err != nil {
3✔
1474
                return err
×
1475
        }
×
1476
        return binary.Write(w, endian, b.FundingOutPoint.Index)
3✔
1477
}
1478

1479
func decodeBreachResolution(r io.Reader, b *BreachResolution) error {
3✔
1480
        _, err := io.ReadFull(r, b.FundingOutPoint.Hash[:])
3✔
1481
        if err != nil {
3✔
1482
                return err
×
1483
        }
×
1484
        return binary.Read(r, endian, &b.FundingOutPoint.Index)
3✔
1485
}
1486

1487
func encodeHtlcSetKey(w io.Writer, htlcSetKey HtlcSetKey) error {
3✔
1488
        err := binary.Write(w, endian, htlcSetKey.IsRemote)
3✔
1489
        if err != nil {
3✔
1490
                return err
×
1491
        }
×
1492

1493
        return binary.Write(w, endian, htlcSetKey.IsPending)
3✔
1494
}
1495

1496
func encodeCommitSet(w io.Writer, c *CommitSet) error {
3✔
1497
        confCommitKey, err := c.ConfCommitKey.UnwrapOrErr(
3✔
1498
                fmt.Errorf("HtlcSetKey is not set"),
3✔
1499
        )
3✔
1500
        if err != nil {
3✔
1501
                return err
×
1502
        }
×
1503
        if err := encodeHtlcSetKey(w, confCommitKey); err != nil {
3✔
1504
                return err
×
1505
        }
×
1506

1507
        numSets := uint8(len(c.HtlcSets))
3✔
1508
        if err := binary.Write(w, endian, numSets); err != nil {
3✔
1509
                return err
×
1510
        }
×
1511

1512
        for htlcSetKey, htlcs := range c.HtlcSets {
6✔
1513
                if err := encodeHtlcSetKey(w, htlcSetKey); err != nil {
3✔
1514
                        return err
×
1515
                }
×
1516

1517
                if err := channeldb.SerializeHtlcs(w, htlcs...); err != nil {
3✔
1518
                        return err
×
1519
                }
×
1520
        }
1521

1522
        return nil
3✔
1523
}
1524

1525
func decodeHtlcSetKey(r io.Reader, h *HtlcSetKey) error {
3✔
1526
        err := binary.Read(r, endian, &h.IsRemote)
3✔
1527
        if err != nil {
3✔
1528
                return err
×
1529
        }
×
1530

1531
        return binary.Read(r, endian, &h.IsPending)
3✔
1532
}
1533

1534
func decodeCommitSet(r io.Reader) (*CommitSet, error) {
3✔
1535
        confCommitKey := HtlcSetKey{}
3✔
1536
        if err := decodeHtlcSetKey(r, &confCommitKey); err != nil {
3✔
1537
                return nil, err
×
1538
        }
×
1539

1540
        c := &CommitSet{
3✔
1541
                ConfCommitKey: fn.Some(confCommitKey),
3✔
1542
                HtlcSets:      make(map[HtlcSetKey][]channeldb.HTLC),
3✔
1543
        }
3✔
1544

3✔
1545
        var numSets uint8
3✔
1546
        if err := binary.Read(r, endian, &numSets); err != nil {
3✔
1547
                return nil, err
×
1548
        }
×
1549

1550
        for i := uint8(0); i < numSets; i++ {
6✔
1551
                var htlcSetKey HtlcSetKey
3✔
1552
                if err := decodeHtlcSetKey(r, &htlcSetKey); err != nil {
3✔
1553
                        return nil, err
×
1554
                }
×
1555

1556
                htlcs, err := channeldb.DeserializeHtlcs(r)
3✔
1557
                if err != nil {
3✔
1558
                        return nil, err
×
1559
                }
×
1560

1561
                c.HtlcSets[htlcSetKey] = htlcs
3✔
1562
        }
1563

1564
        return c, nil
3✔
1565
}
1566

1567
func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
3✔
1568
        tapCase := newTaprootBriefcase()
3✔
1569

3✔
1570
        if c.CommitResolution != nil {
6✔
1571
                commitResolution := c.CommitResolution
3✔
1572
                commitSignDesc := commitResolution.SelfOutputSignDesc
3✔
1573
                //nolint:ll
3✔
1574
                tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = commitSignDesc.ControlBlock
3✔
1575

3✔
1576
                c.CommitResolution.ResolutionBlob.WhenSome(func(b []byte) {
6✔
1577
                        tapCase.SettledCommitBlob = tlv.SomeRecordT(
3✔
1578
                                tlv.NewPrimitiveRecord[tlv.TlvType2](b),
3✔
1579
                        )
3✔
1580
                })
3✔
1581
        }
1582

1583
        htlcBlobs := newAuxHtlcBlobs()
3✔
1584
        for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
6✔
1585
                htlc := htlc
3✔
1586

3✔
1587
                htlcSignDesc := htlc.SweepSignDesc
3✔
1588
                ctrlBlock := htlcSignDesc.ControlBlock
3✔
1589

3✔
1590
                if ctrlBlock == nil {
3✔
1591
                        continue
×
1592
                }
1593

1594
                var resID resolverID
3✔
1595
                if htlc.SignedSuccessTx != nil {
6✔
1596
                        resID = newResolverID(
3✔
1597
                                htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
3✔
1598
                        )
3✔
1599
                        //nolint:ll
3✔
1600
                        tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock
3✔
1601

3✔
1602
                        // For HTLCs we need to go to the second level for, we
3✔
1603
                        // also need to store the control block needed to
3✔
1604
                        // publish the second level transaction.
3✔
1605
                        if htlc.SignDetails != nil {
6✔
1606
                                //nolint:ll
3✔
1607
                                bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock
3✔
1608
                                //nolint:ll
3✔
1609
                                tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
3✔
1610
                        }
3✔
1611
                } else {
3✔
1612
                        resID = newResolverID(htlc.ClaimOutpoint)
3✔
1613
                        //nolint:ll
3✔
1614
                        tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock
3✔
1615
                }
3✔
1616

1617
                htlc.ResolutionBlob.WhenSome(func(b []byte) {
6✔
1618
                        htlcBlobs[resID] = b
3✔
1619
                })
3✔
1620
        }
1621
        for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
6✔
1622
                htlc := htlc
3✔
1623

3✔
1624
                htlcSignDesc := htlc.SweepSignDesc
3✔
1625
                ctrlBlock := htlcSignDesc.ControlBlock
3✔
1626

3✔
1627
                if ctrlBlock == nil {
3✔
1628
                        continue
×
1629
                }
1630

1631
                var resID resolverID
3✔
1632
                if htlc.SignedTimeoutTx != nil {
6✔
1633
                        resID = newResolverID(
3✔
1634
                                htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
3✔
1635
                        )
3✔
1636
                        //nolint:ll
3✔
1637
                        tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock
3✔
1638

3✔
1639
                        // For HTLCs we need to go to the second level for, we
3✔
1640
                        // also need to store the control block needed to
3✔
1641
                        // publish the second level transaction.
3✔
1642
                        //
3✔
1643
                        //nolint:ll
3✔
1644
                        if htlc.SignDetails != nil {
6✔
1645
                                //nolint:ll
3✔
1646
                                bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock
3✔
1647
                                //nolint:ll
3✔
1648
                                tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
3✔
1649
                        }
3✔
1650
                } else {
3✔
1651
                        resID = newResolverID(htlc.ClaimOutpoint)
3✔
1652
                        //nolint:ll
3✔
1653
                        tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock
3✔
1654
                }
3✔
1655

1656
                htlc.ResolutionBlob.WhenSome(func(b []byte) {
6✔
1657
                        htlcBlobs[resID] = b
3✔
1658
                })
3✔
1659
        }
1660

1661
        if c.AnchorResolution != nil {
6✔
1662
                anchorSignDesc := c.AnchorResolution.AnchorSignDescriptor
3✔
1663
                tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak
3✔
1664
        }
3✔
1665

1666
        if len(htlcBlobs) != 0 {
6✔
1667
                tapCase.HtlcBlobs = tlv.SomeRecordT(
3✔
1668
                        tlv.NewRecordT[tlv.TlvType4](htlcBlobs),
3✔
1669
                )
3✔
1670
        }
3✔
1671

1672
        return tapCase.Encode(w)
3✔
1673
}
1674

1675
func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
3✔
1676
        tapCase := newTaprootBriefcase()
3✔
1677
        if err := tapCase.Decode(r); err != nil {
3✔
1678
                return err
×
1679
        }
×
1680

1681
        if c.CommitResolution != nil {
6✔
1682
                c.CommitResolution.SelfOutputSignDesc.ControlBlock =
3✔
1683
                        tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock
3✔
1684

3✔
1685
                tapCase.SettledCommitBlob.WhenSomeV(func(b []byte) {
6✔
1686
                        c.CommitResolution.ResolutionBlob = fn.Some(b)
3✔
1687
                })
3✔
1688
        }
1689

1690
        htlcBlobs := tapCase.HtlcBlobs.ValOpt().UnwrapOr(newAuxHtlcBlobs())
3✔
1691

3✔
1692
        for i := range c.HtlcResolutions.IncomingHTLCs {
6✔
1693
                htlc := c.HtlcResolutions.IncomingHTLCs[i]
3✔
1694

3✔
1695
                var resID resolverID
3✔
1696
                if htlc.SignedSuccessTx != nil {
6✔
1697
                        resID = newResolverID(
3✔
1698
                                htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
3✔
1699
                        )
3✔
1700

3✔
1701
                        //nolint:ll
3✔
1702
                        ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID]
3✔
1703
                        htlc.SweepSignDesc.ControlBlock = ctrlBlock
3✔
1704

3✔
1705
                        //nolint:ll
3✔
1706
                        if htlc.SignDetails != nil {
6✔
1707
                                bridgeCtrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID]
3✔
1708
                                htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock
3✔
1709
                        }
3✔
1710
                } else {
3✔
1711
                        resID = newResolverID(htlc.ClaimOutpoint)
3✔
1712

3✔
1713
                        //nolint:ll
3✔
1714
                        ctrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID]
3✔
1715
                        htlc.SweepSignDesc.ControlBlock = ctrlBlock
3✔
1716
                }
3✔
1717

1718
                if htlcBlob, ok := htlcBlobs[resID]; ok {
6✔
1719
                        htlc.ResolutionBlob = fn.Some(htlcBlob)
3✔
1720
                }
3✔
1721

1722
                c.HtlcResolutions.IncomingHTLCs[i] = htlc
3✔
1723

1724
        }
1725
        for i := range c.HtlcResolutions.OutgoingHTLCs {
6✔
1726
                htlc := c.HtlcResolutions.OutgoingHTLCs[i]
3✔
1727

3✔
1728
                var resID resolverID
3✔
1729
                if htlc.SignedTimeoutTx != nil {
6✔
1730
                        resID = newResolverID(
3✔
1731
                                htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
3✔
1732
                        )
3✔
1733

3✔
1734
                        //nolint:ll
3✔
1735
                        ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID]
3✔
1736
                        htlc.SweepSignDesc.ControlBlock = ctrlBlock
3✔
1737

3✔
1738
                        //nolint:ll
3✔
1739
                        if htlc.SignDetails != nil {
6✔
1740
                                bridgeCtrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID]
3✔
1741
                                htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock
3✔
1742
                        }
3✔
1743
                } else {
3✔
1744
                        resID = newResolverID(htlc.ClaimOutpoint)
3✔
1745

3✔
1746
                        //nolint:ll
3✔
1747
                        ctrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID]
3✔
1748
                        htlc.SweepSignDesc.ControlBlock = ctrlBlock
3✔
1749
                }
3✔
1750

1751
                if htlcBlob, ok := htlcBlobs[resID]; ok {
6✔
1752
                        htlc.ResolutionBlob = fn.Some(htlcBlob)
3✔
1753
                }
3✔
1754

1755
                c.HtlcResolutions.OutgoingHTLCs[i] = htlc
3✔
1756
        }
1757

1758
        if c.AnchorResolution != nil {
6✔
1759
                c.AnchorResolution.AnchorSignDescriptor.TapTweak =
3✔
1760
                        tapCase.TapTweaks.Val.AnchorTweak
3✔
1761
        }
3✔
1762

1763
        return nil
3✔
1764
}
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