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

lightningnetwork / lnd / 11166428937

03 Oct 2024 05:07PM UTC coverage: 58.738% (-0.08%) from 58.817%
11166428937

push

github

web-flow
Merge pull request #8960 from lightningnetwork/0-19-staging-rebased

[custom channels 5/5]: merge custom channel staging branch into master

657 of 875 new or added lines in 29 files covered. (75.09%)

260 existing lines in 20 files now uncovered.

130540 of 222243 relevant lines covered (58.74%)

28084.43 hits per line

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

80.17
/contractcourt/htlc_incoming_contest_resolver.go
1
package contractcourt
2

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

10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/txscript"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/channeldb/models"
14
        "github.com/lightningnetwork/lnd/fn"
15
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
16
        "github.com/lightningnetwork/lnd/invoices"
17
        "github.com/lightningnetwork/lnd/lntypes"
18
        "github.com/lightningnetwork/lnd/lnwallet"
19
        "github.com/lightningnetwork/lnd/lnwire"
20
        "github.com/lightningnetwork/lnd/queue"
21
)
22

23
// htlcIncomingContestResolver is a ContractResolver that's able to resolve an
24
// incoming HTLC that is still contested. An HTLC is still contested, if at the
25
// time of commitment broadcast, we don't know of the preimage for it yet, and
26
// it hasn't expired. In this case, we can resolve the HTLC if we learn of the
27
// preimage, otherwise the remote party will sweep it after it expires.
28
//
29
// TODO(roasbeef): just embed the other resolver?
30
type htlcIncomingContestResolver struct {
31
        // htlcExpiry is the absolute expiry of this incoming HTLC. We use this
32
        // value to determine if we can exit early as if the HTLC times out,
33
        // before we learn of the preimage then we can't claim it on chain
34
        // successfully.
35
        htlcExpiry uint32
36

37
        // htlcSuccessResolver is the inner resolver that may be utilized if we
38
        // learn of the preimage.
39
        *htlcSuccessResolver
40
}
41

42
// newIncomingContestResolver instantiates a new incoming htlc contest resolver.
43
func newIncomingContestResolver(
44
        res lnwallet.IncomingHtlcResolution, broadcastHeight uint32,
45
        htlc channeldb.HTLC, resCfg ResolverConfig) *htlcIncomingContestResolver {
3✔
46

3✔
47
        success := newSuccessResolver(
3✔
48
                res, broadcastHeight, htlc, resCfg,
3✔
49
        )
3✔
50

3✔
51
        return &htlcIncomingContestResolver{
3✔
52
                htlcExpiry:          htlc.RefundTimeout,
3✔
53
                htlcSuccessResolver: success,
3✔
54
        }
3✔
55
}
3✔
56

57
func (h *htlcIncomingContestResolver) processFinalHtlcFail() error {
8✔
58
        // Mark the htlc as final failed.
8✔
59
        err := h.ChainArbitratorConfig.PutFinalHtlcOutcome(
8✔
60
                h.ChannelArbitratorConfig.ShortChanID, h.htlc.HtlcIndex, false,
8✔
61
        )
8✔
62
        if err != nil {
8✔
63
                return err
×
64
        }
×
65

66
        // Send notification.
67
        h.ChainArbitratorConfig.HtlcNotifier.NotifyFinalHtlcEvent(
8✔
68
                models.CircuitKey{
8✔
69
                        ChanID: h.ShortChanID,
8✔
70
                        HtlcID: h.htlc.HtlcIndex,
8✔
71
                },
8✔
72
                channeldb.FinalHtlcInfo{
8✔
73
                        Settled:  false,
8✔
74
                        Offchain: false,
8✔
75
                },
8✔
76
        )
8✔
77

8✔
78
        return nil
8✔
79
}
80

