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

lightningnetwork / lnd / 11965896165

22 Nov 2024 03:26AM UTC coverage: 49.579% (-9.4%) from 58.98%
11965896165

Pull #8512

github

Roasbeef
lnwallet/chancloser: add unit tests for new rbf coop close
Pull Request #8512: [3/4] - lnwallet/chancloser: add new protofsm based RBF chan closer

30 of 1081 new or added lines in 8 files covered. (2.78%)

25859 existing lines in 424 files now uncovered.

99910 of 201515 relevant lines covered (49.58%)

2.06 hits per line

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

0.0
/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/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/wire"
11
        "github.com/lightningnetwork/lnd/chainntnfs"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/fn"
14
        "github.com/lightningnetwork/lnd/input"
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
        // ErrUnknownFinalBalance is returned if we're unable to determine the
31
        // final channel balance after a flush.
32
        ErrUnknownFinalBalance = fmt.Errorf("unknown final balance")
33

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

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

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

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

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

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

68
// SpendEvent indicates that a transaction spending the funding outpoint has
69
// been confirmed in the main chain.
70
//
71
// TODO(roasbeef): need a mapper from generic event to this one, then can
72
// populate fields
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.
NEW
83
func (s *SpendEvent) protocolSealed() {}
×
84

85
// SendShutdown indicates that the user wishes to co-op close the channel, so
86
// we should send a new shutdown message to the remote party.  From the
87
// ChannelActive state, this transitions us to the ChannelFlushing state.
88
type SendShutdown struct {
89
        fromState *ChannelActive
90
        toState   *ChannelFlushing
91

92
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
93
        // attempt.
94
        IdealFeeRate chainfee.SatPerVByte
95

96
        // DeliveryAddr is the address we'd like to receive the funds to. If
97
        // None, then a new addr will be generated.
98
        DeliveryAddr fn.Option[lnwire.DeliveryAddress]
99
}
100

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

104
// ShutdownReceived indicates that we received a shutdown event so we need to
105
// enter the flushing state.  From the ChannelActive state, this transitions us
106
// to the ChannelFlushing state.
107
type ShutdownReceived struct {
108
        // BlockHeight is the height at which the shutdown message was
109
        // received. This is used for channel leases to determine if a co-op
110
        // close can occur.
111
        BlockHeight uint32
112

113
        // ShutdownScript is the script the remote party wants to use to
114
        // shutdown.
115
        ShutdownScript lnwire.DeliveryAddress
116

117
        fromState *ChannelActive
118
        toState   *ChannelFlushing
119
}
120

121
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
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
type ShutdownComplete struct {
128
        fromState *ShutdownPending
129
        toState   *ChannelFlushing
130
}
131

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

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

141
        // RemoteBalance is the remote balance of the channel.
142
        RemoteBalance lnwire.MilliSatoshi
143
}
144

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

