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

lightningnetwork / lnd / 13598365092

28 Feb 2025 11:36PM UTC coverage: 58.81%. First build
13598365092

Pull #9559

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

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

The tests have been updated accordingly.
Pull Request #9559: multi: preliminary changes towards integrating the new RBF close protocol feature

89 of 253 new or added lines in 15 files covered. (35.18%)

136460 of 232035 relevant lines covered (58.81%)

19246.53 hits per line

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

33.79
/lnwallet/chancloser/rbf_coop_states.go
1
package chancloser
2

3
import (
4
        "fmt"
5

6
        "github.com/btcsuite/btcd/btcec/v2"
7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/btcsuite/btcd/chaincfg"
9
        "github.com/btcsuite/btcd/wire"
10
        "github.com/lightningnetwork/lnd/chainntnfs"
11
        "github.com/lightningnetwork/lnd/channeldb"
12
        "github.com/lightningnetwork/lnd/fn/v2"
13
        "github.com/lightningnetwork/lnd/input"
14
        "github.com/lightningnetwork/lnd/lntypes"
15
        "github.com/lightningnetwork/lnd/lnwallet"
16
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
17
        "github.com/lightningnetwork/lnd/lnwire"
18
        "github.com/lightningnetwork/lnd/protofsm"
19
)
20

21
var (
22
        // ErrInvalidStateTransition is returned when we receive an unexpected
23
        // event for a given state.
24
        ErrInvalidStateTransition = fmt.Errorf("invalid state transition")
25

26
        // ErrTooManySigs is returned when we receive too many sigs from the
27
        // remote party in the ClosingSigs message.
28
        ErrTooManySigs = fmt.Errorf("too many sigs received")
29

30
        // ErrNoSig is returned when we receive no sig from the remote party.
31
        ErrNoSig = fmt.Errorf("no sig received")
32

33
        // ErrUnknownFinalBalance is returned if we're unable to determine the
34
        // final channel balance after a flush.
35
        ErrUnknownFinalBalance = fmt.Errorf("unknown final balance")
36

37
        // ErrRemoteCannotPay is returned if the remote party cannot pay the
38
        // pay for the fees when it sends a signature.
39
        ErrRemoteCannotPay = fmt.Errorf("remote cannot pay fees")
40

41
        // ErrNonFinalSequence is returned if we receive a non-final sequence
42
        // from the remote party for their signature.
43
        ErrNonFinalSequence = fmt.Errorf("received non-final sequence")
44

45
        // ErrCloserNoClosee is returned if our balance is dust, but the remote
46
        // party includes our output.
47
        ErrCloserNoClosee = fmt.Errorf("expected CloserNoClosee sig")
48

49
        // ErrCloserAndClosee is returned when we expect a sig covering both
50
        // outputs, it isn't present.
51
        ErrCloserAndClosee = fmt.Errorf("expected CloserAndClosee sig")
52
)
53

54
// ProtocolEvent is a special interface used to create the equivalent of a
55
// sum-type, but using a "sealed" interface. Protocol events can be used as
56
// input to trigger a state transition, and also as output to trigger a new set
57
// of events into the very same state machine.
58
type ProtocolEvent interface {
59
        protocolSealed()
60
}
61

62
// ProtocolEvents is a special type constraint that enumerates all the possible
63
// protocol events. This is used mainly as type-level documentation, and may
64
// also be useful to constraint certain state transition functions.
65
type ProtocolEvents interface {
66
        SendShutdown | ShutdownReceived | ShutdownComplete | ChannelFlushed |
67
                SendOfferEvent | OfferReceivedEvent | LocalSigReceived |
68
                SpendEvent
69
}
70

71
// SpendEvent indicates that a transaction spending the funding outpoint has
72
// been confirmed in the main chain.
73
type SpendEvent struct {
74
        // Tx is the spending transaction that has been confirmed.
75
        Tx *wire.MsgTx
76

77
        // BlockHeight is the height of the block that confirmed the
78
        // transaction.
79
        BlockHeight uint32
80
}
81

82
// protocolSealed indicates that this struct is a ProtocolEvent instance.
83
func (s *SpendEvent) protocolSealed() {}
×
84

