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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 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/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.
UNCOV
341
func (e *Environment) Name() string {
×
UNCOV
342
        return fmt.Sprintf("rbf_chan_closer(%v)", e.ChanPoint)
×
UNCOV
343
}
×
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

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

372
        // ShouldRouteTo returns true if the target state should process the
373
        // target event.
374
        ShouldRouteTo(ProtocolEvent) bool
375
}
376

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

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

404
// IsTerminal returns true if the target state is a terminal state.
405
func (c *ChannelActive) IsTerminal() bool {
×
406
        return false
×
407
}
×
408

409
// protocolSealed indicates that this struct is a ProtocolEvent instance.
410
func (c *ChannelActive) protocolStateSealed() {}
×
411

412
// ShutdownScripts is a set of scripts that we'll use to co-op close the
413
// channel.
414
type ShutdownScripts struct {
415
        // LocalDeliveryScript is the script that we'll send our settled
416
        // channel funds to.
417
        LocalDeliveryScript lnwire.DeliveryAddress
418

419
        // RemoteDeliveryScript is the script that we'll send the remote
420
        // party's settled channel funds to.
421
        RemoteDeliveryScript lnwire.DeliveryAddress
422
}
423

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

441
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
442
        // attempt.
443
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
444
}
445

446
// IsTerminal returns true if the target state is a terminal state.
447
func (s *ShutdownPending) IsTerminal() bool {
×
448
        return false
×
449
}
×
450

451
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
452
func (s *ShutdownPending) protocolStateSealed() {}
×
453

454
// ChannelFlushing is the state we enter into after we've received or sent a
455
// shutdown message. In this state, we wait the ChannelFlushed event, after
456
// which we'll transition to the CloseReady state.
457
//
458
// transition:
459
//   - fromState: ShutdownPending
460
//   - toState: ClosingNegotiation
461
//
462
// input events:
463
//   - ShutdownComplete
464
//   - ShutdownReceived
465
type ChannelFlushing struct {
466
        // EarlyRemoteOffer is the offer we received from the remote party
467
        // before we obtained the local channel flushed event. We'll stash this
468
        // to process later.
469
        EarlyRemoteOffer fn.Option[OfferReceivedEvent]
470

471
        // ShutdownScripts store the set of scripts we'll use to initiate a coop
472
        // close.
473
        ShutdownScripts
474

475
        // IdealFeeRate is the ideal fee rate we'd like to use for the closing
476
        // transaction. Once the channel has been flushed, we'll use this as
477
        // our target fee rate.
478
        IdealFeeRate fn.Option[chainfee.SatPerVByte]
479
}
480

481
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
482
func (c *ChannelFlushing) protocolStateSealed() {}
×
483

484
// IsTerminal returns true if the target state is a terminal state.
485
func (c *ChannelFlushing) IsTerminal() bool {
×
486
        return false
×
487
}
×
488

489
// ClosingNegotiation is the state we transition to once the channel has been
490
// flushed. This is actually a composite state that contains one for each side
491
// of the channel, as the closing process is asymmetric. Once either of the
492
// peer states reaches the CloseFin state, then the channel is fully closed,
493
// and we'll transition to that terminal state.
494
//
495
// transition:
496
//   - fromState: ChannelFlushing
497
//   - toState: CloseFin
498
//
499
// input events:
500
//   - ChannelFlushed
501
type ClosingNegotiation struct {
502
        // PeerStates is a composite state that contains the state for both the
503
        // local and remote parties. Our usage of Dual makes this a special
504
        // state that allows us to treat two states as a single state. We'll use
505
        // the ShouldRouteTo method to determine which state route incoming
506
        // events to.
507
        PeerState lntypes.Dual[AsymmetricPeerState]
508
}
509

510
// IsTerminal returns true if the target state is a terminal state.
511
func (c *ClosingNegotiation) IsTerminal() bool {
×
512
        return false
×
513
}
×
514

515
// protocolSealed indicates that this struct is a ProtocolEvent instance.
516
func (c *ClosingNegotiation) protocolStateSealed() {}
×
517

518
// CloseChannelTerms is a set of terms that we'll use to close the channel. This
519
// includes the balances of the channel, and the scripts we'll use to send each
520
// party's funds to.
521
type CloseChannelTerms struct {
522
        ShutdownScripts
523

524
        ShutdownBalances
525
}
526