149
// ChannelFlushed is an event that indicates the channel has been fully flushed
150
// can we can now start closing negotiation.
151
type ChannelFlushed struct {
152
        fromState *ChannelFlushing
153
        toState   *ClosingNegotiation
154

155
        // FreshFlush indicates if this is the first time the channel has been
156
        // flushed, or if this is a flush as part of an RBF iteration.
157
        FreshFlush bool
158

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

165
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
166
func (c *ChannelFlushed) protocolSealed() {}
×
167

168
// SendOfferEvent is a self-triggered event that transitions us from the
169
// LocalCloseStart state to the LocalOfferSent state. This kicks off the new
170
// signing process for the co-op close process.
171
type SendOfferEvent struct {
172
        fromState *LocalCloseStart
173
        toState   *LocalOfferSent
174

175
        TargetFeeRate chainfee.SatPerVByte
176
}
177

178
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
179
func (s *SendOfferEvent) protocolSealed() {}
×
180

181
// LocalSigReceived is an event that indicates we've received a signature from
182
// the remote party, which signs our the co-op close transaction at our
183
// specified fee rate.
184
type LocalSigReceived struct {
185
        fromState *LocalOfferSent
186
        toState   *ClosePending
187

188
        // SigMsg is the sig message we received from the remote party.
189
        SigMsg lnwire.ClosingSig
190
}
191

192
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
193
func (s *LocalSigReceived) protocolSealed() {}
×
194

195
// OfferReceivedEvent is an event that indicates we've received an offer from
196
// the remote party. This applies to the RemoteCloseStart state.
197
type OfferReceivedEvent struct {
198
        // SigMsg is the signature message we received from the remote party.
199
        SigMsg lnwire.ClosingComplete
200

201
        fromState *RemoteCloseStart
202
        toState   *ClosePending
203
}
204

205
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
206
func (s *OfferReceivedEvent) protocolSealed() {}
×
207

208
// CloseSigner...
209
type CloseSigner interface {
210
        // CreateCloseProposal creates a new co-op close proposal in the form
211
        // of a valid signature, the chainhash of the final txid, and our final
212
        // balance in the created state.
213
        CreateCloseProposal(proposedFee btcutil.Amount,
214
                localDeliveryScript []byte, remoteDeliveryScript []byte,
215
                closeOpt ...lnwallet.ChanCloseOpt,
216
        ) (
217
                input.Signature, *chainhash.Hash, btcutil.Amount, error)
218

219
        // CompleteCooperativeClose persistently "completes" the cooperative
220
        // close by producing a fully signed co-op close transaction.
221
        CompleteCooperativeClose(localSig, remoteSig input.Signature,
222
                localDeliveryScript, remoteDeliveryScript []byte,
223
                proposedFee btcutil.Amount, closeOpt ...lnwallet.ChanCloseOpt,
224
        ) (*wire.MsgTx, btcutil.Amount, error)
225
}
226

227
// ChanStateObserver is an interface used to observe state changes that occur
228
// in a channel. This can be used to figure out if we're able to send a
229
// shutdown message or not.
230
type ChanStateObserver interface {
231
        // NoDanglingUpdates returns true if there are no dangling updates in
232
        // the channel. In other words, there are no active update messages
233
        // that haven't already been covered by a commit sig.
234
        NoDanglingUpdates() bool
235

236
        // DisableIncomingAdds instructs the channel link to disable process new
237
        // incoming add messages.
238
        DisableIncomingAdds() error
239

240
        // DisableOutgoingAdds instructs the channel link to disable process
241
        // new outgoing add messages.
242
        DisableOutgoingAdds() error
243

244
        // DisableChannel attempts to disable a channel (marking it ineligible
245
        // to foward), and also sends out a network udpate to disable the
246
        // channel.
247
        DisableChannel() error
248

249
        // MarkCoopBroadcasted persistently marks that the channel close
250
        // transaction has been broadcast.
251
        MarkCoopBroadcasted(*wire.MsgTx, bool) error
252

253
        // MarkShutdownSent persists the given ShutdownInfo. The existence of
254
        // the ShutdownInfo represents the fact that the Shutdown message has
255
        // been sent by us and so should be re-sent on re-establish.
256
        MarkShutdownSent(deliveryAddr []byte, isInitiator bool) error
257

258
        // FinalBalances is the balances of the channel once it has been
259
        // flushed. If Some, then this indicates that the channel is now in a
260
        // state where it's always flushed, so we can accelerate the state
261
        // transitions.
262
        FinalBalances() fn.Option[ShutdownBalances]
263
}
264

265
// Environment is a set of dependencies that a state machine may need to carry
266
// out the logic for a given state transition. All fields are to be considered
267
// immutable, and will be fixed for the lifetime of the state machine.
268
//
269
// TODO(roasbef): also permit env update as well?
270
//   - allow latest fee update here?
271
type Environment struct {
272
        // ChainParams is the chain parameters for the channel.
273
        ChainParams chaincfg.Params
274

275
        // ChanPeer is the peer we're attempting to close the channel with.
276
        ChanPeer btcec.PublicKey
277

278
        // ChanPoint is the channel point of the active channel.
279
        ChanPoint wire.OutPoint
280

281
        // ChanID is the channel ID of the channel we're attempting to close.
282
        ChanID lnwire.ChannelID
283

284
        // ShortChanID is the short channel ID of the channel we're attempting
285
        // to close.
286
        Scid lnwire.ShortChannelID
287

288
        // ChanType is the type of channel we're attempting to close.
289
        ChanType channeldb.ChannelType
290

291
        // DefaultFeeRate is the fee we'll use for the closing transaction if
292
        // the user didn't specify an ideal fee rate. This may happen if the
293
        // remote party is the one that initiates the co-op close.
294
        DefaultFeeRate chainfee.SatPerVByte
295

296
        // ThawHeight is the height at which the channel will be thawed. If
297
        // this is None, then co-op close can occur at any moment.
298
        ThawHeight fn.Option[uint32]
299

300
        // RemoteUprontShutdown is the upfront shutdown addr of the remote party.
301
        // We'll use this to validate if the remote peer is authorized to close
302
        // the channel with the sent addr or not.
303
        RemoteUpfrontShutdown fn.Option[lnwire.DeliveryAddress]
304

305
        // LocalUprontShutdown is our upfront shutdown address. If Some, then
306
        // we'll default to using this.
307
        LocalUpfrontShutdown fn.Option[lnwire.DeliveryAddress]
308

309
        // NewDeliveryScript is a function that returns a new delivery script.
310
        // This is used if we don't have an upfront shutdown addr, and no addr
311
        // was specified at closing time.
312
        NewDeliveryScript func() (lnwire.DeliveryAddress, error)
313

314
        // FeeEstimator is the fee estimator we'll use to determine the fee in
315
        // satoshis we'll pay given a local and/or remote output.
316
        FeeEstimator CoopFeeEstimator
317

318
        // ChanObserver is an interface used to observe state changes to the
319
        // channel. We'll use this to figure out when/if we can send certain
320
        // messages.
321
        ChanObserver ChanStateObserver
322

323
        // CloseSigner is the signer we'll use to sign the close transaction.
324
        // This is a part of the ChannelFlushed state, as the channel state
325
        // we'll be signing can only be determined once the channel has been
326
        // flushed.
327
        CloseSigner CloseSigner
328
}
329

330
// CleanUp is a method that is called once the state machine exits.
NEW
331
func (e *Environment) CleanUp() error {
×
NEW
332
        // TODO(roasbeef): actually clean something up?
×
NEW
333
        return nil
×
NEW
334
}
×
335

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

343
// CloseStateTransition...
344
type CloseStateTransition = protofsm.StateTransition[ProtocolEvent, *Environment]
345

346
// ProtocolState is our sum-type ish interface that represents the current
347
// protocol state.
348
type ProtocolState interface {
349
        // protocolStateSealed is a special method that is used to seal the
350
        // interface (only types in this pacakge can implement it).
351
        protocolStateSealed()
352

353
        // IsTerminal returns true if the target state is a terminal state.
354
        IsTerminal() bool
355

356
        // ProcessEvent takes a protocol event, and implements a state
357
        // transition for the state.
358
        ProcessEvent(ProtocolEvent, *Environment) (*CloseStateTransition, error)
359
}
360

361
// AsymmetricPeerState is an extension of the normal ProtocolState interface
362
// that gives a caller a hit on if the target state should process an incoming
363
// event or not.
364
type AsymmetricPeerState interface {
365
        ProtocolState
366

367
        // ShouldRouteTo returns true if the target state should process the
368
        // target event.
369
        ShouldRouteTo(ProtocolEvent) bool
370
}
371

372
// ProtocolStates is a special type constraint that enumerates all the possible
373
// protocol states.
374
type ProtocolStates interface {
375
        ChannelActive | ShutdownPending | ChannelFlushing | ClosingNegotiation |
376
                LocalCloseStart | LocalOfferSent | RemoteCloseStart |
377
                ClosePending | CloseFin
378
}
379

380
// ChannelActive is the base state for the channel closer state machine. In
381
// this state, we haven't begun the shutdown process yet, so the channel is
382
// still active. Receiving the ShutdownSent or ShutdownReceived events will
383
// transition us to the ChannelFushing state.
384
//
385
// When we transition to this state, we emit a DaemonEvent to send the shutdown
386
// message if we received one ourselves. Alternatively, we may send out a new
387
// shutdown if we're initiating it for the very first time.
388
type ChannelActive struct {
389
        nextState *ShutdownPending
390

391
        outputDaemonEvents fn.Option[protofsm.SendMsgEvent[ProtocolEvent]]
392
}
393

394
// IsTerminal returns true if the target state is a terminal state.
NEW
395
func (c *ChannelActive) IsTerminal() bool {
×
NEW
396
        return false
×
NEW
397
}
×
398

399
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
400
func (c *ChannelActive) protocolStateSealed() {}
×
401

402
// ShutdownScripts is a set of scripts that we'll use to co-op close the
403
// channel.
404
type ShutdownScripts struct {
405
        // LocalDeliveryScript is the script that we'll send our settled
406
        // channel funds to.
407
        LocalDeliveryScript lnwire.DeliveryAddress
408

409
        // RemoteDeliveryScript is the script that we'll send the remote
410
        // party's settled channel funds to.
411
        RemoteDeliveryScript lnwire.DeliveryAddress
412
}
413

414
// ShutdownPending is the state we enter into after we've sent or received the
415
// shutdown message. If we sent the shutdown, then we'll wait for the remote
416
// party to send a shutdown. Otherwise, if we received it, then we'll send our
417
// shutdown then go to the next state.
418
type ShutdownPending struct {
419
        // TODO(roasbeef): remove these? just put as comments?
420
        prevState *ChannelActive
421

422
        nextState *ChannelFlushing
423

424
        inputEvents fn.Either[SendShutdown, ShutdownReceived]
425

426
        ShutdownScripts
427

428
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
429
        // attempt.
430
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
431
}
432

433
// IsTerminal returns true if the target state is a terminal state.
NEW
434
func (s *ShutdownPending) IsTerminal() bool {
×
NEW
435
        return false
×
NEW
436
}
×
437

438
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
NEW
439
func (s *ShutdownPending) protocolStateSealed() {}
×
440

441
// ChannelFlushing is the state we enter into after we've received or sent a
442
// shutdown message. In this state, we wait the ChannelFlushed event, after
443
// which we'll transition to the CloseReady state.
444
type ChannelFlushing struct {
445
        inputEvents fn.Either[ShutdownComplete, ShutdownReceived]
446

447
        prevState *ShutdownPending
448

449
        nextState *ClosingNegotiation
450

451
        // EarlyRemoteOffer is the offer we received from the remote party
452
        // before we obtained the local channel flushed event. We'll stash this
453
        // to process later.
454
        EarlyRemoteOffer fn.Option[OfferReceivedEvent]
455

456
        ShutdownScripts
457

458
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
459
        // transaction. Once the channel has been flushed, we'll use this as
460
        // our target fee rate.
461
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
462
}
463

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

467
// IsTerminal returns true if the target state is a terminal state.
NEW
468
func (c *ChannelFlushing) IsTerminal() bool {
×
NEW
469
        return false
×
NEW
470
}
×
471

472
// ClosingNegotiation is the state we transition to once the channel has been
473
// flushed. This is actually a composite state that contains one for each side
474
// of the channel, as the closing process is asymmetric. Once either of the
475
// peer states reaches the CloseFin state, then the channel is fully closed,
476
// and we'll transition to that terminal state.
477
type ClosingNegotiation struct {
478
        prevState *ChannelFlushing
479

480
        // PeerStates is a composite state that contains the state for both the
481
        // local and remote parties.
482
        PeerState DualPeerState
483

484
        nextState *CloseFin
485
}
486

487
// IsTerminal returns true if the target state is a terminal state.
NEW
488
func (c *ClosingNegotiation) IsTerminal() bool {
×
NEW
489
        return false
×
NEW
490
}
×
491

492
// protocolSealed indicates that this struct is a ProtocolEvent instance.
NEW
493
func (c *ClosingNegotiation) protocolStateSealed() {}
×
494

495
// CloseChannelTerms...
496
type CloseChannelTerms struct {
497
        ShutdownBalances
498

499
        ShutdownScripts
500
}
501

502
// DeriveCloseTxOuts takes the close terms, and returns the local and remote tx
503
// out for the close transaction. If an output is dust, then it'll be nil.
504
//
505
// TODO(roasbeef): add func for w/e heuristic to not manifest own output?
NEW
506
func (c *CloseChannelTerms) DeriveCloseTxOuts() (*wire.TxOut, *wire.TxOut) {
×
NEW
507
        deriveTxOut := func(balance btcutil.Amount, pkScript []byte) *wire.TxOut {
×
NEW
508
                dustLimit := lnwallet.DustLimitForSize(len(pkScript))
×
NEW
509
                if balance > dustLimit {
×
NEW
510
                        return &wire.TxOut{
×
NEW
511
                                PkScript: pkScript,
×
NEW
512
                                Value:    int64(balance),
×
NEW
513
                        }
×
NEW
514
                }
×
515

NEW
516
                return nil
×
517
        }
518

NEW
519
        localTxOut := deriveTxOut(
×
NEW
520
                c.LocalBalance.ToSatoshis(),
×
NEW
521
                c.LocalDeliveryScript,
×
NEW
522
        )
×
NEW
523
        remoteTxOut := deriveTxOut(
×
NEW
524
                c.RemoteBalance.ToSatoshis(),
×
NEW
525
                c.RemoteDeliveryScript,
×
NEW
526
        )
×
NEW
527

×
NEW
528
        return localTxOut, remoteTxOut
×
529
}
530

531
// RemoteAmtIsDust returns true if the remote output is dust.
NEW
532
func (c *CloseChannelTerms) RemoteAmtIsDust() bool {
×
NEW
533
        return c.RemoteBalance.ToSatoshis() < lnwallet.DustLimitForSize(
×
NEW
534
                len(c.RemoteDeliveryScript),
×
NEW
535
        )
×
NEW
536
}
×
537

538
// LocalAmtIsDust returns true if the local output is dust.
NEW
539
func (c *CloseChannelTerms) LocalAmtIsDust() bool {
×
NEW
540
        return c.LocalBalance.ToSatoshis() < lnwallet.DustLimitForSize(
×
NEW
541
                len(c.LocalDeliveryScript),
×
NEW
542
        )
×
NEW
543
}
×
544

545
// LocalCanPayFees returns true if the local party can pay the absolute fee
546
// from their local settled balance.
NEW
547
func (c *CloseChannelTerms) LocalCanPayFees(absoluteFee btcutil.Amount) bool {
×
NEW
548
        return c.LocalBalance.ToSatoshis() >= absoluteFee
×
NEW
549
}
×
550

551
// RemoteCanPayFees returns true if the remote party can pay the absolute fee
552
// from their remote settled balance.
NEW
553
func (c *CloseChannelTerms) RemoteCanPayFees(absoluteFee btcutil.Amount) bool {
×
NEW
554
        return c.RemoteBalance.ToSatoshis() >= absoluteFee
×
NEW
555
}
×
556

557
// LocalCloseStart is the state we enter into after we've received or sent
558
// shutdown, and the channel has been flushed. In this state, we'll emit a new
559
// event to send our offer to drive the rest of the process.
560
type LocalCloseStart struct {
561
        targetFeeRate chainfee.SatPerVByte
562

563
        outputEvent *SendOfferEvent
564

565
        CloseChannelTerms
566
}
567

568
// ShouldRouteTo returns true if the target state should process the target
569
// event.
NEW
570
func (l *LocalCloseStart) ShouldRouteTo(event ProtocolEvent) bool {
×
NEW
571
        switch event.(type) {
×
NEW
572
        case *SendOfferEvent:
×
NEW
573
                return true
×
NEW
574
        default:
×
NEW
575
                return false
×
576
        }
577
}
578

579
// IsTerminal returns true if the target state is a terminal state.
NEW
580
func (l *LocalCloseStart) IsTerminal() bool {
×
NEW
581
        return false
×
NEW
582
}
×
583

584
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
NEW
585
func (l *LocalCloseStart) protocolStateSealed() {}
×
586

587
// A compile-time assertion to ensure LocalCloseStart satisfies the
588
// AsymmetricPeerState interface.
589
var _ AsymmetricPeerState = (*LocalCloseStart)(nil)
590

591
// LocalOfferSent is the state we transition to after we reveiver the
592
// SendOfferEvent in the LocalCloseStart state. With this state we send our
593
// offer to the remote party, then await a sig from them which concludes the
594
// local cooperative close process.
595
type LocalOfferSent struct {
596
        prevState *LocalCloseStart
597

598
        transitionEvent *SendOfferEvent
599

600
        nextState ClosePending
601

602
        outputDaemonEvents protofsm.SendMsgEvent[ProtocolEvent]
603

604
        // ProposedFee is the fee we proposed to the remote party.
605
        ProposedFee btcutil.Amount
606

607
        // LocalSig is the signature we sent to the remote party.
608
        LocalSig lnwire.Sig
609

610
        CloseChannelTerms
611
}
612

613
// ShouldRouteTo returns true if the target state should process the target
614
// event.
NEW
615
func (l *LocalOfferSent) ShouldRouteTo(event ProtocolEvent) bool {
×
NEW
616
        switch event.(type) {
×
NEW
617
        case *LocalSigReceived:
×
NEW
618
                return true
×
NEW
619
        default:
×
NEW
620
                return false
×
621
        }
622
}
623

624
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
NEW
625
func (l *LocalOfferSent) protocolStateSealed() {}
×
626

627
// IsTerminal returns true if the target state is a terminal state.
NEW
628
func (l *LocalOfferSent) IsTerminal() bool {
×
NEW
629
        return false
×
NEW
630
}
×
631

632
// A compile-time assertion to ensure LocalOfferSent satisfies the
633
// AsymmetricPeerState interface.
634
var _ AsymmetricPeerState = (*LocalOfferSent)(nil)
635

636
// ClosePending is the state we enter after concluding the negotiation for the
637
// remote or local state. At this point, given a confirmation notification we
638
// can terminate the process. Otherwise, we can receive a fresh CoopCloseReq to
639
// go back to the very start.
640
type ClosePending struct {
641
        transitionEvents fn.Either[LocalSigReceived, OfferReceivedEvent]
642

643
        nextState fn.Either[CloseFin, ChannelFlushing]
644

645
        // CloseTx is the pending close transaction.
646
        CloseTx *wire.MsgTx
647

648
        outputDaemonEvents fn.Option[protofsm.BroadcastTxn]
649
}
650

651
// ShouldRouteTo returns true if the target state should process the target
652
// event.
NEW
653
func (c *ClosePending) ShouldRouteTo(event ProtocolEvent) bool {
×
NEW
654
        switch event.(type) {
×
NEW
655
        case *SpendEvent:
×
NEW
656
                return true
×
NEW
657
        default:
×
NEW
658
                return false
×
659
        }
660
}
661

662
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
NEW
663
func (c *ClosePending) protocolStateSealed() {}
×
664

665
// IsTerminal returns true if the target state is a terminal state.
NEW
666
func (c *ClosePending) IsTerminal() bool {
×
NEW
667
        return true
×
NEW
668
}
×
669

670
// CloseFin is the terminal state for the channel closer state machine. At this
671
// point, the close tx has been confirmed on chain.
672
type CloseFin struct {
673
        transitionEvent *SpendEvent
674

675
        // ConfirmedTx is the transaction that confirmed the channel close.
676
        ConfirmedTx *wire.MsgTx
677
}
678

679
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
NEW
680
func (c *CloseFin) protocolStateSealed() {}
×
681

682
// IsTerminal returns true if the target state is a terminal state.
NEW
683
func (c *CloseFin) IsTerminal() bool {
×
NEW
684
        return true
×
NEW
685
}
×
686

687
// RemoteCloseStart is similar to the LocalCloseStart, but is used to drive the
688
// process of signing an offer for the remote party
689
type RemoteCloseStart struct {
690
        peerPub btcec.PublicKey
691

692
        nextState *ClosePending
693

694
        CloseChannelTerms
695
}
696

697
// ShouldRouteTo returns true if the target state should process the target
698
// event.
NEW
699
func (l *RemoteCloseStart) ShouldRouteTo(event ProtocolEvent) bool {
×
NEW
700
        switch event.(type) {
×
NEW
701
        case *OfferReceivedEvent:
×
NEW
702
                return true
×
NEW
703
        default:
×
NEW
704
                return false
×
705
        }
706
}
707

708
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
NEW
709
func (c *RemoteCloseStart) protocolStateSealed() {}
×
710

711
// IsTerminal returns true if the target state is a terminal state.
NEW
712
func (c *RemoteCloseStart) IsTerminal() bool {
×
NEW
713
        return false
×
NEW
714
}
×
715

716
// A compile-time assertion to ensure RemoteCloseStart satisfies the
717
// AsymmetricPeerState interface.
718
var _ AsymmetricPeerState = (*LocalOfferSent)(nil)
719

720
// DualPeerState is a special state that allows us to treat two states as a
721
// single state. We'll use the ShouldRouteTo method to determine which state
722
// route incoming events to.
723
type DualPeerState struct {
724
        // LocalState is the state for the local party.
725
        LocalState AsymmetricPeerState
726

727
        // RemoteState is the state for the remote party.
728
        RemoteState AsymmetricPeerState
729
}
730

731
// RbfChanCloser is a state machine that handles the RBF-enabled cooperative
732
// channel close protocol.
733
type RbfChanCloser = protofsm.StateMachine[ProtocolEvent, *Environment]
734

735
// RbfChanCloserCfg is a configuration struct that is used to initialize a new
736
// RBF chan closer state machine.
737
type RbfChanCloserCfg = protofsm.StateMachineCfg[ProtocolEvent, *Environment]
738

739
// RbfSpendMapper is a type used to map the generic spend event to one specific
740
// to this package.
741
type RbfSpendMapper = protofsm.SpendMapper[ProtocolEvent]
742

NEW
743
func SpendMapper(spendEvent *chainntnfs.SpendDetail) ProtocolEvent {
×
NEW
744
        return &SpendEvent{
×
NEW
745
                Tx:          spendEvent.SpendingTx,
×
NEW
746
                BlockHeight: uint32(spendEvent.SpendingHeight),
×
NEW
747
        }
×
NEW
748
}
×
749

750
// RbfMsgMapperT is a type used to map incoming wire messages to protocol
751
// events.
752
type RbfMsgMapperT = protofsm.MsgMapper[ProtocolEvent]
753

754
// RbfState is a type alias for the state of the RBF channel closer.
755
type RbfState = protofsm.State[ProtocolEvent, *Environment]
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