@@ -11396,3 +11396,92 @@ func TestNoopAddSettle(t *testing.T) {
11396
11396
require .Equal (t , aliceBalance , aliceBalanceFinal )
11397
11397
require .Equal (t , bobBalance , bobBalanceFinal )
11398
11398
}
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