527
// DeriveCloseTxOuts takes the close terms, and returns the local and remote tx
528
// out for the close transaction. If an output is dust, then it'll be nil.
529
//
530
// TODO(roasbeef): add func for w/e heuristic to not manifest own output?
UNCOV
531
func (c *CloseChannelTerms) DeriveCloseTxOuts() (*wire.TxOut, *wire.TxOut) {
×
UNCOV
532
        //nolint:ll
×
UNCOV
533
        deriveTxOut := func(balance btcutil.Amount, pkScript []byte) *wire.TxOut {
×
UNCOV
534
                dustLimit := lnwallet.DustLimitForSize(len(pkScript))
×
UNCOV
535
                if balance >= dustLimit {
×
UNCOV
536
                        return &wire.TxOut{
×
UNCOV
537
                                PkScript: pkScript,
×
UNCOV
538
                                Value:    int64(balance),
×
UNCOV
539
                        }
×
UNCOV
540
                }
×
541

UNCOV
542
                return nil
×
543
        }
544

UNCOV
545
        localTxOut := deriveTxOut(
×
UNCOV
546
                c.LocalBalance.ToSatoshis(),
×
UNCOV
547
                c.LocalDeliveryScript,
×
UNCOV
548
        )
×
UNCOV
549
        remoteTxOut := deriveTxOut(
×
UNCOV
550
                c.RemoteBalance.ToSatoshis(),
×
UNCOV
551
                c.RemoteDeliveryScript,
×
UNCOV
552
        )
×
UNCOV
553

×
UNCOV
554
        return localTxOut, remoteTxOut
×
555
}
556

557
// RemoteAmtIsDust returns true if the remote output is dust.
558
func (c *CloseChannelTerms) RemoteAmtIsDust() bool {
×
559
        return c.RemoteBalance.ToSatoshis() < lnwallet.DustLimitForSize(
×
560
                len(c.RemoteDeliveryScript),
×
561
        )
×
562
}
×
563

564
// LocalAmtIsDust returns true if the local output is dust.
UNCOV
565
func (c *CloseChannelTerms) LocalAmtIsDust() bool {
×
UNCOV
566
        return c.LocalBalance.ToSatoshis() < lnwallet.DustLimitForSize(
×
UNCOV
567
                len(c.LocalDeliveryScript),
×
UNCOV
568
        )
×
UNCOV
569
}
×
570

571
// LocalCanPayFees returns true if the local party can pay the absolute fee
572
// from their local settled balance.
UNCOV
573
func (c *CloseChannelTerms) LocalCanPayFees(absoluteFee btcutil.Amount) bool {
×
UNCOV
574
        return c.LocalBalance.ToSatoshis() >= absoluteFee
×
UNCOV
575
}
×
576

577
// RemoteCanPayFees returns true if the remote party can pay the absolute fee
578
// from their remote settled balance.
UNCOV
579
func (c *CloseChannelTerms) RemoteCanPayFees(absoluteFee btcutil.Amount) bool {
×
UNCOV
580
        return c.RemoteBalance.ToSatoshis() >= absoluteFee
×
UNCOV
581
}
×
582

583
// LocalCloseStart is the state we enter into after we've received or sent
584
// shutdown, and the channel has been flushed. In this state, we'll emit a new
585
// event to send our offer to drive the rest of the process.
586
//
587
// transition:
588
//   - fromState: ChannelFlushing
589
//   - toState: LocalOfferSent
590
//
591
// input events:
592
//   - SendOfferEvent
593
type LocalCloseStart struct {
594
        CloseChannelTerms
595
}
596

597
// ShouldRouteTo returns true if the target state should process the target
598
// event.
UNCOV
599
func (l *LocalCloseStart) ShouldRouteTo(event ProtocolEvent) bool {
×
UNCOV
600
        switch event.(type) {
×
UNCOV
601
        case *SendOfferEvent:
×
UNCOV
602
                return true
×
UNCOV
603
        default:
×
UNCOV
604
                return false
×
605
        }
606
}
607

608
// IsTerminal returns true if the target state is a terminal state.
609
func (l *LocalCloseStart) IsTerminal() bool {
×
610
        return false
×
611
}
×
612

613
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
614
func (l *LocalCloseStart) protocolStateSealed() {}
×
615

616
// LocalOfferSent is the state we transition to after we reveiver the
617
// SendOfferEvent in the LocalCloseStart state. With this state we send our
618
// offer to the remote party, then await a sig from them which concludes the
619
// local cooperative close process.
620
//
621
// transition:
622
//   - fromState: LocalCloseStart
623
//   - toState: ClosePending
624
//
625
// input events:
626
//   - LocalSigReceived
627
type LocalOfferSent struct {
628
        CloseChannelTerms
629

630
        // ProposedFee is the fee we proposed to the remote party.
631
        ProposedFee btcutil.Amount
632

633
        // LocalSig is the signature we sent to the remote party.
634
        LocalSig lnwire.Sig
635
}
636