81
// Resolve attempts to resolve this contract. As we don't yet know of the
82
// preimage for the contract, we'll wait for one of two things to happen:
83
//
84
//  1. We learn of the preimage! In this case, we can sweep the HTLC incoming
85
//     and ensure that if this was a multi-hop HTLC we are made whole. In this
86
//     case, an additional ContractResolver will be returned to finish the
87
//     job.
88
//
89
//  2. The HTLC expires. If this happens, then the contract is fully resolved
90
//     as we have no remaining actions left at our disposal.
91
//
92
// NOTE: Part of the ContractResolver interface.
93
func (h *htlcIncomingContestResolver) Resolve(
94
        _ bool) (ContractResolver, error) {
12✔
95

12✔
96
        // If we're already full resolved, then we don't have anything further
12✔
97
        // to do.
12✔
98
        if h.resolved {
12✔
99
                return nil, nil
×
100
        }
×
101

102
        // If the HTLC has custom records, then for now we'll pause resolution.
103
        //
104
        // TODO(roasbeef): Implement resolving HTLCs with custom records
105
        // (follow-up PR).
106
        if len(h.htlc.CustomRecords) != 0 {
12✔
NEW
107
                select { //nolint:gosimple
×
NEW
108
                case <-h.quit:
×
NEW
109
                        return nil, errResolverShuttingDown
×
110
                }
111
        }
112

113
        // First try to parse the payload. If that fails, we can stop resolution
114
        // now.
115
        payload, nextHopOnionBlob, err := h.decodePayload()
12✔
116
        if err != nil {
12✔
117
                log.Debugf("ChannelArbitrator(%v): cannot decode payload of "+
×
118
                        "htlc %v", h.ChanPoint, h.HtlcPoint())
×
119

×
120
                // If we've locked in an htlc with an invalid payload on our
×
121
                // commitment tx, we don't need to resolve it. The other party
×
122
                // will time it out and get their funds back. This situation
×
123
                // can present itself when we crash before processRemoteAdds in
×
124
                // the link has ran.
×
125
                h.resolved = true
×
126

×
127
                if err := h.processFinalHtlcFail(); err != nil {
×
128
                        return nil, err
×
129
                }
×
130

131
                // We write a report to disk that indicates we could not decode
132
                // the htlc.
133
                resReport := h.report().resolverReport(
×
134
                        nil, channeldb.ResolverTypeIncomingHtlc,
×
135
                        channeldb.ResolverOutcomeAbandoned,
×
136
                )
×
137
                return nil, h.PutResolverReport(nil, resReport)
×
138
        }
139

140
        // Register for block epochs. After registration, the current height
141
        // will be sent on the channel immediately.
142
        blockEpochs, err := h.Notifier.RegisterBlockEpochNtfn(nil)
12✔
143
        if err != nil {
12✔
144
                return nil, err
×
145
        }
×
146
        defer blockEpochs.Cancel()
12✔
147

12✔
148
        var currentHeight int32
12✔
149
        select {
12✔
150
        case newBlock, ok := <-blockEpochs.Epochs:
12✔
151
                if !ok {
12✔
152
                        return nil, errResolverShuttingDown
×
153
                }
×
154
                currentHeight = newBlock.Height
12✔
155
        case <-h.quit:
×
156
                return nil, errResolverShuttingDown
×
157
        }
158

159
        log.Debugf("%T(%v): Resolving incoming HTLC(expiry=%v, height=%v)", h,
12✔
160
                h.htlcResolution.ClaimOutpoint, h.htlcExpiry, currentHeight)
12✔
161

12✔
162
        // We'll first check if this HTLC has been timed out, if so, we can
12✔
163
        // return now and mark ourselves as resolved. If we're past the point of
12✔
164
        // expiry of the HTLC, then at this point the sender can sweep it, so
12✔
165
        // we'll end our lifetime. Here we deliberately forego the chance that
12✔
166
        // the sender doesn't sweep and we already have or will learn the
12✔
167
        // preimage. Otherwise the resolver could potentially stay active
12✔
168
        // indefinitely and the channel will never close properly.
12✔
169
        if uint32(currentHeight) >= h.htlcExpiry {
16✔
170
                // TODO(roasbeef): should also somehow check if outgoing is
4✔
171
                // resolved or not
4✔
172
                //  * may need to hook into the circuit map
4✔
173
                //  * can't timeout before the outgoing has been
4✔
174

4✔
175
                log.Infof("%T(%v): HTLC has timed out (expiry=%v, height=%v), "+
4✔
176
                        "abandoning", h, h.htlcResolution.ClaimOutpoint,
4✔
177
                        h.htlcExpiry, currentHeight)
4✔
178
                h.resolved = true
4✔
179

4✔
180
                if err := h.processFinalHtlcFail(); err != nil {
4✔
181
                        return nil, err
×
182
                }
×
183

184
                // Finally, get our report and checkpoint our resolver with a
185
                // timeout outcome report.
186
                report := h.report().resolverReport(
4✔
187
                        nil, channeldb.ResolverTypeIncomingHtlc,
4✔
188
                        channeldb.ResolverOutcomeTimeout,
4✔
189
                )
4✔
190
                return nil, h.Checkpoint(h, report)
4✔
191
        }
192

193
        // applyPreimage is a helper function that will populate our internal
194
        // resolver with the preimage we learn of. This should be called once
195
        // the preimage is revealed so the inner resolver can properly complete
196
        // its duties. The error return value indicates whether the preimage
197
        // was properly applied.
198
        applyPreimage := func(preimage lntypes.Preimage) error {
18✔
199
                // Sanity check to see if this preimage matches our htlc. At
7✔
200
                // this point it should never happen that it does not match.
7✔
201
                if !preimage.Matches(h.htlc.RHash) {
7✔
202
                        return errors.New("preimage does not match hash")
×
203
                }
×
204

205
                // Update htlcResolution with the matching preimage.
206
                h.htlcResolution.Preimage = preimage
7✔
207

7✔
208
                log.Infof("%T(%v): applied preimage=%v", h,
7✔
209
                        h.htlcResolution.ClaimOutpoint, preimage)
7✔
210

7✔
211
                isSecondLevel := h.htlcResolution.SignedSuccessTx != nil
7✔
212

7✔
213
                // If we didn't have to go to the second level to claim (this
7✔
214
                // is the remote commitment transaction), then we don't need to
7✔
215
                // modify our canned witness.
7✔
216
                if !isSecondLevel {
14✔
217
                        return nil
7✔
218
                }
7✔
219

220
                isTaproot := txscript.IsPayToTaproot(
3✔
221
                        h.htlcResolution.SignedSuccessTx.TxOut[0].PkScript,
3✔
222
                )
3✔
223

3✔
224
                // If this is our commitment transaction, then we'll need to
3✔
225
                // populate the witness for the second-level HTLC transaction.
3✔
226
                switch {
3✔
227
                // For taproot channels, the witness for sweeping with success
228
                // looks like:
229
                //   - <sender sig> <receiver sig> <preimage> <success_script>
230
                //     <control_block>
231
                //
232
                // So we'll insert it at the 3rd index of the witness.
233
                case isTaproot:
3✔
234
                        //nolint:lll
3✔
235
                        h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[2] = preimage[:]
3✔
236

237
                // Within the witness for the success transaction, the
238
                // preimage is the 4th element as it looks like:
239
                //
240
                //  * <0> <sender sig> <recvr sig> <preimage> <witness script>
241
                //
242
                // We'll populate it within the witness, as since this
243
                // was a "contest" resolver, we didn't yet know of the
244
                // preimage.
245
                case !isTaproot:
3✔
246
                        h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
3✔
247
                }
248

249
                return nil
3✔
250
        }
251

252
        // Define a closure to process htlc resolutions either directly or
253
        // triggered by future notifications.
254
        processHtlcResolution := func(e invoices.HtlcResolution) (
11✔
255
                ContractResolver, error) {
18✔
256

7✔
257
                // Take action based on the type of resolution we have
7✔
258
                // received.
7✔
259
                switch resolution := e.(type) {
7✔
260
                // If the htlc resolution was a settle, apply the
261
                // preimage and return a success resolver.
262
                case *invoices.HtlcSettleResolution:
5✔
263
                        err := applyPreimage(resolution.Preimage)
5✔
264
                        if err != nil {
5✔
265
                                return nil, err
×
266
                        }
×
267

268
                        return h.htlcSuccessResolver, nil
5✔
269

270
                // If the htlc was failed, mark the htlc as
271
                // resolved.
272
                case *invoices.HtlcFailResolution:
5✔
273
                        log.Infof("%T(%v): Exit hop HTLC canceled "+
5✔
274
                                "(expiry=%v, height=%v), abandoning", h,
5✔
275
                                h.htlcResolution.ClaimOutpoint,
5✔
276
                                h.htlcExpiry, currentHeight)
5✔
277

5✔
278
                        h.resolved = true
5✔
279

5✔
280
                        if err := h.processFinalHtlcFail(); err != nil {
5✔
281
                                return nil, err
×
282
                        }
×
283

284
                        // Checkpoint our resolver with an abandoned outcome
285
                        // because we take no further action on this htlc.
286
                        report := h.report().resolverReport(
5✔
287
                                nil, channeldb.ResolverTypeIncomingHtlc,
5✔
288
                                channeldb.ResolverOutcomeAbandoned,
5✔
289
                        )
5✔
290
                        return nil, h.Checkpoint(h, report)
5✔
291

292
                // Error if the resolution type is unknown, we are only
293
                // expecting settles and fails.
294
                default:
×
295
                        return nil, fmt.Errorf("unknown resolution"+
×
296
                                " type: %v", e)
×
297
                }
298
        }
299

300
        var (
11✔
301
                hodlChan       <-chan interface{}
11✔
302
                witnessUpdates <-chan lntypes.Preimage
11✔
303
        )
11✔
304
        if payload.FwdInfo.NextHop == hop.Exit {
19✔
305
                // Create a buffered hodl chan to prevent deadlock.
8✔
306
                hodlQueue := queue.NewConcurrentQueue(10)
8✔
307
                hodlQueue.Start()
8✔
308

8✔
309
                hodlChan = hodlQueue.ChanOut()
8✔
310

8✔
311
                // Notify registry that we are potentially resolving as an exit
8✔
312
                // hop on-chain. If this HTLC indeed pays to an existing
8✔
313
                // invoice, the invoice registry will tell us what to do with
8✔
314
                // the HTLC. This is identical to HTLC resolution in the link.
8✔
315
                circuitKey := models.CircuitKey{
8✔
316
                        ChanID: h.ShortChanID,
8✔
317
                        HtlcID: h.htlc.HtlcIndex,
8✔
318
                }
8✔
319

8✔
320
                resolution, err := h.Registry.NotifyExitHopHtlc(
8✔
321
                        h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, currentHeight,
8✔
322
                        circuitKey, hodlQueue.ChanIn(), nil, payload,
8✔
323
                )
8✔
324
                if err != nil {
8✔
325
                        return nil, err
×
326
                }
×
327

328
                defer func() {
16✔
329
                        h.Registry.HodlUnsubscribeAll(hodlQueue.ChanIn())
8✔
330

8✔
331
                        hodlQueue.Stop()
8✔
332
                }()
8✔
333

334
                // Take action based on the resolution we received. If the htlc
335
                // was settled, or a htlc for a known invoice failed we can
336
                // resolve it directly. If the resolution is nil, the htlc was
337
                // neither accepted nor failed, so we cannot take action yet.
338
                switch res := resolution.(type) {
8✔
339
                case *invoices.HtlcFailResolution:
4✔
340
                        // In the case where the htlc failed, but the invoice
4✔
341
                        // was known to the registry, we can directly resolve
4✔
342
                        // the htlc.
4✔
343
                        if res.Outcome != invoices.ResultInvoiceNotFound {
5✔
344
                                return processHtlcResolution(resolution)
1✔
345
                        }
1✔
346

347
                // If we settled the htlc, we can resolve it.
348
                case *invoices.HtlcSettleResolution:
4✔
349
                        return processHtlcResolution(resolution)
4✔
350

351
                // If the resolution is nil, the htlc was neither settled nor
352
                // failed so we cannot take action at present.
353
                case nil:
6✔
354

355
                default:
×
356
                        return nil, fmt.Errorf("unknown htlc resolution type: %T",
×
357
                                resolution)
×
358
                }
359
        } else {
6✔
360
                // If the HTLC hasn't expired yet, then we may still be able to
6✔
361
                // claim it if we learn of the pre-image, so we'll subscribe to
6✔
362
                // the preimage database to see if it turns up, or the HTLC
6✔
363
                // times out.
6✔
364
                //
6✔
365
                // NOTE: This is done BEFORE opportunistically querying the db,
6✔
366
                // to ensure the preimage can't be delivered between querying
6✔
367
                // and registering for the preimage subscription.
6✔
368
                preimageSubscription, err := h.PreimageDB.SubscribeUpdates(
6✔
369
                        h.htlcSuccessResolver.ShortChanID, &h.htlc,
6✔
370
                        payload, nextHopOnionBlob,
6✔
371
                )
6✔
372
                if err != nil {
6✔
373
                        return nil, err
×
374
                }
×
375
                defer preimageSubscription.CancelSubscription()
6✔
376

6✔
377
                // With the epochs and preimage subscriptions initialized, we'll
6✔
378
                // query to see if we already know the preimage.
6✔
379
                preimage, ok := h.PreimageDB.LookupPreimage(h.htlc.RHash)
6✔
380
                if ok {
10✔
381
                        // If we do, then this means we can claim the HTLC!
4✔
382
                        // However, we don't know how to ourselves, so we'll
4✔
383
                        // return our inner resolver which has the knowledge to
4✔
384
                        // do so.
4✔
385
                        if err := applyPreimage(preimage); err != nil {
4✔
386
                                return nil, err
×
387
                        }
×
388

389
                        return h.htlcSuccessResolver, nil
4✔
390
                }
391

392
                witnessUpdates = preimageSubscription.WitnessUpdates
5✔
393
        }
394

395
        for {
17✔
396
                select {
9✔
397
                case preimage := <-witnessUpdates:
4✔
398
                        // We received a new preimage, but we need to ignore
4✔
399
                        // all except the preimage we are waiting for.
4✔
400
                        if !preimage.Matches(h.htlc.RHash) {
7✔
401
                                continue
3✔
402
                        }
403

404
                        if err := applyPreimage(preimage); err != nil {
4✔
405
                                return nil, err
×
406
                        }
×
407

408
                        // We've learned of the preimage and this information
409
                        // has been added to our inner resolver. We return it so
410
                        // it can continue contract resolution.
411
                        return h.htlcSuccessResolver, nil
4✔
412

413
                case hodlItem := <-hodlChan:
5✔
414
                        htlcResolution := hodlItem.(invoices.HtlcResolution)
5✔
415
                        return processHtlcResolution(htlcResolution)
5✔
416

417
                case newBlock, ok := <-blockEpochs.Epochs:
6✔
418
                        if !ok {
6✔
419
                                return nil, errResolverShuttingDown
×
420
                        }
×
421

422
                        // If this new height expires the HTLC, then this means
423
                        // we never found out the preimage, so we can mark
424
                        // resolved and exit.
425
                        newHeight := uint32(newBlock.Height)
6✔
426
                        if newHeight >= h.htlcExpiry {
11✔
427
                                log.Infof("%T(%v): HTLC has timed out "+
5✔
428
                                        "(expiry=%v, height=%v), abandoning", h,
5✔
429
                                        h.htlcResolution.ClaimOutpoint,
5✔
430
                                        h.htlcExpiry, currentHeight)
5✔
431
                                h.resolved = true
5✔
432

5✔
433
                                if err := h.processFinalHtlcFail(); err != nil {
5✔
434
                                        return nil, err
×
435
                                }
×
436

437
                                report := h.report().resolverReport(
5✔
438
                                        nil,
5✔
439
                                        channeldb.ResolverTypeIncomingHtlc,
5✔
440
                                        channeldb.ResolverOutcomeTimeout,
5✔
441
                                )
5✔
442
                                return nil, h.Checkpoint(h, report)
5✔
443
                        }
444

445
                case <-h.quit:
3✔
446
                        return nil, errResolverShuttingDown
3✔
447
                }
448
        }
449
}
450

