Skip to content

Commit eb863e4

Browse files
committed
lnwallet/chancloser: fix fllake in TestRbfCloseClosingNegotiationLocal/send_offer_rbf_wrong_local_script
In this commit, we fix a flake in the `TestRbfCloseClosingNegotiationLocal/send_offer_rbf_wrong_local_script` test. This flake can happen if the test shuts down _before_ the state machine is actually able to process the sent event. In this case, the expectations are triggered, and we find that the error isn't sent. To resolve this, we create a new wrapper function that'll use a sync channel send to assert that the error has been sent before we exit the test.
1 parent 40f58da commit eb863e4

File tree

2 files changed

+87
-63
lines changed

2 files changed

+87
-63
lines changed

lnwallet/chancloser/mock.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package chancloser
22

33
import (
44
"sync/atomic"
5+
"testing"
56

67
"github.com/btcsuite/btcd/btcec/v2"
78
"github.com/btcsuite/btcd/btcutil"
@@ -140,10 +141,32 @@ func (m *mockChanObserver) DisableChannel() error {
140141

141142
type mockErrorReporter struct {
142143
mock.Mock
144+
errorReported chan error
145+
}
146+
147+
// newMockErrorReporter creates a new mockErrorReporter.
148+
func newMockErrorReporter(t *testing.T) *mockErrorReporter {
149+
return &mockErrorReporter{
150+
// Buffer of 1 allows ReportError to send without blocking
151+
// if the test isn't immediately ready to receive.
152+
errorReported: make(chan error, 1),
153+
}
143154
}
144155

145156
func (m *mockErrorReporter) ReportError(err error) {
157+
// Keep existing behavior of forwarding to mock.Mock
146158
m.Called(err)
159+
160+
// Non-blockingly send the error to the channel.
161+
select {
162+
case m.errorReported <- err:
163+
164+
// If the channel is full or no one is listening, this prevents
165+
// ReportError from blocking. This might happen if ReportError is called
166+
// multiple times and the test only waits for the first, or if the test
167+
// times out waiting.
168+
default:
169+
}
147170
}
148171

149172
type mockCloseSigner struct {

lnwallet/chancloser/rbf_coop_test.go

Lines changed: 64 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,10 @@ func assertUnknownEventFail(t *testing.T, startingState ProtocolState) {
146146
})
147147
defer closeHarness.stopAndAssert()
148148

149-
closeHarness.expectFailure(ErrInvalidStateTransition)
150-
151-
closeHarness.chanCloser.SendEvent(
149+
closeHarness.sendEventAndExpectFailure(
152150
context.Background(), &unknownEvent{},
151+
ErrInvalidStateTransition,
153152
)
154-
155-
// There should be no further state transitions.
156-
closeHarness.assertNoStateTransitions()
157153
})
158154
}
159155

@@ -239,6 +235,31 @@ func (r *rbfCloserTestHarness) assertExpectations() {
239235
r.signer.AssertExpectations(r.T)
240236
}
241237