85
// SendShutdown indicates that the user wishes to co-op close the channel, so we
86
// should send a new shutdown message to the remote party.
87
//
88
// transition:
89
//   - fromState: ChannelActive
90
//   - toState: ChannelFlushing
91
type SendShutdown struct {
92
        // DeliveryAddr is the address we'd like to receive the funds to. If
93
        // None, then a new addr will be generated.
94
        DeliveryAddr fn.Option[lnwire.DeliveryAddress]
95

96
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
97
        // attempt.
98
        IdealFeeRate chainfee.SatPerVByte
99
}
100

101
// protocolSealed indicates that this struct is a ProtocolEvent instance.
102
func (s *SendShutdown) protocolSealed() {}
×
103

104
// ShutdownReceived indicates that we received a shutdown event so we need to
105
// enter the flushing state.
106
//
107
// transition:
108
//   - fromState: ChannelActive
109
//   - toState: ChannelFlushing
110
type ShutdownReceived struct {
111
        // ShutdownScript is the script the remote party wants to use to
112
        // shutdown.
113
        ShutdownScript lnwire.DeliveryAddress
114

115
        // BlockHeight is the height at which the shutdown message was
116
        // received. This is used for channel leases to determine if a co-op
117
        // close can occur.
118
        BlockHeight uint32
119
}
120

121
// protocolSealed indicates that this struct is a ProtocolEvent instance.
122
func (s *ShutdownReceived) protocolSealed() {}
×
123

124
// ShutdownComplete is an event that indicates the channel has been fully
125
// shutdown. At this point, we'll go to the ChannelFlushing state so we can
126
// wait for all pending updates to be gone from the channel.
127
//
128
// transition:
129
//   - fromState: ShutdownPending
130
//   - toState: ChannelFlushing
131
type ShutdownComplete struct {
132
}
133

134
// protocolSealed indicates that this struct is a ProtocolEvent instance.
135
func (s *ShutdownComplete) protocolSealed() {}
×
136

137
// ShutdownBalances holds the local+remote balance once the channel has been
138
// fully flushed.
139
type ShutdownBalances struct {
140
        // LocalBalance is the local balance of the channel.
141
        LocalBalance lnwire.MilliSatoshi
142

143
        // RemoteBalance is the remote balance of the channel.
144
        RemoteBalance lnwire.MilliSatoshi
145
}
146

147
// unknownBalance is a special variable used to denote an unknown channel
148
// balance (channel not fully flushed yet).
149
var unknownBalance = ShutdownBalances{}
150

151
// ChannelFlushed is an event that indicates the channel has been fully flushed
152
// can we can now start closing negotiation.
153
//
154
// transition:
155
//   - fromState: ChannelFlushing
156
//   - toState: ClosingNegotiation
157
type ChannelFlushed struct {
158
        // FreshFlush indicates if this is the first time the channel has been
159
        // flushed, or if this is a flush as part of an RBF iteration.
160
        FreshFlush bool
161

162
        // ShutdownBalances is the balances of the channel once it has been
163
        // flushed. We tie this to the ChannelFlushed state as this may not be
164
        // the same as the starting value.
165
        ShutdownBalances
166
}
167

168
// protocolSealed indicates that this struct is a ProtocolEvent instance.
169
func (c *ChannelFlushed) protocolSealed() {}
×
170

171
// SendOfferEvent is a self-triggered event that transitions us from the
172
// LocalCloseStart state to the LocalOfferSent state. This kicks off the new
173
// signing process for the co-op close process.
174
//
175
// transition:
176
//   - fromState: LocalCloseStart
177
//   - toState: LocalOfferSent
178
type SendOfferEvent struct {
179
        // TargetFeeRate is the fee rate we'll use for the closing transaction.
180
        TargetFeeRate chainfee.SatPerVByte
181
}
182

183
// protocolSealed indicates that this struct is a ProtocolEvent instance.
184
func (s *SendOfferEvent) protocolSealed() {}
×
185

186
// LocalSigReceived is an event that indicates we've received a signature from
187
// the remote party, which signs our the co-op close transaction at our
188
// specified fee rate.
189
//
190
// transition:
191
//   - fromState: LocalOfferSent
192
//   - toState: ClosePending
193
type LocalSigReceived struct {
194
        // SigMsg is the sig message we received from the remote party.
195
        SigMsg lnwire.ClosingSig
196
}
197