451
// report returns a report on the resolution state of the contract.
452
func (h *htlcIncomingContestResolver) report() *ContractReport {
8✔
453
        // No locking needed as these values are read-only.
8✔
454

8✔
455
        finalAmt := h.htlc.Amt.ToSatoshis()
8✔
456
        if h.htlcResolution.SignedSuccessTx != nil {
11✔
457
                finalAmt = btcutil.Amount(
3✔
458
                        h.htlcResolution.SignedSuccessTx.TxOut[0].Value,
3✔
459
                )
3✔
460
        }
3✔
461

462
        return &ContractReport{
8✔
463
                Outpoint:       h.htlcResolution.ClaimOutpoint,
8✔
464
                Type:           ReportOutputIncomingHtlc,
8✔
465
                Amount:         finalAmt,
8✔
466
                MaturityHeight: h.htlcExpiry,
8✔
467
                LimboBalance:   finalAmt,
8✔
468
                Stage:          1,
8✔
469
        }
8✔
470
}
471

472
// Stop signals the resolver to cancel any current resolution processes, and
473
// suspend.
474
//
475
// NOTE: Part of the ContractResolver interface.
476
func (h *htlcIncomingContestResolver) Stop() {
3✔
477
        close(h.quit)
3✔
478
}
3✔
479

