Skip to content

Commit 6f09d96

Browse files
authored
Merge pull request #10052 from guggero/rbf-close-err
chancloser: fix flakes in chancloser tests
2 parents 90e2116 + 1882939 commit 6f09d96

File tree

6 files changed

+109
-99
lines changed

6 files changed

+109
-99
lines changed

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ flakehunter-unit:
286286
@$(call print, "Flake hunting unit test.")
287287
scripts/unit-test-flake-hunter.sh ${pkg} ${case}
288288

289+
#? flakehunter-unit-all: Run all unit tests continuously until one fails
290+
flakehunter-unit-all: $(BTCD_BIN)
291+
@$(call print, "Flake hunting unit tests.")
292+
while [ $$? -eq 0 ]; do make unit; done
293+
294+
#? flakehunter-unit-race: Run all unit tests in race detector mode continuously until one fails
295+
flakehunter-unit-race: $(BTCD_BIN)
296+
@$(call print, "Flake hunting unit tests in race detector mode.")
297+
while [ $$? -eq 0 ]; do make unit-race; done
298+
289299
#? flakehunter-itest-parallel: Run the integration tests continuously until one fails, running up to ITEST_PARALLELISM test tranches in parallel (default 4)
290300
flakehunter-itest-parallel:
291301
@$(call print, "Flake hunting ${backend} integration tests in parallel.")

