2023-12-08 12:50:43 -08:00
|
|
|
package htlcswitch
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/fn"
|
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
var cid = lnwire.ChannelID(bytes.Repeat([]byte{0x00}, 32))
|
|
|
|
|
|
|
|
type quiescerTestHarness struct {
|
|
|
|
pendingUpdates lntypes.Dual[uint64]
|
|
|
|
quiescer Quiescer
|
|
|
|
conn <-chan lnwire.Stfu
|
|
|
|
}
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
func initQuiescerTestHarness(
|
|
|
|
channelInitiator lntypes.ChannelParty) *quiescerTestHarness {
|
|
|
|
|
2023-12-08 12:50:43 -08:00
|
|
|
conn := make(chan lnwire.Stfu, 1)
|
|
|
|
harness := &quiescerTestHarness{
|
|
|
|
pendingUpdates: lntypes.Dual[uint64]{},
|
|
|
|
conn: conn,
|
|
|
|
}
|
|
|
|
|
|
|
|
harness.quiescer = NewQuiescer(QuiescerCfg{
|
2024-03-12 12:32:43 -07:00
|
|
|
chanID: cid,
|
|
|
|
channelInitiator: channelInitiator,
|
2023-12-08 12:50:43 -08:00
|
|
|
sendMsg: func(msg lnwire.Stfu) error {
|
|
|
|
conn <- msg
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
return harness
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescerDoubleRecvInvalid ensures that we get an error response when we
|
|
|
|
// receive the Stfu message twice during the lifecycle of the quiescer.
|
|
|
|
func TestQuiescerDoubleRecvInvalid(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.Error(t, err, ErrStfuAlreadyRcvd)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescerPendingUpdatesRecvInvalid ensures that we get an error if we
|
|
|
|
// receive the Stfu message while the Remote party has panding updates on the
|
|
|
|
// channel.
|
|
|
|
func TestQuiescerPendingUpdatesRecvInvalid(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Remote, 1)
|
|
|
|
err := harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.ErrorIs(t, err, ErrPendingRemoteUpdates)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescenceRemoteInit ensures that we can successfully traverse the state
|
|
|
|
// graph of quiescence beginning with the Remote party initiating quiescence.
|
|
|
|
func TestQuiescenceRemoteInit(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 1)
|
|
|
|
|
|
|
|
err := harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-harness.conn:
|
|
|
|
t.Fatalf("stfu sent when not expected")
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 0)
|
|
|
|
err = harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case msg := <-harness.conn:
|
|
|
|
require.False(t, msg.Initiator)
|
|
|
|
default:
|
|
|
|
t.Fatalf("stfu not sent when expected")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
func TestQuiescenceLocalInit(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 1)
|
|
|
|
|
|
|
|
stfuReq, stfuRes := fn.NewReq[fn.Unit, fn.Result[lntypes.ChannelParty]](
|
|
|
|
fn.Unit{},
|
|
|
|
)
|
|
|
|
harness.quiescer.InitStfu(stfuReq)
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 1)
|
|
|
|
err := harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-harness.conn:
|
|
|
|
t.Fatalf("stfu sent when not expected")
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 0)
|
|
|
|
err = harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case msg := <-harness.conn:
|
|
|
|
require.True(t, msg.Initiator)
|
|
|
|
default:
|
|
|
|
t.Fatalf("stfu not sent when expected")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case party := <-stfuRes:
|
|
|
|
require.Equal(t, fn.Ok(lntypes.Local), party)
|
|
|
|
default:
|
|
|
|
t.Fatalf("quiescence request not resolved")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-08 12:50:43 -08:00
|
|
|
// TestQuiescenceInitiator ensures that the quiescenceInitiator is the Remote
|
|
|
|
// party when we have a receive first traversal of the quiescer's state graph.
|
|
|
|
func TestQuiescenceInitiator(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
// Remote Initiated
|
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
require.True(t, harness.quiescer.QuiescenceInitiator().IsErr())
|
|
|
|
|
|
|
|
// Receive
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.True(t, harness.quiescer.QuiescenceInitiator().IsErr())
|
|
|
|
|
|
|
|
// Send
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local),
|
|
|
|
)
|
|
|
|
require.Equal(
|
|
|
|
t, harness.quiescer.QuiescenceInitiator(),
|
|
|
|
fn.Ok(lntypes.Remote),
|
|
|
|
)
|
2024-03-12 12:32:43 -07:00
|
|
|
|
|
|
|
// Local Initiated
|
|
|
|
harness = initQuiescerTestHarness(lntypes.Local)
|
|
|
|
require.True(t, harness.quiescer.quiescenceInitiator().IsErr())
|
|
|
|
|
|
|
|
req, res := fn.NewReq[fn.Unit, fn.Result[lntypes.ChannelParty]](
|
|
|
|
fn.Unit{},
|
|
|
|
)
|
|
|
|
harness.quiescer.initStfu(req)
|
|
|
|
req2, res2 := fn.NewReq[fn.Unit, fn.Result[lntypes.ChannelParty]](
|
|
|
|
fn.Unit{},
|
|
|
|
)
|
|
|
|
harness.quiescer.initStfu(req2)
|
|
|
|
select {
|
|
|
|
case initiator := <-res2:
|
|
|
|
require.True(t, initiator.IsErr())
|
|
|
|
default:
|
|
|
|
t.Fatal("quiescence request not resolved")
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.sendOwedStfu(harness.pendingUpdates.Local),
|
|
|
|
)
|
|
|
|
require.True(t, harness.quiescer.quiescenceInitiator().IsErr())
|
|
|
|
|
|
|
|
msg = lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: false,
|
|
|
|
}
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.recvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.True(t, harness.quiescer.quiescenceInitiator().IsOk())
|
|
|
|
|
|
|
|
select {
|
|
|
|
case initiator := <-res:
|
|
|
|
require.Equal(t, fn.Ok(lntypes.Local), initiator)
|
|
|
|
default:
|
|
|
|
t.Fatal("quiescence request not resolved")
|
|
|
|
}
|
2023-12-08 12:50:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescenceCantReceiveUpdatesAfterStfu tests that we can receive channel
|
|
|
|
// updates prior to but not after we receive Stfu.
|
|
|
|
func TestQuiescenceCantReceiveUpdatesAfterStfu(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
require.True(t, harness.quiescer.CanRecvUpdates())
|
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.False(t, harness.quiescer.CanRecvUpdates())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescenceCantSendUpdatesAfterStfu tests that we can send channel updates
|
|
|
|
// prior to but not after we send Stfu.
|
|
|
|
func TestQuiescenceCantSendUpdatesAfterStfu(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
require.True(t, harness.quiescer.CanSendUpdates())
|
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := harness.quiescer.RecvStfu(msg, harness.pendingUpdates.Remote)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
err = harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.False(t, harness.quiescer.CanSendUpdates())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescenceStfuNotNeededAfterRecv tests that after we receive an Stfu we
|
|
|
|
// do not needStfu either before or after receiving it if we do not initiate
|
|
|
|
// quiescence.
|
|
|
|
func TestQuiescenceStfuNotNeededAfterRecv(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
require.False(t, harness.quiescer.NeedStfu())
|
|
|
|
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
require.False(t, harness.quiescer.NeedStfu())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQuiescenceInappropriateMakeStfuReturnsErr ensures that we cannot call
|
|
|
|
// makeStfu at times when it would be a protocol violation to send it.
|
|
|
|
func TestQuiescenceInappropriateMakeStfuReturnsErr(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2024-03-12 12:32:43 -07:00
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
2023-12-08 12:50:43 -08:00
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 1)
|
|
|
|
|
|
|
|
require.True(
|
|
|
|
t, harness.quiescer.MakeStfu(
|
|
|
|
harness.pendingUpdates.Local,
|
|
|
|
).IsErr(),
|
|
|
|
)
|
|
|
|
|
|
|
|
harness.pendingUpdates.SetForParty(lntypes.Local, 0)
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.True(
|
|
|
|
t, harness.quiescer.MakeStfu(
|
|
|
|
harness.pendingUpdates.Local,
|
|
|
|
).IsOk(),
|
|
|
|
)
|
|
|
|
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.SendOwedStfu(harness.pendingUpdates.Local),
|
|
|
|
)
|
|
|
|
require.True(
|
|
|
|
t, harness.quiescer.MakeStfu(
|
|
|
|
harness.pendingUpdates.Local,
|
|
|
|
).IsErr(),
|
|
|
|
)
|
|
|
|
}
|
2024-03-12 12:32:43 -07:00
|
|
|
|
|
|
|
// TestQuiescerTieBreaker ensures that if both parties attempt to claim the
|
|
|
|
// initiator role that the result of the negotiation breaks the tie using the
|
|
|
|
// channel initiator.
|
|
|
|
func TestQuiescerTieBreaker(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
for _, initiator := range []lntypes.ChannelParty{
|
|
|
|
lntypes.Local, lntypes.Remote,
|
|
|
|
} {
|
|
|
|
harness := initQuiescerTestHarness(initiator)
|
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
req, res := fn.NewReq[fn.Unit, fn.Result[lntypes.ChannelParty]](
|
|
|
|
fn.Unit{},
|
|
|
|
)
|
|
|
|
|
|
|
|
harness.quiescer.InitStfu(req)
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.SendOwedStfu(
|
|
|
|
harness.pendingUpdates.Local,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case party := <-res:
|
|
|
|
require.Equal(t, fn.Ok(initiator), party)
|
|
|
|
default:
|
|
|
|
t.Fatal("quiescence party unavailable")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-08 18:17:00 -07:00
|
|
|
|
|
|
|
// TestQuiescerResume ensures that the hooks that are attached to the quiescer
|
|
|
|
// are called when we call the resume method and no earlier.
|
|
|
|
func TestQuiescerResume(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
harness := initQuiescerTestHarness(lntypes.Local)
|
|
|
|
|
|
|
|
msg := lnwire.Stfu{
|
|
|
|
ChanID: cid,
|
|
|
|
Initiator: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.RecvStfu(
|
|
|
|
msg, harness.pendingUpdates.Remote,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
require.NoError(
|
|
|
|
t, harness.quiescer.SendOwedStfu(
|
|
|
|
harness.pendingUpdates.Local,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
require.True(t, harness.quiescer.IsQuiescent())
|
|
|
|
var resumeHooksCalled = false
|
|
|
|
harness.quiescer.OnResume(func() {
|
|
|
|
resumeHooksCalled = true
|
|
|
|
})
|
|
|
|
require.False(t, resumeHooksCalled)
|
|
|
|
|
|
|
|
harness.quiescer.Resume()
|
|
|
|
require.True(t, resumeHooksCalled)
|
|
|
|
require.False(t, harness.quiescer.IsQuiescent())
|
|
|
|
}
|