Skip to content

Commit c7a7368

Browse files
committed
lnwallet: add noop HTLC below reserve test
Add an extra test that exposes the behavior of the lightning channel state machine when one party's balance is close to the configured reserve. Specifically we check that the expected amount of noop HTLCs actually send the amount to the remote side, with the rest of them correctly getting nullified.
1 parent 2c9932b commit c7a7368

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

lnwallet/channel_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11396,3 +11396,92 @@ func TestNoopAddSettle(t *testing.T) {
1139611396
require.Equal(t, aliceBalance, aliceBalanceFinal)
1139711397
require.Equal(t, bobBalance, bobBalanceFinal)
1139811398
}
11399+
11400+
// TestNoopAddBelowReserve tests that the noop HTLCs behave as expected when
11401+
// added over a channel where a party is below their reserve.
11402+
func TestNoopAddBelowReserve(t *testing.T) {
11403+
t.Parallel()
11404+
11405+
// Create a test channel which will be used for the duration of this
11406+
// unittest. The channel will be funded evenly with Alice having 5 BTC,
11407+
// and Bob having 5 BTC.
11408+
chanType := channeldb.SimpleTaprootFeatureBit |
11409+
channeldb.AnchorOutputsBit | channeldb.ZeroHtlcTxFeeBit |
11410+
channeldb.SingleFunderTweaklessBit | channeldb.TapscriptRootBit
11411+
aliceChan, bobChan, err := CreateTestChannels(t, chanType)
11412+
require.NoError(t, err, "unable to create test channels")
11413+
11414+
aliceBalance := aliceChan.channelState.LocalCommitment.LocalBalance
11415+
bobBalance := bobChan.channelState.LocalCommitment.LocalBalance
11416+
11417+
const (
11418+
// htlcAmt is the default HTLC amount to be used, epxressed in
11419+
// milli-satoshis.
11420+
htlcAmt = lnwire.MilliSatoshi(500_000)
11421+
11422+
// numHtlc is the total number of HTLCs to be added/settled over
11423+
// the channel.
11424+
numHtlc = 20
11425+
)
11426+
11427+
// Let's create the noop add TLV record to be used in all added HTLCs
11428+
// over the channel.
11429+
noopRecord := tlv.NewPrimitiveRecord[NoopAddHtlcType, bool](true)
11430+
records, err := tlv.RecordsToMap([]tlv.Record{noopRecord.Record()})
11431+
require.NoError(t, err)
11432+
11433+
// Let's set Bob's reserve to whatever his local balance is, minus half
11434+
// of the total amount to be added by the total HTLCs. This way we can
11435+
// also verify that the noop-adds will start the nullification only once
11436+
// Bob is above reserve.
11437+
reserveTarget := (numHtlc / 2) * htlcAmt
11438+
bobReserve := bobBalance + reserveTarget
11439+
11440+
bobChan.channelState.LocalChanCfg.ChanReserve =
11441+
bobReserve.ToSatoshis()
11442+
11443+
aliceChan.channelState.RemoteChanCfg.ChanReserve =
11444+
bobReserve.ToSatoshis()
11445+
11446+
// Add and settle all the HTLCs over the channel.
11447+
for i := range numHtlc {
11448+
htlc, preimage := createHTLC(i, htlcAmt)
11449+
htlc.CustomRecords = records
11450+
11451+
aliceHtlcIndex, err := aliceChan.AddHTLC(htlc, nil)
11452+
require.NoError(t, err, "alice unable to add htlc")
11453+
bobHtlcIndex, err := bobChan.ReceiveHTLC(htlc)
11454+
require.NoError(t, err, "bob unable to receive htlc")
11455+
11456+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11457+
11458+
// We'll have Bob settle the HTLC, then force another state
11459+
// transition.
11460+
err = bobChan.SettleHTLC(preimage, bobHtlcIndex, nil, nil, nil)
11461+
require.NoError(t, err, "bob unable to settle inbound htlc")
11462+
err = aliceChan.ReceiveHTLCSettle(preimage, aliceHtlcIndex)
11463+
if err != nil {
11464+
t.Fatalf("alice unable to accept settle of outbound "+
11465+
"htlc: %v", err)
11466+
}
11467+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11468+
}
11469+
11470+
// We need to kick the state transition one last time for the balances
11471+
// to be updated on both commitments.
11472+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11473+
11474+
aliceBalanceFinal := aliceChan.channelState.LocalCommitment.LocalBalance
11475+
bobBalanceFinal := bobChan.channelState.LocalCommitment.LocalBalance
11476+
11477+
// The balances of Alice and Bob must have changed exactly by half the
11478+
// total number of HTLCs we added over the channel, plus one to get Bob
11479+
// above the reserve. Bob's final balance should be as much as his
11480+
// reserve plus one extra default HTLC amount.
11481+
require.Equal(t, aliceBalance-htlcAmt*(numHtlc/2+1), aliceBalanceFinal)
11482+
require.Equal(t, bobBalance+htlcAmt*(numHtlc/2+1), bobBalanceFinal)
11483+
require.Equal(
11484+
t, bobBalanceFinal.ToSatoshis(),
11485+
bobChan.LocalChanReserve()+htlcAmt.ToSatoshis(),
11486+
)
11487+
}

0 commit comments

Comments
 (0)