198
// protocolSealed indicates that this struct is a ProtocolEvent instance.
199
func (s *LocalSigReceived) protocolSealed() {}
×
200

201
// OfferReceivedEvent is an event that indicates we've received an offer from
202
// the remote party. This applies to the RemoteCloseStart state.
203
//
204
// transition:
205
//   - fromState: RemoteCloseStart
206
//   - toState: ClosePending
207
type OfferReceivedEvent struct {
208
        // SigMsg is the signature message we received from the remote party.
209
        SigMsg lnwire.ClosingComplete
210
}
211

212
// protocolSealed indicates that this struct is a ProtocolEvent instance.
213
func (s *OfferReceivedEvent) protocolSealed() {}
×
214

215
// CloseSigner is an interface that abstracts away the details of the signing
216
// new coop close transactions.
217
type CloseSigner interface {
218
        // CreateCloseProposal creates a new co-op close proposal in the form
219
        // of a valid signature, the chainhash of the final txid, and our final
220
        // balance in the created state.
221
        CreateCloseProposal(proposedFee btcutil.Amount,
222
                localDeliveryScript []byte, remoteDeliveryScript []byte,
223
                closeOpt ...lnwallet.ChanCloseOpt,
224
        ) (
225
                input.Signature, *wire.MsgTx, btcutil.Amount, error)
226

227
        // CompleteCooperativeClose persistently "completes" the cooperative
228
        // close by producing a fully signed co-op close transaction.
229
        CompleteCooperativeClose(localSig, remoteSig input.Signature,
230
                localDeliveryScript, remoteDeliveryScript []byte,
231
                proposedFee btcutil.Amount, closeOpt ...lnwallet.ChanCloseOpt,
232
        ) (*wire.MsgTx, btcutil.Amount, error)
233
}
234

235
// ChanStateObserver is an interface used to observe state changes that occur
236
// in a channel. This can be used to figure out if we're able to send a
237
// shutdown message or not.
238
type ChanStateObserver interface {
239
        // NoDanglingUpdates returns true if there are no dangling updates in
240
        // the channel. In other words, there are no active update messages
241
        // that haven't already been covered by a commit sig.
242
        NoDanglingUpdates() bool
243

244
        // DisableIncomingAdds instructs the channel link to disable process new
245
        // incoming add messages.
246
        DisableIncomingAdds() error
247

248
        // DisableOutgoingAdds instructs the channel link to disable process
249
        // new outgoing add messages.
250
        DisableOutgoingAdds() error
251

252
        // DisableChannel attempts to disable a channel (marking it ineligible
253
        // to forward), and also sends out a network update to disable the
254
        // channel.
255
        DisableChannel() error
256

257
        // MarkCoopBroadcasted persistently marks that the channel close
258
        // transaction has been broadcast.
259
        MarkCoopBroadcasted(*wire.MsgTx, bool) error
260

261
        // MarkShutdownSent persists the given ShutdownInfo. The existence of
262
        // the ShutdownInfo represents the fact that the Shutdown message has
263
        // been sent by us and so should be re-sent on re-establish.
264
        MarkShutdownSent(deliveryAddr []byte, isInitiator bool) error
265

266
        // FinalBalances is the balances of the channel once it has been
267
        // flushed. If Some, then this indicates that the channel is now in a
268
        // state where it's always flushed, so we can accelerate the state
269
        // transitions.
270
        FinalBalances() fn.Option[ShutdownBalances]
271
}
272

