@@ -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