637
// ShouldRouteTo returns true if the target state should process the target
638
// event.
UNCOV
639
func (l *LocalOfferSent) ShouldRouteTo(event ProtocolEvent) bool {
×
UNCOV
640
        switch event.(type) {
×
UNCOV
641
        case *LocalSigReceived:
×
UNCOV
642
                return true
×
643
        default:
×
644
                return false
×
645
        }
646
}
647

648
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
649
func (l *LocalOfferSent) protocolStateSealed() {}
×
650

651
// IsTerminal returns true if the target state is a terminal state.
652
func (l *LocalOfferSent) IsTerminal() bool {
×
653
        return false
×
654
}
×
655

656
// ClosePending is the state we enter after concluding the negotiation for the
657
// remote or local state. At this point, given a confirmation notification we
658
// can terminate the process. Otherwise, we can receive a fresh CoopCloseReq to
659
// go back to the very start.
660
//
661
// transition:
662
//   - fromState: LocalOfferSent || RemoteCloseStart
663
//   - toState: CloseFin
664
//
665
// input events:
666
//   - LocalSigReceived
667
//   - OfferReceivedEvent
668
type ClosePending struct {
669
        // CloseTx is the pending close transaction.
670
        CloseTx *wire.MsgTx
671
}
672

673
// ShouldRouteTo returns true if the target state should process the target
674
// event.
675
func (c *ClosePending) ShouldRouteTo(event ProtocolEvent) bool {
×
676
        switch event.(type) {
×
677
        case *SpendEvent:
×
678
                return true
×
679
        default:
×
680
                return false
×
681
        }
682
}
683

684
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
685
func (c *ClosePending) protocolStateSealed() {}
×
686

687
// IsTerminal returns true if the target state is a terminal state.
688
func (c *ClosePending) IsTerminal() bool {
×
689
        return true
×
690
}
×
691

692
// CloseFin is the terminal state for the channel closer state machine. At this
693
// point, the close tx has been confirmed on chain.
694
type CloseFin struct {
695
        // ConfirmedTx is the transaction that confirmed the channel close.
696
        ConfirmedTx *wire.MsgTx
697
}
698

699
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
700
func (c *CloseFin) protocolStateSealed() {}
×
701

702
// IsTerminal returns true if the target state is a terminal state.
703
func (c *CloseFin) IsTerminal() bool {
×
704
        return true
×
705
}
×
706

707
// RemoteCloseStart is similar to the LocalCloseStart, but is used to drive the
708
// process of signing an offer for the remote party
709
//
710
// transition:
711
//   - fromState: ChannelFlushing
712
//   - toState: ClosePending
713
type RemoteCloseStart struct {
714
        CloseChannelTerms
715
}
716

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

728
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
729
func (l *RemoteCloseStart) protocolStateSealed() {}
×
730

731
// IsTerminal returns true if the target state is a terminal state.
732
func (l *RemoteCloseStart) IsTerminal() bool {
×
733
        return false
×
734
}
×
735

736
// RbfChanCloser is a state machine that handles the RBF-enabled cooperative
737
// channel close protocol.
738
type RbfChanCloser = protofsm.StateMachine[ProtocolEvent, *Environment]
739

740
// RbfChanCloserCfg is a configuration struct that is used to initialize a new
741
// RBF chan closer state machine.
742
type RbfChanCloserCfg = protofsm.StateMachineCfg[ProtocolEvent, *Environment]
743

744
// RbfSpendMapper is a type used to map the generic spend event to one specific
745
// to this package.
746
type RbfSpendMapper = protofsm.SpendMapper[ProtocolEvent]
747

748
func SpendMapper(spendEvent *chainntnfs.SpendDetail) ProtocolEvent {
×
749
        return &SpendEvent{
×
750
                Tx:          spendEvent.SpendingTx,
×
751
                BlockHeight: uint32(spendEvent.SpendingHeight),
×
752
        }
×
753
}
×
754

755
// RbfMsgMapperT is a type used to map incoming wire messages to protocol
756
// events.
757
type RbfMsgMapperT = protofsm.MsgMapper[ProtocolEvent]
758

759
// RbfState is a type alias for the state of the RBF channel closer.
760
type RbfState = protofsm.State[ProtocolEvent, *Environment]
761

762
// RbfEvent is a type alias for the event type of the RBF channel closer.
763
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