273
// Environment is a set of dependencies that a state machine may need to carry
274
// out the logic for a given state transition. All fields are to be considered
275
// immutable, and will be fixed for the lifetime of the state machine.
276
type Environment struct {
277
        // ChainParams is the chain parameters for the channel.
278
        ChainParams chaincfg.Params
279

280
        // ChanPeer is the peer we're attempting to close the channel with.
281
        ChanPeer btcec.PublicKey
282

283
        // ChanPoint is the channel point of the active channel.
284
        ChanPoint wire.OutPoint
285

286
        // ChanID is the channel ID of the channel we're attempting to close.
287
        ChanID lnwire.ChannelID
288

289
        // ShortChanID is the short channel ID of the channel we're attempting
290
        // to close.
291
        Scid lnwire.ShortChannelID
292

293
        // ChanType is the type of channel we're attempting to close.
294
        ChanType channeldb.ChannelType
295

296
        // BlockHeight is the current block height.
297
        BlockHeight uint32
298

299
        // DefaultFeeRate is the fee we'll use for the closing transaction if
300
        // the user didn't specify an ideal fee rate. This may happen if the
301
        // remote party is the one that initiates the co-op close.
302
        DefaultFeeRate chainfee.SatPerVByte
303

304
        // ThawHeight is the height at which the channel will be thawed. If
305
        // this is None, then co-op close can occur at any moment.
306
        ThawHeight fn.Option[uint32]
307

308
        // RemoteUprontShutdown is the upfront shutdown addr of the remote
309
        // party. We'll use this to validate if the remote peer is authorized to
310
        // close the channel with the sent addr or not.
311
        RemoteUpfrontShutdown fn.Option[lnwire.DeliveryAddress]
312

313
        // LocalUprontShutdown is our upfront shutdown address. If Some, then
314
        // we'll default to using this.
315
        LocalUpfrontShutdown fn.Option[lnwire.DeliveryAddress]
316

317
        // NewDeliveryScript is a function that returns a new delivery script.
318
        // This is used if we don't have an upfront shutdown addr, and no addr
319
        // was specified at closing time.
320
        NewDeliveryScript func() (lnwire.DeliveryAddress, error)
321

322
        // FeeEstimator is the fee estimator we'll use to determine the fee in
323
        // satoshis we'll pay given a local and/or remote output.
324
        FeeEstimator CoopFeeEstimator
325

326
        // ChanObserver is an interface used to observe state changes to the
327
        // channel. We'll use this to figure out when/if we can send certain
328
        // messages.
329
        ChanObserver ChanStateObserver
330

331
        // CloseSigner is the signer we'll use to sign the close transaction.
332
        // This is a part of the ChannelFlushed state, as the channel state
333
        // we'll be signing can only be determined once the channel has been
334
        // flushed.
335
        CloseSigner CloseSigner
336
}
337

338
// Name returns the name of the environment. This is used to uniquely identify
339
// the environment of related state machines. For this state machine, the name
340
// is based on the channel ID.
341
func (e *Environment) Name() string {
24✔
342
        return fmt.Sprintf("rbf_chan_closer(%v)", e.ChanPoint)
24✔
343
}
24✔
344

345
// CloseStateTransition is the StateTransition type specific to the coop close
346
// state machine.
347
//
348
//nolint:ll
349
type CloseStateTransition = protofsm.StateTransition[ProtocolEvent, *Environment]
350

351
// ProtocolState is our sum-type ish interface that represents the current
352
// protocol state.
353
type ProtocolState interface {
354
        // protocolStateSealed is a special method that is used to seal the
355
        // interface (only types in this package can implement it).
356
        protocolStateSealed()
357

358
        // IsTerminal returns true if the target state is a terminal state.
359
        IsTerminal() bool
360

361
        // ProcessEvent takes a protocol event, and implements a state
362
        // transition for the state.
363
        ProcessEvent(ProtocolEvent, *Environment) (*CloseStateTransition, error)
364

365
        // String returns the name of the state.
366
        String() string
367
}
368

369
// AsymmetricPeerState is an extension of the normal ProtocolState interface
370
// that gives a caller a hit on if the target state should process an incoming
371
// event or not.
372
type AsymmetricPeerState interface {
373
        ProtocolState
374

375
        // ShouldRouteTo returns true if the target state should process the
376
        // target event.
377
        ShouldRouteTo(ProtocolEvent) bool
378
}
379

380
// ProtocolStates is a special type constraint that enumerates all the possible
381
// protocol states.
382
type ProtocolStates interface {
383
        ChannelActive | ShutdownPending | ChannelFlushing | ClosingNegotiation |
384
                LocalCloseStart | LocalOfferSent | RemoteCloseStart |
385
                ClosePending | CloseFin
386
}
387

