Skip to content

Commit 334f224

Browse files
committed
lnwallet: add noop HTLC tests
Adds some simple tests to check the noop HTLC logic of the lightning channel.
1 parent 3b54abc commit 334f224

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

lnwallet/channel_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11339,3 +11339,143 @@ func TestCreateCooperativeCloseTx(t *testing.T) {
1133911339
})
1134011340
}
1134111341
}
11342+
11343+
// TestNoopAddSettle tests that adding and settling an HTLC with no-op, no
11344+
// balances are actually affected.
11345+
func TestNoopAddSettle(t *testing.T) {
11346+
t.Parallel()
11347+
11348+
// Create a test channel which will be used for the duration of this
11349+
// unittest. The channel will be funded evenly with Alice having 5 BTC,
11350+
// and Bob having 5 BTC.
11351+
chanType := channeldb.SimpleTaprootFeatureBit |
11352+
channeldb.AnchorOutputsBit | channeldb.ZeroHtlcTxFeeBit |
11353+
channeldb.SingleFunderTweaklessBit | channeldb.TapscriptRootBit
11354+
aliceChannel, bobChannel, err := CreateTestChannels(
11355+
t, chanType,
11356+
)
11357+
require.NoError(t, err, "unable to create test channels")
11358+
11359+
const htlcAmt = 10_000
11360+
htlc, preimage := createHTLC(0, htlcAmt)
11361+
noopRecord := tlv.NewPrimitiveRecord[tlv.TlvType65544, bool](true)
11362+
11363+
records, err := tlv.RecordsToMap([]tlv.Record{noopRecord.Record()})
11364+
require.NoError(t, err)
11365+
htlc.CustomRecords = records
11366+
11367+
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance
11368+
bobBalance := bobChannel.channelState.LocalCommitment.LocalBalance
11369+
11370+
// Have Alice add the HTLC, then lock it in with a new state transition.
11371+
aliceHtlcIndex, err := aliceChannel.AddHTLC(htlc, nil)
11372+
require.NoError(t, err, "alice unable to add htlc")
11373+
bobHtlcIndex, err := bobChannel.ReceiveHTLC(htlc)
11374+
require.NoError(t, err, "bob unable to receive htlc")
11375+
11376+
err = ForceStateTransition(aliceChannel, bobChannel)
11377+
require.NoError(t, err)
11378+
11379+
// We'll have Bob settle the HTLC, then force another state transition.
11380+
err = bobChannel.SettleHTLC(preimage, bobHtlcIndex, nil, nil, nil)
11381+
require.NoError(t, err, "bob unable to settle inbound htlc")
11382+
err = aliceChannel.ReceiveHTLCSettle(preimage, aliceHtlcIndex)
11383+
require.NoError(t, err)
11384+
11385+
err = ForceStateTransition(aliceChannel, bobChannel)
11386+
require.NoError(t, err)
11387+
11388+
aliceBalanceFinal := aliceChannel.channelState.LocalCommitment.LocalBalance //nolint:ll
11389+
bobBalanceFinal := bobChannel.channelState.LocalCommitment.LocalBalance
11390+
11391+
// The balances of Alice and Bob should be the exact same and shouldn't
11392+
// have changed.
11393+
require.Equal(t, aliceBalance, aliceBalanceFinal)
11394+
require.Equal(t, bobBalance, bobBalanceFinal)
11395+
}
11396+
11397+
// TestNoopAddBelowReserve tests that the noop HTLCs behave as expected when
11398+
// added over a channel where a party is below their reserve.
11399+
func TestNoopAddBelowReserve(t *testing.T) {
11400+
t.Parallel()
11401+
11402+
// Create a test channel which will be used for the duration of this
11403+
// unittest. The channel will be funded evenly with Alice having 5 BTC,
11404+
// and Bob having 5 BTC.
11405+
chanType := channeldb.SimpleTaprootFeatureBit |
11406+
channeldb.AnchorOutputsBit | channeldb.ZeroHtlcTxFeeBit |
11407+
channeldb.SingleFunderTweaklessBit | channeldb.TapscriptRootBit
11408+
aliceChan, bobChan, err := CreateTestChannels(t, chanType)
11409+
require.NoError(t, err, "unable to create test channels")
11410+
11411+
aliceBalance := aliceChan.channelState.LocalCommitment.LocalBalance
11412+
bobBalance := bobChan.channelState.LocalCommitment.LocalBalance
11413+
11414+
const (
11415+
// htlcAmt is the default HTLC amount to be used, epxressed in
11416+
// milli-satoshis.
11417+
htlcAmt = lnwire.MilliSatoshi(500_000)
11418+
11419+
// numHtlc is the total number of HTLCs to be added/settled over
11420+
// the channel.
11421+
numHtlc = 20
11422+
)
11423+
11424+
// Let's create the noop add TLV record to be used in all added HTLCs
11425+
// over the channel.
11426+
noopRecord := tlv.NewPrimitiveRecord[NoopAddHtlcType, bool](true)
11427+
records, err := tlv.RecordsToMap([]tlv.Record{noopRecord.Record()})
11428+
require.NoError(t, err)
11429+
11430+
// Let's set Bob's reserve to whatever his local balance is, minus half
11431+
// of the total amount to be added by the total HTLCs. This way we can
11432+
// also verify that the noop-adds will start the nullification only once
11433+
// Bob is above reserve.
11434+
reserveTarget := (numHtlc / 2) * htlcAmt
11435+
bobReserve := bobBalance + reserveTarget
11436+
11437+
bobChan.channelState.LocalChanCfg.ChanReserve =
11438+
bobReserve.ToSatoshis()
11439+
11440+
aliceChan.channelState.RemoteChanCfg.ChanReserve =
11441+
bobReserve.ToSatoshis()
11442+
11443+
// Add and settle all the HTLCs over the channel.
11444+
for i := range numHtlc {
11445+
htlc, preimage := createHTLC(i, htlcAmt)
11446+
htlc.CustomRecords = records
11447+
11448+
aliceHtlcIndex, err := aliceChan.AddHTLC(htlc, nil)
11449+
require.NoError(t, err, "alice unable to add htlc")
11450+
bobHtlcIndex, err := bobChan.ReceiveHTLC(htlc)
11451+
require.NoError(t, err, "bob unable to receive htlc")
11452+
11453+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11454+
11455+
// We'll have Bob settle the HTLC, then force another state
11456+
// transition.
11457+
err = bobChan.SettleHTLC(preimage, bobHtlcIndex, nil, nil, nil)
11458+
require.NoError(t, err, "bob unable to settle inbound htlc")
11459+
err = aliceChan.ReceiveHTLCSettle(preimage, aliceHtlcIndex)
11460+
require.NoError(t, err)
11461+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11462+
}
11463+
11464+
// We need to kick the state transition one last time for the balances
11465+
// to be updated on both commitments.
11466+
require.NoError(t, ForceStateTransition(aliceChan, bobChan))
11467+
11468+
aliceBalanceFinal := aliceChan.channelState.LocalCommitment.LocalBalance
11469+
bobBalanceFinal := bobChan.channelState.LocalCommitment.LocalBalance
11470+
11471+
// The balances of Alice and Bob must have changed exactly by half the
11472+
// total number of HTLCs we added over the channel, plus one to get Bob
11473+
// above the reserve. Bob's final balance should be as much as his
11474+
// reserve plus one extra default HTLC amount.
11475+
require.Equal(t, aliceBalance-htlcAmt*(numHtlc/2+1), aliceBalanceFinal)
11476+
require.Equal(t, bobBalance+htlcAmt*(numHtlc/2+1), bobBalanceFinal)
11477+
require.Equal(
11478+
t, bobBalanceFinal.ToSatoshis(),
11479+
bobChan.LocalChanReserve()+htlcAmt.ToSatoshis(),
11480+
)
11481+
}

0 commit comments

Comments
 (0)