238+
// sendEventAndExpectFailure is a helper to expect a failure, send an event,
239+
// and wait for the error report.
240+
func (r *rbfCloserTestHarness) sendEventAndExpectFailure(
241+
ctx context.Context, event ProtocolEvent, expectedErr error) {
242+
243+
r.T.Helper()
244+
245+
r.expectFailure(expectedErr)
246+
247+
r.chanCloser.SendEvent(ctx, event)
248+
249+
select {
250+
case reportedErr := <-r.errReporter.errorReported:
251+
r.T.Logf("Test received error report: %v", reportedErr)
252+
253+
if !errors.Is(reportedErr, expectedErr) {
254+
r.T.Fatalf("reported error (%v) is not the "+
255+
"expected error (%v)", reportedErr, expectedErr)
256+
}
257+
258+
case <-time.After(1 * time.Second):
259+
r.T.Fatalf("timed out waiting for error to be "+
260+
"reported by mockErrorReporter for event %T", event)
261+
}
262+
}
242263
func (r *rbfCloserTestHarness) stopAndAssert() {
243264
r.T.Helper()
244265

@@ -728,7 +749,7 @@ func newRbfCloserTestHarness(t *testing.T,
728749

729750
feeEstimator := &mockFeeEstimator{}
730751
mockObserver := &mockChanObserver{}
731-
errReporter := &mockErrorReporter{}
752+
errReporter := newMockErrorReporter(t)
732753
mockSigner := &mockCloseSigner{}
733754

734755
harness := &rbfCloserTestHarness{
@@ -838,14 +859,13 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
838859
defer closeHarness.stopAndAssert()
839860

840861
closeHarness.failNewAddrFunc()
841-
closeHarness.expectFailure(errfailAddr)
842862

843863
// We don't specify an upfront shutdown addr, and don't specify
844864
// on here in the vent, so we should call new addr, but then
845865
// fail.
846-
closeHarness.chanCloser.SendEvent(ctx, &SendShutdown{})
847-
848-
// We shouldn't have transitioned to a new state.
866+
closeHarness.sendEventAndExpectFailure(
867+
ctx, &SendShutdown{}, errfailAddr,
868+
)
849869
closeHarness.assertNoStateTransitions()
850870
})
851871

@@ -902,16 +922,13 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
902922

903923
// Next, we'll emit the recv event, with the addr of the remote
904924
// party.
905-
closeHarness.chanCloser.SendEvent(
906-
ctx, &ShutdownReceived{
907-
ShutdownScript: remoteAddr,
908-
BlockHeight: 1,
909-
},
925+
event := &ShutdownReceived{
926+
ShutdownScript: remoteAddr,
927+
BlockHeight: 1,
928+
}
929+
closeHarness.sendEventAndExpectFailure(
930+
ctx, event, ErrThawHeightNotReached,
910931
)
911-
912-
// We expect a failure as the block height is less than the
913-
// start height.
914-
closeHarness.expectFailure(ErrThawHeightNotReached)
915932
})
916933

917934
// When we receive a shutdown, we should transition to the shutdown
@@ -998,18 +1015,16 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
9981015
})
9991016
defer closeHarness.stopAndAssert()
10001017

1001-
// We should fail as the shutdown script isn't what we
1002-
// expected.
1003-
closeHarness.expectFailure(ErrUpfrontShutdownScriptMismatch)
1004-
10051018
// We'll now send in a ShutdownReceived event, but with a
10061019
// different address provided in the shutdown message. This
10071020
// should result in an error.
1008-
closeHarness.chanCloser.SendEvent(ctx, &ShutdownReceived{
1021+
event := &ShutdownReceived{
10091022
ShutdownScript: localAddr,
1010-
})
1023+
}
10111024

1012-
// We shouldn't have transitioned to a new state.
1025+
closeHarness.sendEventAndExpectFailure(
1026+
ctx, event, ErrUpfrontShutdownScriptMismatch,
1027+
)
10131028
closeHarness.assertNoStateTransitions()
10141029
})
10151030

@@ -1459,13 +1474,9 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
14591474
},
14601475
}
14611476

1462-
// We expect that the state machine fails as we received more
1463-
// than one error.
1464-
closeHarness.expectFailure(ErrTooManySigs)
1465-
1466-
// We should fail as the remote party sent us more than one
1467-
// signature.
1468-
closeHarness.chanCloser.SendEvent(ctx, localSigEvent)
1477+
closeHarness.sendEventAndExpectFailure(
1478+
ctx, localSigEvent, ErrTooManySigs,
1479+
)
14691480
})
14701481

14711482
// Next, we'll verify that if the balance of the remote party is dust,
@@ -1545,8 +1556,6 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
15451556

15461557
// The remote party will send a ClosingSig message, but with the
15471558
// wrong local script. We should expect an error.
1548-
closeHarness.expectFailure(ErrWrongLocalScript)
1549-
15501559
// We'll send this message in directly, as we shouldn't get any
15511560
// further in the process.
15521561
// assuming we start in this negotiation state.
@@ -1561,7 +1570,9 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
15611570
},
15621571
},
15631572
}
1564-
closeHarness.chanCloser.SendEvent(ctx, localSigEvent)
1573+
closeHarness.sendEventAndExpectFailure(
1574+
ctx, localSigEvent, ErrWrongLocalScript,
1575+
)
15651576
})
15661577

15671578
// In this test, we'll assert that we're able to restart the RBF loop
@@ -1705,21 +1716,19 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17051716
})
17061717
defer closeHarness.stopAndAssert()
17071718