388
// ChannelActive is the base state for the channel closer state machine. In
389
// this state, we haven't begun the shutdown process yet, so the channel is
390
// still active. Receiving the ShutdownSent or ShutdownReceived events will
391
// transition us to the ChannelFushing state.
392
//
393
// When we transition to this state, we emit a DaemonEvent to send the shutdown
394
// message if we received one ourselves. Alternatively, we may send out a new
395
// shutdown if we're initiating it for the very first time.
396
//
397
// transition:
398
//   - fromState: None
399
//   - toState: ChannelFlushing
400
//
401
// input events:
402
//   - SendShutdown
403
//   - ShutdownReceived
404
type ChannelActive struct {
405
}
406

407
// String returns the name of the state for ChannelActive.
NEW
408
func (c *ChannelActive) String() string {
×
NEW
409
        return "ChannelActive"
×
NEW
410
}
×
411

412
// IsTerminal returns true if the target state is a terminal state.
413
func (c *ChannelActive) IsTerminal() bool {
×
414
        return false
×
415
}
×
416

417
// protocolSealed indicates that this struct is a ProtocolEvent instance.
418
func (c *ChannelActive) protocolStateSealed() {}
×
419

420
// ShutdownScripts is a set of scripts that we'll use to co-op close the
421
// channel.
422
type ShutdownScripts struct {
423
        // LocalDeliveryScript is the script that we'll send our settled
424
        // channel funds to.
425
        LocalDeliveryScript lnwire.DeliveryAddress
426

427
        // RemoteDeliveryScript is the script that we'll send the remote
428
        // party's settled channel funds to.
429
        RemoteDeliveryScript lnwire.DeliveryAddress
430
}
431

432
// ShutdownPending is the state we enter into after we've sent or received the
433
// shutdown message. If we sent the shutdown, then we'll wait for the remote
434
// party to send a shutdown. Otherwise, if we received it, then we'll send our
435
// shutdown then go to the next state.
436
//
437
// transition:
438
//   - fromState: ChannelActive
439
//   - toState: ChannelFlushing
440
//
441
// input events:
442
//   - SendShutdown
443
//   - ShutdownReceived
444
type ShutdownPending struct {
445
        // ShutdownScripts store the set of scripts we'll use to initiate a coop
446
        // close.
447
        ShutdownScripts
448

449
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
450
        // attempt.
451
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
452
}
453

454
// String returns the name of the state for ShutdownPending.
NEW
455
func (s *ShutdownPending) String() string {
×
NEW
456
        return "ShutdownPending"
×
NEW
457
}
×
458

459
// IsTerminal returns true if the target state is a terminal state.
460
func (s *ShutdownPending) IsTerminal() bool {
×
461
        return false
×
462
}
×
463

464
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
465
func (s *ShutdownPending) protocolStateSealed() {}
×
466

467
// ChannelFlushing is the state we enter into after we've received or sent a
468
// shutdown message. In this state, we wait the ChannelFlushed event, after
469
// which we'll transition to the CloseReady state.
470
//
471
// transition:
472
//   - fromState: ShutdownPending
473
//   - toState: ClosingNegotiation
474
//
475
// input events:
476
//   - ShutdownComplete
477
//   - ShutdownReceived
478
type ChannelFlushing struct {
479
        // EarlyRemoteOffer is the offer we received from the remote party
480
        // before we obtained the local channel flushed event. We'll stash this
481
        // to process later.
482
        EarlyRemoteOffer fn.Option[OfferReceivedEvent]
483

484
        // ShutdownScripts store the set of scripts we'll use to initiate a coop
485
        // close.
486
        ShutdownScripts
487

488
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
489
        // transaction. Once the channel has been flushed, we'll use this as
490
        // our target fee rate.
491
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
492
}
493

494
// String returns the name of the state for ChannelFlushing.
NEW
495
func (c *ChannelFlushing) String() string {
×
NEW
496
        return "ChannelFlushing"
×
NEW
497
}
×
498

499
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
500
func (c *ChannelFlushing) protocolStateSealed() {}
×
501

502
// IsTerminal returns true if the target state is a terminal state.
503
func (c *ChannelFlushing) IsTerminal() bool {
×
504
        return false
×
505
}
×
506