480
// IsResolved returns true if the stored state in the resolve is fully
481
// resolved. In this case the target output can be forgotten.
482
//
483
// NOTE: Part of the ContractResolver interface.
484
func (h *htlcIncomingContestResolver) IsResolved() bool {
3✔
485
        return h.resolved
3✔
486
}
3✔
487

488
// Encode writes an encoded version of the ContractResolver into the passed
489
// Writer.
490
//
491
// NOTE: Part of the ContractResolver interface.
492
func (h *htlcIncomingContestResolver) Encode(w io.Writer) error {
4✔
493
        // We'll first write out the one field unique to this resolver.
4✔
494
        if err := binary.Write(w, endian, h.htlcExpiry); err != nil {
4✔
495
                return err
×
496
        }
×
497

498
        // Then we'll write out our internal resolver.
499
        return h.htlcSuccessResolver.Encode(w)
4✔
500
}
501

502
// newIncomingContestResolverFromReader attempts to decode an encoded ContractResolver
503
// from the passed Reader instance, returning an active ContractResolver
504
// instance.
505
func newIncomingContestResolverFromReader(r io.Reader, resCfg ResolverConfig) (
506
        *htlcIncomingContestResolver, error) {
4✔
507

4✔
508
        h := &htlcIncomingContestResolver{}
4✔
509

4✔
510
        // We'll first read the one field unique to this resolver.
4✔
511
        if err := binary.Read(r, endian, &h.htlcExpiry); err != nil {
4✔
512
                return nil, err
×
513
        }
×
514

515
        // Then we'll decode our internal resolver.
516
        successResolver, err := newSuccessResolverFromReader(r, resCfg)
4✔
517
        if err != nil {
4✔
518
                return nil, err
×
519
        }
×
520
        h.htlcSuccessResolver = successResolver
4✔
521

4✔
522
        return h, nil
4✔
523
}
524

