itest: refactor test testUpdateChanStatus
This commit is contained in:
3 files changed
+125
-147
No files matched your search
@@ -6,4 +6,9 @@ package itest
|
|||||||
import "github.com/lightningnetwork/lnd/lntemp"
|
import "github.com/lightningnetwork/lnd/lntemp"
|
||||||
|
|
||||||
// TODO(yy): remove the temp.
|
// TODO(yy): remove the temp.
|
||||||
var allTestCasesTemp = []*lntemp.TestCase{}
|
var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
|
{
|
||||||
|
Name: "update channel status",
|
||||||
|
TestFunc: testUpdateChanStatus,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -23,120 +25,60 @@ import (
|
|||||||
// testUpdateChanStatus checks that calls to the UpdateChanStatus RPC update
|
// testUpdateChanStatus checks that calls to the UpdateChanStatus RPC update
|
||||||
// the channel graph as expected, and that channel state is properly updated
|
// the channel graph as expected, and that channel state is properly updated
|
||||||
// in the presence of interleaved node disconnects / reconnects.
|
// in the presence of interleaved node disconnects / reconnects.
|
||||||
func testUpdateChanStatus(net *lntest.NetworkHarness, t *harnessTest) {
|
//
|
||||||
ctxb := context.Background()
|
// NOTE: this test can be flaky as we are testing the chan-enable-timeout and
|
||||||
|
// chan-disable-timeout flags here.
|
||||||
|
//
|
||||||
|
// For chan-enable-timeout, setting this value too small will cause an enabled
|
||||||
|
// channel never be considered active by our channel status manager. Upon
|
||||||
|
// reconnection, our Brontide will send a request to enable this channel after
|
||||||
|
// the "chan-enable-timeout" has passed. The request is handled by the channel
|
||||||
|
// status manager, which will check the channel's eligibility to forward links
|
||||||
|
// by asking htlcswitch/link. Meanwhile, the htlcswitch/link won't mark the
|
||||||
|
// link as eligible unless it has finished its initialization, which takes some
|
||||||
|
// time. Thus, if the Brontide sends a request too early it will get a false
|
||||||
|
// report saying the channel link is not eligible because that link hasn't
|
||||||
|
// finished its initialization.
|
||||||
|
//
|
||||||
|
// For chan-disable-timeout, setting this value too small will cause an already
|
||||||
|
// enabled channel being marked as disabled. For instance, if some operations
|
||||||
|
// take more than 5 seconds to finish, the channel will be marked as disabled,
|
||||||
|
// thus a following operation will fail if it relies on the channel being
|
||||||
|
// enabled.
|
||||||
|
func testUpdateChanStatus(ht *lntemp.HarnessTest) {
|
||||||
// Create two fresh nodes and open a channel between them.
|
// Create two fresh nodes and open a channel between them.
|
||||||
alice := net.NewNode(t.t, "Alice", []string{
|
alice, bob := ht.Alice, ht.Bob
|
||||||
"--minbackoff=10s",
|
args := []string{
|
||||||
"--chan-enable-timeout=1.5s",
|
"--minbackoff=60s",
|
||||||
"--chan-disable-timeout=3s",
|
"--chan-enable-timeout=3s",
|
||||||
"--chan-status-sample-interval=.5s",
|
"--chan-disable-timeout=6s",
|
||||||
})
|
"--chan-status-sample-interval=0.5s",
|
||||||
defer shutdownAndAssert(net, t, alice)
|
}
|
||||||
|
ht.RestartNodeWithExtraArgs(alice, args)
|
||||||
bob := net.NewNode(t.t, "Bob", []string{
|
ht.RestartNodeWithExtraArgs(bob, args)
|
||||||
"--minbackoff=10s",
|
ht.EnsureConnected(alice, bob)
|
||||||
"--chan-enable-timeout=1.5s",
|
|
||||||
"--chan-disable-timeout=3s",
|
|
||||||
"--chan-status-sample-interval=.5s",
|
|
||||||
})
|
|
||||||
defer shutdownAndAssert(net, t, bob)
|
|
||||||
|
|
||||||
// Connect Alice to Bob.
|
|
||||||
net.ConnectNodes(t.t, alice, bob)
|
|
||||||
|
|
||||||
// Give Alice some coins so she can fund a channel.
|
|
||||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice)
|
|
||||||
|
|
||||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||||
// being the sole funder of the channel.
|
// being the sole funder of the channel.
|
||||||
chanAmt := btcutil.Amount(100000)
|
chanAmt := btcutil.Amount(100000)
|
||||||
chanPoint := openChannelAndAssert(
|
chanPoint := ht.OpenChannel(
|
||||||
t, net, alice, bob, lntest.OpenChannelParams{
|
alice, bob, lntemp.OpenChannelParams{Amt: chanAmt},
|
||||||
Amt: chanAmt,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
defer ht.CloseChannel(alice, chanPoint, false)
|
||||||
|
|
||||||
// Wait for Alice and Bob to receive the channel edge from the
|
// assertEdgeDisabled ensures that Alice has the correct Disabled state
|
||||||
// funding manager.
|
// for given channel from her DescribeGraph.
|
||||||
err := alice.WaitForNetworkChannelOpen(chanPoint)
|
assertEdgeDisabled := func(disabled bool) {
|
||||||
require.NoError(t.t, err, "alice didn't see the alice->bob channel")
|
outPoint := ht.OutPointFromChannelPoint(chanPoint)
|
||||||
|
|
||||||
err = bob.WaitForNetworkChannelOpen(chanPoint)
|
err := wait.NoError(func() error {
|
||||||
require.NoError(t.t, err, "bob didn't see the alice->bob channel")
|
edge := ht.AssertNumEdges(alice, 1, true)[0]
|
||||||
|
|
||||||
// Launch a node for Carol which will connect to Alice and Bob in order
|
|
||||||
// to receive graph updates. This will ensure that the channel updates
|
|
||||||
// are propagated throughout the network.
|
|
||||||
carol := net.NewNode(t.t, "Carol", nil)
|
|
||||||
defer shutdownAndAssert(net, t, carol)
|
|
||||||
|
|
||||||
// Connect both Alice and Bob to the new node Carol, so she can sync her
|
|
||||||
// graph.
|
|
||||||
net.ConnectNodes(t.t, alice, carol)
|
|
||||||
net.ConnectNodes(t.t, bob, carol)
|
|
||||||
waitForGraphSync(t, carol)
|
|
||||||
|
|
||||||
// assertChannelUpdate checks that the required policy update has
|
|
||||||
// happened on the given node.
|
|
||||||
assertChannelUpdate := func(node *lntest.HarnessNode,
|
|
||||||
policy *lnrpc.RoutingPolicy) {
|
|
||||||
|
|
||||||
require.NoError(
|
|
||||||
t.t, carol.WaitForChannelPolicyUpdate(
|
|
||||||
node.PubKeyStr, policy, chanPoint, false,
|
|
||||||
), "error while waiting for channel update",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendReq sends an UpdateChanStatus request to the given node.
|
|
||||||
sendReq := func(node *lntest.HarnessNode, chanPoint *lnrpc.ChannelPoint,
|
|
||||||
action routerrpc.ChanStatusAction) {
|
|
||||||
|
|
||||||
req := &routerrpc.UpdateChanStatusRequest{
|
|
||||||
ChanPoint: chanPoint,
|
|
||||||
Action: action,
|
|
||||||
}
|
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err = node.RouterClient.UpdateChanStatus(ctxt, req)
|
|
||||||
require.NoErrorf(t.t, err, "UpdateChanStatus")
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertEdgeDisabled ensures that a given node has the correct
|
|
||||||
// Disabled state for a channel.
|
|
||||||
assertEdgeDisabled := func(node *lntest.HarnessNode,
|
|
||||||
chanPoint *lnrpc.ChannelPoint, disabled bool) {
|
|
||||||
|
|
||||||
outPoint, err := lntest.MakeOutpoint(chanPoint)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
req := &lnrpc.ChannelGraphRequest{
|
|
||||||
IncludeUnannounced: true,
|
|
||||||
}
|
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
chanGraph, err := node.DescribeGraph(ctxt, req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to query node %v's "+
|
|
||||||
"graph: %v", node, err)
|
|
||||||
}
|
|
||||||
numEdges := len(chanGraph.Edges)
|
|
||||||
if numEdges != 1 {
|
|
||||||
return fmt.Errorf("expected to find 1 edge in "+
|
|
||||||
"the graph, found %d", numEdges)
|
|
||||||
}
|
|
||||||
edge := chanGraph.Edges[0]
|
|
||||||
if edge.ChanPoint != outPoint.String() {
|
if edge.ChanPoint != outPoint.String() {
|
||||||
return fmt.Errorf("expected chan_point %v, "+
|
return fmt.Errorf("expected chan_point %v, "+
|
||||||
"got %v", outPoint, edge.ChanPoint)
|
"got %v", outPoint, edge.ChanPoint)
|
||||||
}
|
}
|
||||||
var policy *lnrpc.RoutingPolicy
|
var policy *lnrpc.RoutingPolicy
|
||||||
if node.PubKeyStr == edge.Node1Pub {
|
if alice.PubKeyStr == edge.Node1Pub {
|
||||||
policy = edge.Node1Policy
|
policy = edge.Node1Policy
|
||||||
} else {
|
} else {
|
||||||
policy = edge.Node2Policy
|
policy = edge.Node2Policy
|
||||||
@@ -144,18 +86,61 @@ func testUpdateChanStatus(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
if disabled != policy.Disabled {
|
if disabled != policy.Disabled {
|
||||||
return fmt.Errorf("expected policy.Disabled "+
|
return fmt.Errorf("expected policy.Disabled "+
|
||||||
"to be %v, but policy was %v", disabled,
|
"to be %v, but policy was %v", disabled,
|
||||||
policy)
|
policy.Disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, defaultTimeout)
|
}, defaultTimeout)
|
||||||
require.NoError(t.t, err)
|
require.NoError(ht, err, "assert edge disabled timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aliceSendReq sends an UpdateChanStatus request to Alice.
|
||||||
|
aliceSendReq := func(action routerrpc.ChanStatusAction) {
|
||||||
|
req := &routerrpc.UpdateChanStatusRequest{
|
||||||
|
ChanPoint: chanPoint,
|
||||||
|
Action: action,
|
||||||
|
}
|
||||||
|
alice.RPC.UpdateChanStatus(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially, the channel between Alice and Bob should not be disabled.
|
||||||
|
//
|
||||||
|
// NOTE: This check should happen right after the channel openning as
|
||||||
|
// we've used a short timeout value for `--chan-disable-timeout`. If we
|
||||||
|
// wait longer than that we might get a flake saying the channel is
|
||||||
|
// disabled.
|
||||||
|
assertEdgeDisabled(false)
|
||||||
|
|
||||||
|
// Launch a node for Carol which will connect to Alice and Bob in order
|
||||||
|
// to receive graph updates. This will ensure that the channel updates
|
||||||
|
// are propagated throughout the network.
|
||||||
|
carol := ht.NewNode("Carol", nil)
|
||||||
|
|
||||||
|
// assertChannelUpdate checks that the required policy update has been
|
||||||
|
// heard in Carol's network.
|
||||||
|
assertChannelUpdate := func(node *node.HarnessNode,
|
||||||
|
policy *lnrpc.RoutingPolicy) {
|
||||||
|
|
||||||
|
ht.AssertChannelPolicyUpdate(
|
||||||
|
carol, node, policy, chanPoint, false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect both Alice and Bob to the new node Carol, so she can sync
|
||||||
|
// her graph.
|
||||||
|
ht.ConnectNodes(alice, carol)
|
||||||
|
ht.ConnectNodes(bob, carol)
|
||||||
|
ht.WaitForGraphSync(carol)
|
||||||
|
|
||||||
|
// If the above waitForGraphSync takes more than 4 seconds, the channel
|
||||||
|
// Alice=>Bob will be marked as disabled now. Thus we connect Alice and
|
||||||
|
// Bob again to make sure the channel is alive.
|
||||||
|
ht.EnsureConnected(alice, bob)
|
||||||
|
|
||||||
// When updating the state of the channel between Alice and Bob, we
|
// When updating the state of the channel between Alice and Bob, we
|
||||||
// should expect to see channel updates with the default routing
|
// should expect to see channel updates with the default routing
|
||||||
// policy. The value of "Disabled" will depend on the specific
|
// policy. The value of "Disabled" will depend on the specific scenario
|
||||||
// scenario being tested.
|
// being tested.
|
||||||
expectedPolicy := &lnrpc.RoutingPolicy{
|
expectedPolicy := &lnrpc.RoutingPolicy{
|
||||||
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
|
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
|
||||||
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
|
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
|
||||||
@@ -164,83 +149,75 @@ func testUpdateChanStatus(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
MaxHtlcMsat: calculateMaxHtlc(chanAmt),
|
MaxHtlcMsat: calculateMaxHtlc(chanAmt),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initially, the channel between Alice and Bob should not be
|
|
||||||
// disabled.
|
|
||||||
assertEdgeDisabled(alice, chanPoint, false)
|
|
||||||
|
|
||||||
// Manually disable the channel and ensure that a "Disabled = true"
|
// Manually disable the channel and ensure that a "Disabled = true"
|
||||||
// update is propagated.
|
// update is propagated.
|
||||||
sendReq(alice, chanPoint, routerrpc.ChanStatusAction_DISABLE)
|
aliceSendReq(routerrpc.ChanStatusAction_DISABLE)
|
||||||
expectedPolicy.Disabled = true
|
expectedPolicy.Disabled = true
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
|
|
||||||
// Re-enable the channel and ensure that a "Disabled = false" update
|
// Re-enable the channel and ensure that a "Disabled = false" update is
|
||||||
// is propagated.
|
// propagated.
|
||||||
sendReq(alice, chanPoint, routerrpc.ChanStatusAction_ENABLE)
|
aliceSendReq(routerrpc.ChanStatusAction_ENABLE)
|
||||||
expectedPolicy.Disabled = false
|
expectedPolicy.Disabled = false
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
|
|
||||||
// Manually enabling a channel should NOT prevent subsequent
|
// Manually enabling a channel should NOT prevent subsequent
|
||||||
// disconnections from automatically disabling the channel again
|
// disconnections from automatically disabling the channel again (we
|
||||||
// (we don't want to clutter the network with channels that are
|
// don't want to clutter the network with channels that are falsely
|
||||||
// falsely advertised as enabled when they don't work).
|
// advertised as enabled when they don't work).
|
||||||
require.NoError(t.t, net.DisconnectNodes(alice, bob))
|
ht.DisconnectNodes(alice, bob)
|
||||||
expectedPolicy.Disabled = true
|
expectedPolicy.Disabled = true
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
|
|
||||||
// Reconnecting the nodes should propagate a "Disabled = false" update.
|
// Reconnecting the nodes should propagate a "Disabled = false" update.
|
||||||
net.EnsureConnected(t.t, alice, bob)
|
ht.EnsureConnected(alice, bob)
|
||||||
expectedPolicy.Disabled = false
|
expectedPolicy.Disabled = false
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
|
|
||||||
// Manually disabling the channel should prevent a subsequent
|
// Manually disabling the channel should prevent a subsequent
|
||||||
// disconnect / reconnect from re-enabling the channel on
|
// disconnect/reconnect from re-enabling the channel on Alice's end.
|
||||||
// Alice's end. Note the asymmetry between manual enable and
|
// Note the asymmetry between manual enable and manual disable!
|
||||||
// manual disable!
|
aliceSendReq(routerrpc.ChanStatusAction_DISABLE)
|
||||||
sendReq(alice, chanPoint, routerrpc.ChanStatusAction_DISABLE)
|
|
||||||
|
|
||||||
// Alice sends out the "Disabled = true" update in response to
|
// Alice sends out the "Disabled = true" update in response to the
|
||||||
// the ChanStatusAction_DISABLE request.
|
// ChanStatusAction_DISABLE request.
|
||||||
expectedPolicy.Disabled = true
|
expectedPolicy.Disabled = true
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
|
|
||||||
require.NoError(t.t, net.DisconnectNodes(alice, bob))
|
ht.DisconnectNodes(alice, bob)
|
||||||
|
|
||||||
// Bob sends a "Disabled = true" update upon detecting the
|
// Bob sends a "Disabled = true" update upon detecting the disconnect.
|
||||||
// disconnect.
|
|
||||||
expectedPolicy.Disabled = true
|
expectedPolicy.Disabled = true
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
|
|
||||||
// Bob sends a "Disabled = false" update upon detecting the
|
// Bob sends a "Disabled = false" update upon detecting the reconnect.
|
||||||
// reconnect.
|
ht.EnsureConnected(alice, bob)
|
||||||
net.EnsureConnected(t.t, alice, bob)
|
|
||||||
expectedPolicy.Disabled = false
|
expectedPolicy.Disabled = false
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
|
|
||||||
// However, since we manually disabled the channel on Alice's end,
|
// However, since we manually disabled the channel on Alice's end, the
|
||||||
// the policy on Alice's end should still be "Disabled = true". Again,
|
// policy on Alice's end should still be "Disabled = true". Again, note
|
||||||
// note the asymmetry between manual enable and manual disable!
|
// the asymmetry between manual enable and manual disable!
|
||||||
assertEdgeDisabled(alice, chanPoint, true)
|
assertEdgeDisabled(true)
|
||||||
|
|
||||||
require.NoError(t.t, net.DisconnectNodes(alice, bob))
|
ht.DisconnectNodes(alice, bob)
|
||||||
|
|
||||||
// Bob sends a "Disabled = true" update upon detecting the
|
// Bob sends a "Disabled = true" update upon detecting the disconnect.
|
||||||
// disconnect.
|
|
||||||
expectedPolicy.Disabled = true
|
expectedPolicy.Disabled = true
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
|
|
||||||
// After restoring automatic channel state management on Alice's end,
|
// After restoring automatic channel state management on Alice's end,
|
||||||
// BOTH Alice and Bob should set the channel state back to "enabled"
|
// BOTH Alice and Bob should set the channel state back to "enabled" on
|
||||||
// on reconnect.
|
// reconnect.
|
||||||
sendReq(alice, chanPoint, routerrpc.ChanStatusAction_AUTO)
|
aliceSendReq(routerrpc.ChanStatusAction_AUTO)
|
||||||
net.EnsureConnected(t.t, alice, bob)
|
ht.EnsureConnected(alice, bob)
|
||||||
|
|
||||||
expectedPolicy.Disabled = false
|
expectedPolicy.Disabled = false
|
||||||
assertChannelUpdate(alice, expectedPolicy)
|
assertChannelUpdate(alice, expectedPolicy)
|
||||||
assertChannelUpdate(bob, expectedPolicy)
|
assertChannelUpdate(bob, expectedPolicy)
|
||||||
assertEdgeDisabled(alice, chanPoint, false)
|
assertEdgeDisabled(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testUnannouncedChannels checks unannounced channels are not returned by
|
// testUnannouncedChannels checks unannounced channels are not returned by
|
||||||
|
|||||||
@@ -88,10 +88,6 @@ var allTestCases = []*testCase{
|
|||||||
name: "list channels",
|
name: "list channels",
|
||||||
test: testListChannels,
|
test: testListChannels,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "update channel status",
|
|
||||||
test: testUpdateChanStatus,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "test update node announcement rpc",
|
name: "test update node announcement rpc",
|
||||||
test: testUpdateNodeAnnouncement,
|
test: testUpdateNodeAnnouncement,
|
||||||
|
|||||||
Reference in new issue
Block a user