507
// ClosingNegotiation is the state we transition to once the channel has been
508
// flushed. This is actually a composite state that contains one for each side
509
// of the channel, as the closing process is asymmetric. Once either of the
510
// peer states reaches the CloseFin state, then the channel is fully closed,
511
// and we'll transition to that terminal state.
512
//
513
// transition:
514
//   - fromState: ChannelFlushing
515
//   - toState: CloseFin
516
//
517
// input events:
518
//   - ChannelFlushed
519
type ClosingNegotiation struct {
520
        // PeerStates is a composite state that contains the state for both the
521
        // local and remote parties. Our usage of Dual makes this a special
522
        // state that allows us to treat two states as a single state. We'll use
523
        // the ShouldRouteTo method to determine which state route incoming
524
        // events to.
525
        PeerState lntypes.Dual[AsymmetricPeerState]
526
}
527

528
// String returns the name of the state for ClosingNegotiation.
NEW
529
func (c *ClosingNegotiation) String() string {
×
NEW
530
        localState := c.PeerState.GetForParty(lntypes.Local)
×
NEW
531
        remoteState := c.PeerState.GetForParty(lntypes.Remote)
×
NEW
532

×
NEW
533
        return fmt.Sprintf("ClosingNegotiation(local=%v, remote=%v)",
×
NEW
534
                localState, remoteState)
×
NEW
535
}
×
536

537
// IsTerminal returns true if the target state is a terminal state.
538
func (c *ClosingNegotiation) IsTerminal() bool {
×
539
        return false
×
540
}
×
541

542
// protocolSealed indicates that this struct is a ProtocolEvent instance.
543
func (c *ClosingNegotiation) protocolStateSealed() {}
×
544

545
// CloseChannelTerms is a set of terms that we'll use to close the channel. This
546
// includes the balances of the channel, and the scripts we'll use to send each
547
// party's funds to.
548
type CloseChannelTerms struct {
549
        ShutdownScripts
550

551
        ShutdownBalances
552
}
553

554
// DeriveCloseTxOuts takes the close terms, and returns the local and remote tx
555
// out for the close transaction. If an output is dust, then it'll be nil.
556
//
557
// TODO(roasbeef): add func for w/e heuristic to not manifest own output?
558
func (c *CloseChannelTerms) DeriveCloseTxOuts() (*wire.TxOut, *wire.TxOut) {
14✔
559
        //nolint:ll
14✔
560
        deriveTxOut := func(balance btcutil.Amount, pkScript []byte) *wire.TxOut {
42✔
561
                dustLimit := lnwallet.DustLimitForSize(len(pkScript))
28✔
562
                if balance >= dustLimit {
55✔
563
                        return &wire.TxOut{
27✔
564
                                PkScript: pkScript,
27✔
565
                                Value:    int64(balance),
27✔
566
                        }
27✔
567
                }
27✔
568

569
                return nil
1✔
570
        }
571

572
        localTxOut := deriveTxOut(
14✔
573
                c.LocalBalance.ToSatoshis(),
14✔
574
                c.LocalDeliveryScript,
14✔
575
        )
14✔
576
        remoteTxOut := deriveTxOut(
14✔
577
                c.RemoteBalance.ToSatoshis(),
14✔
578
                c.RemoteDeliveryScript,
14✔
579
        )
14✔
580

14✔
581
        return localTxOut, remoteTxOut
14✔
582
}
583

584
// RemoteAmtIsDust returns true if the remote output is dust.
585
func (c *CloseChannelTerms) RemoteAmtIsDust() bool {
×
586
        return c.RemoteBalance.ToSatoshis() < lnwallet.DustLimitForSize(
×
587
                len(c.RemoteDeliveryScript),
×
588
        )
×
589
}
×
590

591
// LocalAmtIsDust returns true if the local output is dust.
592
func (c *CloseChannelTerms) LocalAmtIsDust() bool {
4✔
593
        return c.LocalBalance.ToSatoshis() < lnwallet.DustLimitForSize(
4✔
594
                len(c.LocalDeliveryScript),
4✔
595
        )
4✔
596
}
4✔
597