525
// Supplement adds additional information to the resolver that is required
526
// before Resolve() is called.
527
//
528
// NOTE: Part of the htlcContractResolver interface.
529
func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) {
3✔
530
        h.htlc = htlc
3✔
531
}
3✔
532

533
// SupplementDeadline does nothing for an incoming htlc resolver.
534
//
535
// NOTE: Part of the htlcContractResolver interface.
536
func (h *htlcIncomingContestResolver) SupplementDeadline(_ fn.Option[int32]) {
×
537
}
×
538

539
// decodePayload (re)decodes the hop payload of a received htlc.
540
func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
541
        []byte, error) {
12✔
542

12✔
543
        blindingInfo := hop.ReconstructBlindingInfo{
12✔
544
                IncomingAmt:    h.htlc.Amt,
12✔
545
                IncomingExpiry: h.htlc.RefundTimeout,
12✔
546
                BlindingKey:    h.htlc.BlindingPoint,
12✔
547
        }
12✔
548

12✔
549
        onionReader := bytes.NewReader(h.htlc.OnionBlob[:])
12✔
550
        iterator, err := h.OnionProcessor.ReconstructHopIterator(
12✔
551
                onionReader, h.htlc.RHash[:], blindingInfo,
12✔
552
        )
12✔
553
        if err != nil {
12✔
554
                return nil, nil, err
×
555
        }
×
556

557
        payload, _, err := iterator.HopPayload()
12✔
558
        if err != nil {
12✔
559
                return nil, nil, err
×
560
        }
×
561

562
        // Transform onion blob for the next hop.
563
        var onionBlob [lnwire.OnionPacketSize]byte
12✔
564
        buf := bytes.NewBuffer(onionBlob[0:0])
12✔
565
        err = iterator.EncodeNextHop(buf)
12✔
566
        if err != nil {
12✔
567
                return nil, nil, err
×
568
        }
×
569

570
        return payload, onionBlob[:], nil
12✔
571
}
572

573
// A compile time assertion to ensure htlcIncomingContestResolver meets the
574
// ContractResolver interface.
575
var _ htlcContractResolver = (*htlcIncomingContestResolver)(nil)
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