mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
748 lines
24 KiB
Go
748 lines
24 KiB
Go
package itest
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
|
"github.com/lightningnetwork/lnd/funding"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/peersrpc"
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
|
"github.com/lightningnetwork/lnd/lntest"
|
|
"github.com/lightningnetwork/lnd/lntest/node"
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// testUpdateChanStatus checks that calls to the UpdateChanStatus RPC update
|
|
// the channel graph as expected, and that channel state is properly updated
|
|
// in the presence of interleaved node disconnects / reconnects.
|
|
//
|
|
// 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 *lntest.HarnessTest) {
|
|
// Prepare params.
|
|
chanAmt := btcutil.Amount(100_000)
|
|
openChannelParams := lntest.OpenChannelParams{
|
|
Amt: chanAmt,
|
|
}
|
|
cfg := []string{
|
|
"--minbackoff=60s",
|
|
"--chan-enable-timeout=3s",
|
|
"--chan-disable-timeout=6s",
|
|
"--chan-status-sample-interval=0.5s",
|
|
}
|
|
cfgs := [][]string{cfg, cfg}
|
|
|
|
// Create two fresh nodes and open a channel between them.
|
|
chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams)
|
|
chanPoint := chanPoints[0]
|
|
alice, bob := nodes[0], nodes[1]
|
|
|
|
// assertEdgeDisabled ensures that Alice has the correct Disabled state
|
|
// for given channel from her DescribeGraph.
|
|
assertEdgeDisabled := func(disabled bool) {
|
|
outPoint := ht.OutPointFromChannelPoint(chanPoint)
|
|
|
|
err := wait.NoError(func() error {
|
|
edge := ht.AssertNumEdges(alice, 1, true)[0]
|
|
if edge.ChanPoint != outPoint.String() {
|
|
return fmt.Errorf("expected chan_point %v, "+
|
|
"got %v", outPoint, edge.ChanPoint)
|
|
}
|
|
var policy *lnrpc.RoutingPolicy
|
|
if alice.PubKeyStr == edge.Node1Pub {
|
|
policy = edge.Node1Policy
|
|
} else {
|
|
policy = edge.Node2Policy
|
|
}
|
|
if disabled != policy.Disabled {
|
|
return fmt.Errorf("expected policy.Disabled "+
|
|
"to be %v, but policy was %v", disabled,
|
|
policy.Disabled)
|
|
}
|
|
|
|
return nil
|
|
}, defaultTimeout)
|
|
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
|
|
// should expect to see channel updates with the default routing
|
|
// policy. The value of "Disabled" will depend on the specific scenario
|
|
// being tested.
|
|
expectedPolicy := &lnrpc.RoutingPolicy{
|
|
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
|
|
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
|
|
TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
|
|
MinHtlc: 1000, // default value
|
|
MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt),
|
|
}
|
|
|
|
// Manually disable the channel and ensure that a "Disabled = true"
|
|
// update is propagated.
|
|
aliceSendReq(routerrpc.ChanStatusAction_DISABLE)
|
|
expectedPolicy.Disabled = true
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
|
|
// Re-enable the channel and ensure that a "Disabled = false" update is
|
|
// propagated.
|
|
aliceSendReq(routerrpc.ChanStatusAction_ENABLE)
|
|
expectedPolicy.Disabled = false
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
|
|
// Manually enabling a channel should NOT prevent subsequent
|
|
// disconnections from automatically disabling the channel again (we
|
|
// don't want to clutter the network with channels that are falsely
|
|
// advertised as enabled when they don't work).
|
|
ht.DisconnectNodes(alice, bob)
|
|
expectedPolicy.Disabled = true
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
|
|
// Reconnecting the nodes should propagate a "Disabled = false" update.
|
|
ht.EnsureConnected(alice, bob)
|
|
expectedPolicy.Disabled = false
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
|
|
// Manually disabling the channel should prevent a subsequent
|
|
// disconnect/reconnect from re-enabling the channel on Alice's end.
|
|
// Note the asymmetry between manual enable and manual disable!
|
|
aliceSendReq(routerrpc.ChanStatusAction_DISABLE)
|
|
|
|
// Alice sends out the "Disabled = true" update in response to the
|
|
// ChanStatusAction_DISABLE request.
|
|
expectedPolicy.Disabled = true
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
|
|
ht.DisconnectNodes(alice, bob)
|
|
|
|
// Bob sends a "Disabled = true" update upon detecting the disconnect.
|
|
expectedPolicy.Disabled = true
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
|
|
// Bob sends a "Disabled = false" update upon detecting the reconnect.
|
|
ht.EnsureConnected(alice, bob)
|
|
expectedPolicy.Disabled = false
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
|
|
// However, since we manually disabled the channel on Alice's end, the
|
|
// policy on Alice's end should still be "Disabled = true". Again, note
|
|
// the asymmetry between manual enable and manual disable!
|
|
assertEdgeDisabled(true)
|
|
|
|
ht.DisconnectNodes(alice, bob)
|
|
|
|
// Bob sends a "Disabled = true" update upon detecting the disconnect.
|
|
expectedPolicy.Disabled = true
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
|
|
// After restoring automatic channel state management on Alice's end,
|
|
// BOTH Alice and Bob should set the channel state back to "enabled" on
|
|
// reconnect.
|
|
aliceSendReq(routerrpc.ChanStatusAction_AUTO)
|
|
ht.EnsureConnected(alice, bob)
|
|
|
|
expectedPolicy.Disabled = false
|
|
assertChannelUpdate(alice, expectedPolicy)
|
|
assertChannelUpdate(bob, expectedPolicy)
|
|
assertEdgeDisabled(false)
|
|
}
|
|
|
|
// testUnannouncedChannels checks unannounced channels are not returned by
|
|
// describeGraph RPC request unless explicitly asked for.
|
|
func testUnannouncedChannels(ht *lntest.HarnessTest) {
|
|
amount := funding.MaxBtcFundingAmount
|
|
alice, bob := ht.Alice, ht.Bob
|
|
|
|
// Open a channel between Alice and Bob, ensuring the
|
|
// channel has been opened properly.
|
|
chanOpenUpdate := ht.OpenChannelAssertStream(
|
|
alice, bob, lntest.OpenChannelParams{Amt: amount},
|
|
)
|
|
|
|
// Mine 2 blocks, and check that the channel is opened but not yet
|
|
// announced to the network.
|
|
ht.MineBlocksAndAssertNumTxes(2, 1)
|
|
|
|
// One block is enough to make the channel ready for use, since the
|
|
// nodes have defaultNumConfs=1 set.
|
|
fundingChanPoint := ht.WaitForChannelOpenEvent(chanOpenUpdate)
|
|
|
|
// Alice should have 1 edge in her graph.
|
|
ht.AssertNumActiveEdges(alice, 1, true)
|
|
|
|
// Channels should not be announced yet, hence Alice should have no
|
|
// announced edges in her graph.
|
|
ht.AssertNumActiveEdges(alice, 0, false)
|
|
|
|
// Mine 4 more blocks, and check that the channel is now announced.
|
|
ht.MineBlocks(4)
|
|
|
|
// Give the network a chance to learn that auth proof is confirmed.
|
|
ht.AssertNumActiveEdges(alice, 1, false)
|
|
|
|
// Close the channel used during the test.
|
|
ht.CloseChannel(alice, fundingChanPoint)
|
|
}
|
|
|
|
func testGraphTopologyNotifications(ht *lntest.HarnessTest) {
|
|
ht.Run("pinned", func(t *testing.T) {
|
|
subT := ht.Subtest(t)
|
|
testGraphTopologyNtfns(subT, true)
|
|
})
|
|
ht.Run("unpinned", func(t *testing.T) {
|
|
subT := ht.Subtest(t)
|
|
testGraphTopologyNtfns(subT, false)
|
|
})
|
|
}
|
|
|
|
func testGraphTopologyNtfns(ht *lntest.HarnessTest, pinned bool) {
|
|
const chanAmt = funding.MaxBtcFundingAmount
|
|
|
|
// Spin up Bob first, since we will need to grab his pubkey when
|
|
// starting Alice to test pinned syncing.
|
|
bob := ht.Bob
|
|
bobInfo := bob.RPC.GetInfo()
|
|
bobPubkey := bobInfo.IdentityPubkey
|
|
|
|
// Restart Bob as he may have leftover announcements from previous
|
|
// tests, causing the graph to be unsynced.
|
|
ht.RestartNodeWithExtraArgs(bob, nil)
|
|
|
|
// For unpinned syncing, start Alice as usual. Otherwise grab Bob's
|
|
// pubkey to include in his pinned syncer set.
|
|
var aliceArgs []string
|
|
if pinned {
|
|
aliceArgs = []string{
|
|
"--numgraphsyncpeers=0",
|
|
fmt.Sprintf("--gossip.pinned-syncers=%s", bobPubkey),
|
|
}
|
|
}
|
|
|
|
alice := ht.Alice
|
|
ht.RestartNodeWithExtraArgs(alice, aliceArgs)
|
|
|
|
// Connect Alice and Bob.
|
|
ht.EnsureConnected(alice, bob)
|
|
|
|
// Assert that Bob has the correct sync type before proceeding.
|
|
if pinned {
|
|
assertSyncType(ht, alice, bobPubkey, lnrpc.Peer_PINNED_SYNC)
|
|
} else {
|
|
assertSyncType(ht, alice, bobPubkey, lnrpc.Peer_ACTIVE_SYNC)
|
|
}
|
|
|
|
// Regardless of syncer type, ensure that both peers report having
|
|
// completed their initial sync before continuing to make a channel.
|
|
ht.WaitForGraphSync(alice)
|
|
|
|
// Open a new channel between Alice and Bob.
|
|
chanPoint := ht.OpenChannel(
|
|
alice, bob, lntest.OpenChannelParams{Amt: chanAmt},
|
|
)
|
|
|
|
// The channel opening above should have triggered a few notifications
|
|
// sent to the notification client. We'll expect two channel updates,
|
|
// and two node announcements.
|
|
ht.AssertNumChannelUpdates(alice, chanPoint, 2)
|
|
ht.AssertNumNodeAnns(alice, alice.PubKeyStr, 1)
|
|
ht.AssertNumNodeAnns(alice, bob.PubKeyStr, 1)
|
|
|
|
blockHeight := ht.CurrentHeight()
|
|
|
|
// Now we'll test that updates are properly sent after channels are
|
|
// closed within the network.
|
|
ht.CloseChannel(alice, chanPoint)
|
|
|
|
// Now that the channel has been closed, we should receive a
|
|
// notification indicating so.
|
|
closedChan := ht.AssertTopologyChannelClosed(alice, chanPoint)
|
|
|
|
require.Equal(ht, blockHeight+1, closedChan.ClosedHeight,
|
|
"close heights of channel mismatch")
|
|
|
|
fundingTxid := ht.OutPointFromChannelPoint(chanPoint)
|
|
closeTxid := ht.OutPointFromChannelPoint(closedChan.ChanPoint)
|
|
require.EqualValues(ht, fundingTxid, closeTxid,
|
|
"channel point hash mismatch")
|
|
|
|
// For the final portion of the test, we'll ensure that once a new node
|
|
// appears in the network, the proper notification is dispatched. Note
|
|
// that a node that does not have any channels open is ignored, so
|
|
// first we disconnect Alice and Bob, open a channel between Bob and
|
|
// Carol, and finally connect Alice to Bob again.
|
|
ht.DisconnectNodes(bob, alice)
|
|
|
|
// Since Alice and Bob has a permanent connection, the above
|
|
// disconnection won't be enough as Alice will try to reconnect to Bob
|
|
// again. Atm, it seems nothing is stopping the reconnection. So we
|
|
// need to shutdown Alice here.
|
|
//
|
|
// TODO(yy): clearly define what `disconnectpeer` rpc is responsible
|
|
// for and its effect. If we disconnect a peer, we shouldn't allow the
|
|
// peer to connect to us again.
|
|
restartAlice := ht.SuspendNode(alice)
|
|
|
|
carol := ht.NewNode("Carol", nil)
|
|
ht.ConnectNodes(bob, carol)
|
|
chanPoint = ht.OpenChannel(
|
|
bob, carol, lntest.OpenChannelParams{Amt: chanAmt},
|
|
)
|
|
|
|
// Restart Alice so she can receive the channel updates from Bob.
|
|
require.NoError(ht, restartAlice(), "failed to restart Alice")
|
|
|
|
// Reconnect Alice and Bob. This should result in the nodes syncing up
|
|
// their respective graph state, with the new addition being the
|
|
// existence of Carol in the graph, and also the channel between Bob
|
|
// and Carol. Note that we will also receive a node announcement from
|
|
// Bob, since a node will update its node announcement after a new
|
|
// channel is opened.
|
|
ht.EnsureConnected(alice, bob)
|
|
|
|
// We should receive an update advertising the newly connected node,
|
|
// Bob's new node announcement, and the channel between Bob and Carol.
|
|
ht.AssertNumChannelUpdates(alice, chanPoint, 2)
|
|
ht.AssertNumNodeAnns(alice, bob.PubKeyStr, 1)
|
|
|
|
// Close the channel between Bob and Carol.
|
|
ht.CloseChannel(bob, chanPoint)
|
|
}
|
|
|
|
// testNodeAnnouncement ensures that when a node is started with one or more
|
|
// external IP addresses specified on the command line, that those addresses
|
|
// announced to the network and reported in the network graph.
|
|
func testNodeAnnouncement(ht *lntest.HarnessTest) {
|
|
alice, bob := ht.Alice, ht.Bob
|
|
|
|
advertisedAddrs := []string{
|
|
"192.168.1.1:8333",
|
|
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337",
|
|
"bkb6azqggsaiskzi.onion:9735",
|
|
"fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swj" +
|
|
"wid.onion:1234",
|
|
}
|
|
|
|
var lndArgs []string
|
|
for _, addr := range advertisedAddrs {
|
|
lndArgs = append(lndArgs, "--externalip="+addr)
|
|
}
|
|
|
|
dave := ht.NewNode("Dave", lndArgs)
|
|
|
|
// We must let Dave have an open channel before he can send a node
|
|
// announcement, so we open a channel with Bob,
|
|
ht.ConnectNodes(bob, dave)
|
|
|
|
// We'll then go ahead and open a channel between Bob and Dave. This
|
|
// ensures that Alice receives the node announcement from Bob as part of
|
|
// the announcement broadcast.
|
|
chanPoint := ht.OpenChannel(
|
|
bob, dave, lntest.OpenChannelParams{Amt: 1000000},
|
|
)
|
|
|
|
assertAddrs := func(addrsFound []string, targetAddrs ...string) {
|
|
addrs := make(map[string]struct{}, len(addrsFound))
|
|
for _, addr := range addrsFound {
|
|
addrs[addr] = struct{}{}
|
|
}
|
|
|
|
for _, addr := range targetAddrs {
|
|
_, ok := addrs[addr]
|
|
require.True(ht, ok, "address %v not found in node "+
|
|
"announcement", addr)
|
|
}
|
|
}
|
|
// We'll then wait for Alice to receive Dave's node announcement
|
|
// including the expected advertised addresses from Bob since they
|
|
// should already be connected.
|
|
allUpdates := ht.AssertNumNodeAnns(alice, dave.PubKeyStr, 1)
|
|
nodeUpdate := allUpdates[len(allUpdates)-1]
|
|
assertAddrs(nodeUpdate.Addresses, advertisedAddrs...)
|
|
|
|
// Close the channel between Bob and Dave.
|
|
ht.CloseChannel(bob, chanPoint)
|
|
}
|
|
|
|
// testUpdateNodeAnnouncement ensures that the RPC endpoint validates
|
|
// the requests correctly and that the new node announcement is brodcasted
|
|
// with the right information after updating our node.
|
|
func testUpdateNodeAnnouncement(ht *lntest.HarnessTest) {
|
|
alice, bob := ht.Alice, ht.Bob
|
|
|
|
var lndArgs []string
|
|
|
|
// Add some exta addresses to the default ones.
|
|
extraAddrs := []string{
|
|
"192.168.1.1:8333",
|
|
"[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8337",
|
|
"bkb6azqggsaiskzi.onion:9735",
|
|
"fomvuglh6h6vcag73xo5t5gv56ombih3zr2xvplkpbfd7wrog4swj" +
|
|
"wid.onion:1234",
|
|
}
|
|
for _, addr := range extraAddrs {
|
|
lndArgs = append(lndArgs, "--externalip="+addr)
|
|
}
|
|
dave := ht.NewNode("Dave", lndArgs)
|
|
|
|
// assertNodeAnn is a helper closure that checks a given node update
|
|
// from Dave is seen by Alice.
|
|
assertNodeAnn := func(expected *lnrpc.NodeUpdate) {
|
|
err := wait.NoError(func() error {
|
|
// Get a list of node updates seen by Alice.
|
|
updates := alice.Watcher.GetNodeUpdates(dave.PubKeyStr)
|
|
|
|
// Check at least one of the updates matches the given
|
|
// node update.
|
|
for _, update := range updates {
|
|
err := compareNodeAnns(update, expected)
|
|
// Found a match, return nil.
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// We've check all the updates and no match found.
|
|
return fmt.Errorf("alice didn't see the update: %v",
|
|
expected)
|
|
}, defaultTimeout)
|
|
|
|
require.NoError(ht, err, "assertNodeAnn failed")
|
|
}
|
|
|
|
// Get dave default information so we can compare it lately with the
|
|
// brodcasted updates.
|
|
resp := dave.RPC.GetInfo()
|
|
defaultAddrs := make([]*lnrpc.NodeAddress, 0, len(resp.Uris))
|
|
for _, uri := range resp.GetUris() {
|
|
values := strings.Split(uri, "@")
|
|
defaultAddrs = append(
|
|
defaultAddrs, &lnrpc.NodeAddress{
|
|
Addr: values[1],
|
|
Network: "tcp",
|
|
},
|
|
)
|
|
}
|
|
|
|
// Test that we can't modify a feature bit that is known to LND.
|
|
reservedFeatureBit := lnrpc.FeatureBit_WUMBO_CHANNELS_REQ
|
|
_, known := lnwire.Features[lnwire.FeatureBit(reservedFeatureBit)]
|
|
require.True(ht, known, "feature bit should be known to lnd")
|
|
|
|
nodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{
|
|
FeatureUpdates: []*peersrpc.UpdateFeatureAction{
|
|
{
|
|
Action: peersrpc.UpdateAction_ADD,
|
|
FeatureBit: reservedFeatureBit,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Node announcement should fail because we're not allowed to alter
|
|
// "known" feature bits.
|
|
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
|
|
|
|
// We also set an unknown feature bit (which we should be able to
|
|
// set/unset) and test that we can update it accordingly.
|
|
featureBit := lnrpc.FeatureBit(999)
|
|
_, known = lnwire.Features[lnwire.FeatureBit(featureBit)]
|
|
require.False(ht, known, "feature bit should be unknown to lnd")
|
|
featureIdx := uint32(featureBit)
|
|
_, ok := resp.Features[featureIdx]
|
|
require.False(ht, ok, "unexpected feature bit enabled by default")
|
|
|
|
defaultDaveNodeAnn := &lnrpc.NodeUpdate{
|
|
Alias: resp.Alias,
|
|
Color: resp.Color,
|
|
NodeAddresses: defaultAddrs,
|
|
}
|
|
|
|
// Dave must have an open channel before he can send a node
|
|
// announcement, so we open a channel with Bob.
|
|
ht.ConnectNodes(bob, dave)
|
|
|
|
// Go ahead and open a channel between Bob and Dave. This
|
|
// ensures that Alice receives the node announcement from Bob as part of
|
|
// the announcement broadcast.
|
|
chanPoint := ht.OpenChannel(
|
|
bob, dave, lntest.OpenChannelParams{
|
|
Amt: 1000000,
|
|
},
|
|
)
|
|
|
|
// Wait for Alice to receive dave's node announcement with the default
|
|
// values.
|
|
assertNodeAnn(defaultDaveNodeAnn)
|
|
|
|
// We cannot differentiate between requests with Alias = "" and
|
|
// requests that do not provide that field. If a user sets Alias = ""
|
|
// in the request the field will simply be ignored. The request must
|
|
// fail because no modifiers are applied.
|
|
invalidNodeAnnReq := &peersrpc.NodeAnnouncementUpdateRequest{Alias: ""}
|
|
dave.RPC.UpdateNodeAnnouncementErr(invalidNodeAnnReq)
|
|
|
|
// Alias too long.
|
|
invalidNodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
|
|
Alias: strings.Repeat("a", 50),
|
|
}
|
|
dave.RPC.UpdateNodeAnnouncementErr(invalidNodeAnnReq)
|
|
|
|
// Update Node.
|
|
newAlias := "new-alias"
|
|
newColor := "#2288ee"
|
|
|
|
newAddresses := []string{
|
|
"192.168.1.10:8333",
|
|
"192.168.1.11:8333",
|
|
}
|
|
|
|
updateAddressActions := []*peersrpc.UpdateAddressAction{
|
|
{
|
|
Action: peersrpc.UpdateAction_ADD,
|
|
Address: newAddresses[0],
|
|
},
|
|
{
|
|
Action: peersrpc.UpdateAction_ADD,
|
|
Address: newAddresses[1],
|
|
},
|
|
{
|
|
Action: peersrpc.UpdateAction_REMOVE,
|
|
Address: defaultAddrs[0].Addr,
|
|
},
|
|
}
|
|
|
|
updateFeatureActions := []*peersrpc.UpdateFeatureAction{
|
|
{
|
|
Action: peersrpc.UpdateAction_ADD,
|
|
FeatureBit: featureBit,
|
|
},
|
|
}
|
|
|
|
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
|
|
Alias: newAlias,
|
|
Color: newColor,
|
|
AddressUpdates: updateAddressActions,
|
|
FeatureUpdates: updateFeatureActions,
|
|
}
|
|
|
|
response := dave.RPC.UpdateNodeAnnouncement(nodeAnnReq)
|
|
|
|
expectedOps := map[string]int{
|
|
"features": 1,
|
|
"color": 1,
|
|
"alias": 1,
|
|
"addresses": 3,
|
|
}
|
|
assertUpdateNodeAnnouncementResponse(ht, response, expectedOps)
|
|
|
|
newNodeAddresses := []*lnrpc.NodeAddress{}
|
|
// We removed the first address.
|
|
newNodeAddresses = append(newNodeAddresses, defaultAddrs[1:]...)
|
|
newNodeAddresses = append(
|
|
newNodeAddresses,
|
|
&lnrpc.NodeAddress{Addr: newAddresses[0], Network: "tcp"},
|
|
&lnrpc.NodeAddress{Addr: newAddresses[1], Network: "tcp"},
|
|
)
|
|
|
|
// After updating the node we expect the update to contain
|
|
// the requested color, requested alias and the new added addresses.
|
|
newDaveNodeAnn := &lnrpc.NodeUpdate{
|
|
Alias: newAlias,
|
|
Color: newColor,
|
|
NodeAddresses: newNodeAddresses,
|
|
}
|
|
|
|
// We'll then wait for Alice to receive dave's node announcement
|
|
// with the new values.
|
|
assertNodeAnn(newDaveNodeAnn)
|
|
|
|
// Check that the feature bit was set correctly.
|
|
resp = dave.RPC.GetInfo()
|
|
_, ok = resp.Features[featureIdx]
|
|
require.True(ht, ok, "failed to set feature bit")
|
|
|
|
// Check that we cannot set a feature bit that is already set.
|
|
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
|
|
FeatureUpdates: updateFeatureActions,
|
|
}
|
|
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
|
|
|
|
// Check that we can unset feature bits.
|
|
updateFeatureActions = []*peersrpc.UpdateFeatureAction{
|
|
{
|
|
Action: peersrpc.UpdateAction_REMOVE,
|
|
FeatureBit: featureBit,
|
|
},
|
|
}
|
|
|
|
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
|
|
FeatureUpdates: updateFeatureActions,
|
|
}
|
|
response = dave.RPC.UpdateNodeAnnouncement(nodeAnnReq)
|
|
|
|
expectedOps = map[string]int{
|
|
"features": 1,
|
|
}
|
|
assertUpdateNodeAnnouncementResponse(ht, response, expectedOps)
|
|
|
|
resp = dave.RPC.GetInfo()
|
|
_, ok = resp.Features[featureIdx]
|
|
require.False(ht, ok, "failed to unset feature bit")
|
|
|
|
// Check that we cannot unset a feature bit that is already unset.
|
|
nodeAnnReq = &peersrpc.NodeAnnouncementUpdateRequest{
|
|
FeatureUpdates: updateFeatureActions,
|
|
}
|
|
dave.RPC.UpdateNodeAnnouncementErr(nodeAnnReq)
|
|
|
|
// Close the channel between Bob and Dave.
|
|
ht.CloseChannel(bob, chanPoint)
|
|
}
|
|
|
|
// assertSyncType asserts that the peer has an expected syncType.
|
|
//
|
|
// NOTE: only made for tests in this file.
|
|
func assertSyncType(ht *lntest.HarnessTest, hn *node.HarnessNode,
|
|
peer string, syncType lnrpc.Peer_SyncType) {
|
|
|
|
resp := hn.RPC.ListPeers()
|
|
for _, rpcPeer := range resp.Peers {
|
|
if rpcPeer.PubKey != peer {
|
|
continue
|
|
}
|
|
|
|
require.Equal(ht, syncType, rpcPeer.SyncType)
|
|
|
|
return
|
|
}
|
|
|
|
ht.Fatalf("unable to find peer: %s", peer)
|
|
}
|
|
|
|
// compareNodeAnns compares that two node announcements match or returns an
|
|
// error.
|
|
//
|
|
// NOTE: only used for tests in this file.
|
|
func compareNodeAnns(n1, n2 *lnrpc.NodeUpdate) error {
|
|
// Alias should match.
|
|
if n1.Alias != n2.Alias {
|
|
return fmt.Errorf("alias not match")
|
|
}
|
|
|
|
// Color should match.
|
|
if n1.Color != n2.Color {
|
|
return fmt.Errorf("color not match")
|
|
}
|
|
|
|
// NodeAddresses should match.
|
|
if len(n1.NodeAddresses) != len(n2.NodeAddresses) {
|
|
return fmt.Errorf("node addresses don't match")
|
|
}
|
|
|
|
addrs := make(map[string]struct{}, len(n1.NodeAddresses))
|
|
for _, nodeAddr := range n1.NodeAddresses {
|
|
addrs[nodeAddr.Addr] = struct{}{}
|
|
}
|
|
|
|
for _, nodeAddr := range n2.NodeAddresses {
|
|
if _, ok := addrs[nodeAddr.Addr]; !ok {
|
|
return fmt.Errorf("address %v not found in node "+
|
|
"announcement", nodeAddr.Addr)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// assertUpdateNodeAnnouncementResponse is a helper function to assert
|
|
// the response expected values.
|
|
//
|
|
// NOTE: only used for tests in this file.
|
|
func assertUpdateNodeAnnouncementResponse(ht *lntest.HarnessTest,
|
|
response *peersrpc.NodeAnnouncementUpdateResponse,
|
|
expectedOps map[string]int) {
|
|
|
|
require.Equal(
|
|
ht, len(response.Ops), len(expectedOps),
|
|
"unexpected number of Ops updating dave's node announcement",
|
|
)
|
|
|
|
ops := make(map[string]int, len(response.Ops))
|
|
for _, op := range response.Ops {
|
|
ops[op.Entity] = len(op.Actions)
|
|
}
|
|
|
|
for k, v := range expectedOps {
|
|
if v != ops[k] {
|
|
ht.Fatalf("unexpected number of actions for operation "+
|
|
"%s: got %d wanted %d", k, ops[k], v)
|
|
}
|
|
}
|
|
}
|