598
// LocalCanPayFees returns true if the local party can pay the absolute fee
599
// from their local settled balance.
600
func (c *CloseChannelTerms) LocalCanPayFees(absoluteFee btcutil.Amount) bool {
6✔
601
        return c.LocalBalance.ToSatoshis() >= absoluteFee
6✔
602
}
6✔
603

604
// RemoteCanPayFees returns true if the remote party can pay the absolute fee
605
// from their remote settled balance.
606
func (c *CloseChannelTerms) RemoteCanPayFees(absoluteFee btcutil.Amount) bool {
5✔
607
        return c.RemoteBalance.ToSatoshis() >= absoluteFee
5✔
608
}
5✔
609

610
// LocalCloseStart is the state we enter into after we've received or sent
611
// shutdown, and the channel has been flushed. In this state, we'll emit a new
612
// event to send our offer to drive the rest of the process.
613
//
614
// transition:
615
//   - fromState: ChannelFlushing
616
//   - toState: LocalOfferSent
617
//
618
// input events:
619
//   - SendOfferEvent
620
type LocalCloseStart struct {
621
        CloseChannelTerms
622
}
623

624
// String returns the name of the state for LocalCloseStart, including proposed
625
// fee details.
NEW
626
func (l *LocalCloseStart) String() string {
×
NEW
627
        return "LocalCloseStart"
×
NEW
628
}
×
629

630
// ShouldRouteTo returns true if the target state should process the target
631
// event.
632
func (l *LocalCloseStart) ShouldRouteTo(event ProtocolEvent) bool {
13✔
633
        switch event.(type) {
13✔
634
        case *SendOfferEvent:
8✔
635
                return true
8✔
636
        default:
5✔
637
                return false
5✔
638
        }
639
}
640

641
// IsTerminal returns true if the target state is a terminal state.
642
func (l *LocalCloseStart) IsTerminal() bool {
×
643
        return false
×
644
}
×
645

646
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
647
func (l *LocalCloseStart) protocolStateSealed() {}
×
648

649
// LocalOfferSent is the state we transition to after we reveiver the
650
// SendOfferEvent in the LocalCloseStart state. With this state we send our
651
// offer to the remote party, then await a sig from them which concludes the
652
// local cooperative close process.
653
//
654
// transition:
655
//   - fromState: LocalCloseStart
656
//   - toState: ClosePending
657
//
658
// input events:
659
//   - LocalSigReceived
660
type LocalOfferSent struct {
661
        CloseChannelTerms
662

663
        // ProposedFee is the fee we proposed to the remote party.
664
        ProposedFee btcutil.Amount
665

666
        // LocalSig is the signature we sent to the remote party.
667
        LocalSig lnwire.Sig
668
}
669

670
// String returns the name of the state for LocalOfferSent, including proposed.
NEW
671
func (l *LocalOfferSent) String() string {
×
NEW
672
        return fmt.Sprintf("LocalOfferSent(proposed_fee=%v)", l.ProposedFee)
×
NEW
673
}
×
674

675
// ShouldRouteTo returns true if the target state should process the target
676
// event.
677
func (l *LocalOfferSent) ShouldRouteTo(event ProtocolEvent) bool {
4✔
678
        switch event.(type) {
4✔
679
        case *LocalSigReceived:
4✔
680
                return true
4✔
681
        default:
×
682
                return false
×
683
        }
684
}
685

686
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
687
func (l *LocalOfferSent) protocolStateSealed() {}
×
688

689
// IsTerminal returns true if the target state is a terminal state.
690
func (l *LocalOfferSent) IsTerminal() bool {
×
691
        return false
×
692
}
×
693

694
// ClosePending is the state we enter after concluding the negotiation for the
695
// remote or local state. At this point, given a confirmation notification we
696
// can terminate the process. Otherwise, we can receive a fresh CoopCloseReq to
697
// go back to the very start.
698
//
699
// transition:
700
//   - fromState: LocalOfferSent || RemoteCloseStart
701
//   - toState: CloseFin
702
//
703
// input events:
704
//   - LocalSigReceived
705
//   - OfferReceivedEvent
706
type ClosePending struct {
707
        // CloseTx is the pending close transaction.
708
        CloseTx *wire.MsgTx
709
}
710