1708-
// We should fail as they sent a sig, but can't pay for fees.
1709-
closeHarness.expectFailure(ErrRemoteCannotPay)
1710-
17111719
// We'll send in a new fee proposal, but the proposed fee will
17121720
// be higher than the remote party's balance.
1713-
feeOffer := &OfferReceivedEvent{
1721+
event := &OfferReceivedEvent{
17141722
SigMsg: lnwire.ClosingComplete{
17151723
CloserScript: remoteAddr,
17161724
CloseeScript: localAddr,
17171725
FeeSatoshis: absoluteFee * 10,
17181726
},
17191727
}
1720-
closeHarness.chanCloser.SendEvent(ctx, feeOffer)
17211728

1722-
// We shouldn't have transitioned to a new state.
1729+
closeHarness.sendEventAndExpectFailure(
1730+
ctx, event, ErrRemoteCannotPay,
1731+
)
17231732
closeHarness.assertNoStateTransitions()
17241733
})
17251734

@@ -1747,12 +1756,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17471756
})
17481757
defer closeHarness.stopAndAssert()
17491758

1750-
// We should fail as they included the wrong sig.
1751-
closeHarness.expectFailure(ErrCloserNoClosee)
1752-
17531759
// Our balance is dust, so we should reject this signature that
17541760
// includes our output.
1755-
feeOffer := &OfferReceivedEvent{
1761+
event := &OfferReceivedEvent{
17561762
SigMsg: lnwire.ClosingComplete{
17571763
FeeSatoshis: absoluteFee,
17581764
CloserScript: remoteAddr,
@@ -1764,9 +1770,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17641770
},
17651771
},
17661772
}
1767-
closeHarness.chanCloser.SendEvent(ctx, feeOffer)
1768-
1769-
// We shouldn't have transitioned to a new state.
1773+
closeHarness.sendEventAndExpectFailure(
1774+
ctx, event, ErrCloserNoClosee,
1775+
)
17701776
closeHarness.assertNoStateTransitions()
17711777
})
17721778

@@ -1778,12 +1784,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17781784
})
17791785
defer closeHarness.stopAndAssert()
17801786

1781-
// We should fail as they included the wrong sig.
1782-
closeHarness.expectFailure(ErrCloserAndClosee)
1783-
17841787
// Both balances are above dust, we should reject this
17851788
// signature as it excludes an output.
1786-
feeOffer := &OfferReceivedEvent{
1789+
event := &OfferReceivedEvent{
17871790
SigMsg: lnwire.ClosingComplete{
17881791
FeeSatoshis: absoluteFee,
17891792
CloserScript: remoteAddr,
@@ -1795,9 +1798,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
17951798
},
17961799
},
17971800
}
1798-
closeHarness.chanCloser.SendEvent(ctx, feeOffer)
1799-
1800-
// We shouldn't have transitioned to a new state.
1801+
closeHarness.sendEventAndExpectFailure(
1802+
ctx, event, ErrCloserAndClosee,
1803+
)
18011804
closeHarness.assertNoStateTransitions()
18021805
})
18031806

@@ -1875,11 +1878,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
18751878

18761879
// The remote party will send a ClosingComplete message, but
18771880
// with the wrong local script. We should expect an error.
1878-
closeHarness.expectFailure(ErrWrongLocalScript)
1879-
18801881
// We'll send our remote addr as the Closee script, which should
18811882
// trigger an error.
1882-
feeOffer := &OfferReceivedEvent{
1883+
event := &OfferReceivedEvent{
18831884
SigMsg: lnwire.ClosingComplete{
18841885
FeeSatoshis: absoluteFee,
18851886
CloserScript: remoteAddr,
@@ -1891,9 +1892,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
18911892
},
18921893
},
18931894
}
1894-
closeHarness.chanCloser.SendEvent(ctx, feeOffer)
1895-
1896-
// We shouldn't have transitioned to a new state.
1895+
closeHarness.sendEventAndExpectFailure(
1896+
ctx, event, ErrWrongLocalScript,
1897+
)
18971898
closeHarness.assertNoStateTransitions()
18981899
})
18991900

0 commit comments

Comments
 (0)