lnwallet/chancloser/chancloser_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,10 @@ func TestTaprootFastClose(t *testing.T) {
509509
aliceChan := newMockTaprootChan(t, true)
510510
bobChan := newMockTaprootChan(t, false)
511511

512-
broadcastSignal := make(chan struct{}, 2)
512+
// We'll create two distinct broadcast signals to ensure that each party
513+
// broadcasts at the correct time.
514+
aliceBroadcast := make(chan struct{}, 1)
515+
bobBroadcast := make(chan struct{}, 1)
513516

514517
idealFee := chainfee.SatPerKWeight(506)
515518

@@ -520,7 +523,7 @@ func TestTaprootFastClose(t *testing.T) {
520523
Channel: aliceChan,
521524
MusigSession: newMockMusigSession(),
522525
BroadcastTx: func(_ *wire.MsgTx, _ string) error {
523-
broadcastSignal <- struct{}{}
526+
aliceBroadcast <- struct{}{}
524527
return nil
525528
},
526529
MaxFee: chainfee.SatPerKWeight(1000),
@@ -538,7 +541,7 @@ func TestTaprootFastClose(t *testing.T) {
538541
MusigSession: newMockMusigSession(),
539542
MaxFee: chainfee.SatPerKWeight(1000),
540543
BroadcastTx: func(_ *wire.MsgTx, _ string) error {
541-
broadcastSignal <- struct{}{}
544+
bobBroadcast <- struct{}{}
542545
return nil
543546
},
544547
FeeEstimator: &SimpleCoopFeeEstimator{},
@@ -591,7 +594,7 @@ func TestTaprootFastClose(t *testing.T) {
591594

592595
// At this point, Bob has accepted the offer, so he can broadcast the
593596
// closing transaction, and considers the channel closed.
594-
_, err = lnutils.RecvOrTimeout(broadcastSignal, time.Second*1)
597+
_, err = lnutils.RecvOrTimeout(bobBroadcast, time.Second*1)
595598
require.NoError(t, err)
596599

597600
// Bob's fee proposal should exactly match Alice's initial fee.
@@ -623,7 +626,7 @@ func TestTaprootFastClose(t *testing.T) {
623626
aliceClosingSigned = oClosingSigned.UnwrapOrFail(t)
624627

625628
// Alice should now also broadcast her closing transaction.
626-
_, err = lnutils.RecvOrTimeout(broadcastSignal, time.Second*1)
629+
_, err = lnutils.RecvOrTimeout(aliceBroadcast, time.Second*1)
627630
require.NoError(t, err)
628631

629632
// Finally, Bob will process Alice's echo message, and conclude.

lnwallet/chancloser/rbf_coop_states.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,16 @@ type CloseSigner interface {
225225
// balance in the created state.
226226
CreateCloseProposal(proposedFee btcutil.Amount,
227227
localDeliveryScript []byte, remoteDeliveryScript []byte,
228-
closeOpt ...lnwallet.ChanCloseOpt,
229-
) (
230-
input.Signature, *wire.MsgTx, btcutil.Amount, error)
228+
closeOpt ...lnwallet.ChanCloseOpt) (input.Signature,
229+
*wire.MsgTx, btcutil.Amount, error)
231230

232231
// CompleteCooperativeClose persistently "completes" the cooperative
233232
// close by producing a fully signed co-op close transaction.
234233
CompleteCooperativeClose(localSig, remoteSig input.Signature,
235234
localDeliveryScript, remoteDeliveryScript []byte,
236-
proposedFee btcutil.Amount, closeOpt ...lnwallet.ChanCloseOpt,
237-
) (*wire.MsgTx, btcutil.Amount, error)
235+
proposedFee btcutil.Amount,
236+
closeOpt ...lnwallet.ChanCloseOpt) (*wire.MsgTx, btcutil.Amount,
237+
error)
238238
}
239239

240240
// ChanStateObserver is an interface used to observe state changes that occur
@@ -579,8 +579,8 @@ type ErrStateCantPayForFee struct {
579579
}
580580

581581
// NewErrStateCantPayForFee returns a new NewErrStateCantPayForFee error.
582-
func NewErrStateCantPayForFee(localBalance, attemptedFee btcutil.Amount,
583-
) *ErrStateCantPayForFee {
582+
func NewErrStateCantPayForFee(localBalance,
583+
attemptedFee btcutil.Amount) *ErrStateCantPayForFee {
584584

585585
return &ErrStateCantPayForFee{
586586
localBalance: localBalance,
@@ -621,8 +621,9 @@ type CloseChannelTerms struct {
621621
// DeriveCloseTxOuts takes the close terms, and returns the local and remote tx
622622
// out for the close transaction. If an output is dust, then it'll be nil.
623623
func (c *CloseChannelTerms) DeriveCloseTxOuts() (*wire.TxOut, *wire.TxOut) {
624-
//nolint:ll
625-
deriveTxOut := func(balance btcutil.Amount, pkScript []byte) *wire.TxOut {
624+
deriveTxOut := func(balance btcutil.Amount,
625+
pkScript []byte) *wire.TxOut {
626+
626627
// We'll base the existence of the output on our normal dust
627628
// check.
628629
dustLimit := lnwallet.DustLimitForSize(len(pkScript))
@@ -710,10 +711,10 @@ func (l *LocalCloseStart) IsTerminal() bool {
710711
return false
711712
}
712713

713-
// protocolStateaSealed indicates that this struct is a ProtocolEvent instance.
714+
// protocolStateSealed indicates that this struct is a ProtocolEvent instance.
714715
func (l *LocalCloseStart) protocolStateSealed() {}
715716

716-
// LocalOfferSent is the state we transition to after we reveiver the
717+
// LocalOfferSent is the state we transition to after we receiver the
717718
// SendOfferEvent in the LocalCloseStart state. With this state we send our
718719
// offer to the remote party, then await a sig from them which concludes the
719720
// local cooperative close process.

lnwallet/chancloser/rbf_coop_test.go

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@ var (
4949
remoteSigBytes = fromHex("304502210082235e21a2300022738dabb8e1bbd9d1" +
5050
"9cfb1e7ab8c30a23b0afbb8d178abcf3022024bf68e256c534ddfaf966b" +
5151
"f908deb944305596f7bdcc38d69acad7f9c868724")
52-
remoteSig = sigMustParse(remoteSigBytes)
53-
remoteWireSig = mustWireSig(&remoteSig)
52+
remoteSig = sigMustParse(remoteSigBytes)
53+
remoteWireSig = mustWireSig(&remoteSig)
54+
remoteSigRecordType3 = newSigTlv[tlv.TlvType3](remoteWireSig)
55+
remoteSigRecordType1 = newSigTlv[tlv.TlvType1](remoteWireSig)
5456

5557
localTx = wire.MsgTx{Version: 2}
5658

5759
closeTx = wire.NewMsgTx(2)
60+
61+
defaultTimeout = 500 * time.Millisecond
62+
longTimeout = 3 * time.Second
63+
defaultPoll = 50 * time.Millisecond
5864
)
5965

6066
func sigMustParse(sigBytes []byte) ecdsa.Signature {
@@ -111,7 +117,7 @@ func assertStateTransitions[Event any, Env protofsm.Environment](
111117

112118
for _, expectedState := range expectedStates {
113119
newState, err := fn.RecvOrTimeout(
114-
stateSub.NewItemCreated.ChanOut(), 10*time.Millisecond,
120+
stateSub.NewItemCreated.ChanOut(), defaultTimeout,
115121
)
116122
require.NoError(t, err, "expected state: %T", expectedState)
117123

@@ -122,7 +128,7 @@ func assertStateTransitions[Event any, Env protofsm.Environment](
122128
select {
123129
case newState := <-stateSub.NewItemCreated.ChanOut():
124130
t.Fatalf("unexpected state transition: %v", newState)
125-
default:
131+
case <-time.After(defaultPoll):
126132
}
127133
}
128134

@@ -285,10 +291,12 @@ func (r *rbfCloserTestHarness) assertStartupAssertions() {
285291
}
286292

287293
func (r *rbfCloserTestHarness) assertNoStateTransitions() {
294+
r.T.Helper()
295+
288296
select {
289297
case newState := <-r.stateSub.NewItemCreated.ChanOut():
290298
r.T.Fatalf("unexpected state transition: %T", newState)
291-
case <-time.After(10 * time.Millisecond):
299+
case <-time.After(defaultPoll):
292300
}
293301
}
294302

@@ -436,7 +444,7 @@ func (r *rbfCloserTestHarness) waitForMsgSent() {
436444

437445
err := wait.Predicate(func() bool {
438446
return r.daemonAdapters.msgSent.Load()
439-
}, time.Second*3)
447+
}, longTimeout)
440448
require.NoError(r.T, err)
441449
}
442450

@@ -642,7 +650,7 @@ func (r *rbfCloserTestHarness) assertSingleRbfIteration(
642650
// We'll now send in the send offer event, which should trigger 1/2 of
643651
// the RBF loop, ending us in the LocalOfferSent state.
644652
r.expectHalfSignerIteration(
645-
initEvent, balanceAfterClose, absoluteFee, noDustExpect,
653+
initEvent, balanceAfterClose, absoluteFee, dustExpect,
646654
iteration,
647655
)
648656

@@ -693,15 +701,21 @@ func (r *rbfCloserTestHarness) assertSingleRemoteRbfIteration(
693701
}
694702

695703
// Our outer state should transition to ClosingNegotiation state.
696-
r.assertStateTransitions(&ClosingNegotiation{})
704+
transitions := []RbfState{
705+
&ClosingNegotiation{},
706+
}
697707

698708
// If this is an iteration, then we'll go from ClosePending ->
699709
// RemoteCloseStart -> ClosePending. So we'll assert an extra transition
700710
// here.
701711
if iteration {
702-
r.assertStateTransitions(&ClosingNegotiation{})
712+
transitions = append(transitions, &ClosingNegotiation{})
703713
}
704714

715+
// Now that we know how many state transitions to expect, we'll wait
716+
// for them.
717+
r.assertStateTransitions(transitions...)
718+
705719
// If we examine the final resting state, we should see that the we're
706720
// now in the ClosePending state for the remote peer.
707721
currentState := assertStateT[*ClosingNegotiation](r)
@@ -802,7 +816,7 @@ func newRbfCloserTestHarness(t *testing.T,
802816
MsgMapper: fn.Some[protofsm.MsgMapper[ProtocolEvent]](
803817
msgMapper,
804818
),
805-
CustomPollInterval: fn.Some(time.Nanosecond),
819+
CustomPollInterval: fn.Some(defaultPoll),
806820
}
807821

808822
// Before we start we always expect an initial spend event.
@@ -811,10 +825,13 @@ func newRbfCloserTestHarness(t *testing.T,
811825
).Return(nil)
812826

813827
chanCloser := protofsm.NewStateMachine(protoCfg)
814-
chanCloser.Start(ctx)
815828

829+
// We register our subscriber before starting the state machine, to make
830+
// sure we don't miss any events.
816831
harness.stateSub = chanCloser.RegisterStateEvents()
817832

833+
chanCloser.Start(ctx)
834+
818835
harness.chanCloser = &chanCloser
819836

820837
return harness
@@ -1284,9 +1301,8 @@ func TestRbfChannelFlushingTransitions(t *testing.T) {
12841301

12851302
// We'll modify the starting balance to be 3x the required fee
12861303
// to ensure that we can pay for the fee.
1287-
flushEvent.ShutdownBalances.LocalBalance = lnwire.NewMSatFromSatoshis( //nolint:ll
1288-
absoluteFee * 3,
1289-
)
1304+
localBalanceMSat := lnwire.NewMSatFromSatoshis(absoluteFee * 3)
1305+
flushEvent.ShutdownBalances.LocalBalance = localBalanceMSat
12901306

12911307
testName := fmt.Sprintf("local_can_pay_for_fee/"+
12921308
"fresh_flush=%v", isFreshFlush)
@@ -1304,7 +1320,8 @@ func TestRbfChannelFlushingTransitions(t *testing.T) {
13041320
defer closeHarness.stopAndAssert()
13051321

13061322
localBalance := flushEvent.ShutdownBalances.LocalBalance
1307-
balanceAfterClose := localBalance.ToSatoshis() - absoluteFee //nolint:ll
1323+
balanceAfterClose := localBalance.ToSatoshis() -
1324+
absoluteFee
13081325

13091326
// If this is a fresh flush, then we expect the state
13101327
// to be marked on disk.
@@ -1353,9 +1370,7 @@ func TestRbfChannelFlushingTransitions(t *testing.T) {
13531370
CloserScript: remoteAddr,
13541371
CloseeScript: localAddr,
13551372
ClosingSigs: lnwire.ClosingSigs{
1356-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1357-
remoteWireSig,
1358-
),
1373+
CloserAndClosee: remoteSigRecordType3,
13591374
},
13601375
},
13611376
}
@@ -1467,9 +1482,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
14671482
CloserNoClosee: newSigTlv[tlv.TlvType1](
14681483
remoteWireSig,
14691484
),
1470-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1471-
remoteWireSig,
1472-
),
1485+
CloserAndClosee: remoteSigRecordType3,
14731486
},
14741487
},
14751488
}
@@ -1564,9 +1577,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
15641577
CloserScript: remoteAddr,
15651578
CloseeScript: remoteAddr,
15661579
ClosingSigs: lnwire.ClosingSigs{
1567-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1568-
remoteWireSig,
1569-
),
1580+
CloserAndClosee: remoteSigRecordType3,
15701581
},
15711582
},
15721583
}
@@ -1732,7 +1743,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17321743
closeHarness.assertNoStateTransitions()
17331744
})
17341745

1735-
// If our balance, is dust, then the remote party should send a
1746+
// If our balance is dust, then the remote party should send a
17361747
// signature that doesn't include our output.
17371748
t.Run("recv_offer_err_closer_no_closee", func(t *testing.T) {
17381749
// We'll modify our local balance to be dust.
@@ -1764,9 +1775,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17641775
CloserScript: remoteAddr,
17651776
CloseeScript: localAddr,
17661777
ClosingSigs: lnwire.ClosingSigs{
1767-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1768-
remoteWireSig,
1769-
),
1778+
CloserAndClosee: remoteSigRecordType3,
17701779
},
17711780
},
17721781
}
@@ -1792,9 +1801,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17921801
CloserScript: remoteAddr,
17931802
CloseeScript: localAddr,
17941803
ClosingSigs: lnwire.ClosingSigs{
1795-
CloserNoClosee: newSigTlv[tlv.TlvType1]( //nolint:ll
1796-
remoteWireSig,
1797-
),
1804+
CloserNoClosee: remoteSigRecordType1,
17981805
},
17991806
},
18001807
}
@@ -1840,9 +1847,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
18401847
FeeSatoshis: absoluteFee,
18411848
LockTime: 1,
18421849
ClosingSigs: lnwire.ClosingSigs{
1843-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1844-
remoteWireSig,
1845-
),
1850+
CloserAndClosee: remoteSigRecordType3,
18461851
},
18471852
},
18481853
}
@@ -1886,9 +1891,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
18861891
CloserScript: remoteAddr,
18871892
CloseeScript: remoteAddr,
18881893
ClosingSigs: lnwire.ClosingSigs{
1889-
CloserNoClosee: newSigTlv[tlv.TlvType1]( //nolint:ll
1890-
remoteWireSig,
1891-
),
1894+
CloserNoClosee: remoteSigRecordType1,
18921895
},
18931896
},
18941897
}
@@ -1937,9 +1940,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
19371940
FeeSatoshis: absoluteFee,
19381941
LockTime: 1,
19391942
ClosingSigs: lnwire.ClosingSigs{
1940-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
1941-
remoteWireSig,
1942-
),
1943+
CloserAndClosee: remoteSigRecordType3,
19431944
},
19441945
},
19451946
}
@@ -2005,12 +2006,7 @@ func TestRbfCloseErr(t *testing.T) {
20052006
// initiate a new local sig).
20062007
closeHarness.assertSingleRbfIteration(
20072008
localOffer, balanceAfterClose, absoluteFee,
2008-
noDustExpect, false,
2009-
)
2010-
2011-
// We should terminate in the negotiation state.
2012-
closeHarness.assertStateTransitions(
2013-
&ClosingNegotiation{},
2009+
noDustExpect, true,
20142010
)
20152011
})
20162012

@@ -2040,9 +2036,7 @@ func TestRbfCloseErr(t *testing.T) {
20402036
FeeSatoshis: absoluteFee,
20412037
LockTime: 1,
20422038
ClosingSigs: lnwire.ClosingSigs{
2043-
CloserAndClosee: newSigTlv[tlv.TlvType3]( //nolint:ll
2044-
remoteWireSig,
2045-
),
2039+
CloserAndClosee: remoteSigRecordType3,
20462040
},
20472041
},
20482042
}
@@ -2054,7 +2048,7 @@ func TestRbfCloseErr(t *testing.T) {
20542048
// sig.
20552049
closeHarness.assertSingleRemoteRbfIteration(
20562050
feeOffer, balanceAfterClose, absoluteFee, sequence,
2057-
false, true,
2051+
true, true,
20582052
)
20592053
})
20602054

0 commit comments

Comments
 (0)