711
// String returns the name of the state for ClosePending.
NEW
712
func (c *ClosePending) String() string {
×
NEW
713
        return fmt.Sprintf("ClosePending(txid=%v)", c.CloseTx.TxHash())
×
NEW
714
}
×
715

716
// ShouldRouteTo returns true if the target state should process the target
717
// event.
718
func (c *ClosePending) ShouldRouteTo(event ProtocolEvent) bool {
×
719
        switch event.(type) {
×
720
        case *SpendEvent:
×
721
                return true
×
722
        default:
×
723
                return false
×
724
        }
725
}
726

727
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
728
func (c *ClosePending) protocolStateSealed() {}
×
729

730
// IsTerminal returns true if the target state is a terminal state.
731
func (c *ClosePending) IsTerminal() bool {
×
732
        return true
×
733
}
×
734

735
// CloseFin is the terminal state for the channel closer state machine. At this
736
// point, the close tx has been confirmed on chain.
737
type CloseFin struct {
738
        // ConfirmedTx is the transaction that confirmed the channel close.
739
        ConfirmedTx *wire.MsgTx
740
}
741

742
// String returns the name of the state for CloseFin.
NEW
743
func (c *CloseFin) String() string {
×
NEW
744
        return "CloseFin"
×
NEW
745
}
×
746

747
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
748
func (c *CloseFin) protocolStateSealed() {}
×
749

750
// IsTerminal returns true if the target state is a terminal state.
751
func (c *CloseFin) IsTerminal() bool {
×
752
        return true
×
753
}
×
754

755
// RemoteCloseStart is similar to the LocalCloseStart, but is used to drive the
756
// process of signing an offer for the remote party
757
//
758
// transition:
759
//   - fromState: ChannelFlushing
760
//   - toState: ClosePending
761
type RemoteCloseStart struct {
762
        CloseChannelTerms
763
}
764

765
// String returns the name of the state for RemoteCloseStart.
NEW
766
func (r *RemoteCloseStart) String() string {
×
NEW
767
        return "RemoteCloseStart"
×
NEW
768
}
×
769

770
// ShouldRouteTo returns true if the target state should process the target
771
// event.
772
func (l *RemoteCloseStart) ShouldRouteTo(event ProtocolEvent) bool {
5✔
773
        switch event.(type) {
5✔
774
        case *OfferReceivedEvent:
5✔
775
                return true
5✔
776
        default:
×
777
                return false
×
778
        }
779
}
780

781
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
782
func (l *RemoteCloseStart) protocolStateSealed() {}
×
783

784
// IsTerminal returns true if the target state is a terminal state.
785
func (l *RemoteCloseStart) IsTerminal() bool {
×
786
        return false
×
787
}
×
788

789
// RbfChanCloser is a state machine that handles the RBF-enabled cooperative
790
// channel close protocol.
791
type RbfChanCloser = protofsm.StateMachine[ProtocolEvent, *Environment]
792

793
// RbfChanCloserCfg is a configuration struct that is used to initialize a new
794
// RBF chan closer state machine.
795
type RbfChanCloserCfg = protofsm.StateMachineCfg[ProtocolEvent, *Environment]
796

797
// RbfSpendMapper is a type used to map the generic spend event to one specific
798
// to this package.
799
type RbfSpendMapper = protofsm.SpendMapper[ProtocolEvent]
800

801
func SpendMapper(spendEvent *chainntnfs.SpendDetail) ProtocolEvent {
×
802
        return &SpendEvent{
×
803
                Tx:          spendEvent.SpendingTx,
×
804
                BlockHeight: uint32(spendEvent.SpendingHeight),
×
805
        }
×
806
}
×
807

808
// RbfMsgMapperT is a type used to map incoming wire messages to protocol
809
// events.
810
type RbfMsgMapperT = protofsm.MsgMapper[ProtocolEvent]
811

812
// RbfState is a type alias for the state of the RBF channel closer.
813
type RbfState = protofsm.State[ProtocolEvent, *Environment]
814

815
// RbfEvent is a type alias for the event type of the RBF channel closer.
816
type RbfEvent = protofsm.EmittedEvent[ProtocolEvent]
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