lnd test: add testUpdateChannelPolicy

This commit adds a new integration test, that checks that
policy/fee updates get propagated properly in the network,
such that the other nodes learn about the changes.
This commit is contained in:
Johan T. Halseth 2017-12-19 17:10:00 +01:00
parent f4f024aff2
commit d030773c8d
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -467,6 +467,290 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
}
// testUpdateChannelPolicy tests that policy updates made to a channel
// gets propagated to other nodes in the network.
func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
timeout := time.Duration(time.Second * 5)
ctxb := context.Background()
// Launch notification clients for all nodes, such that we can
// get notified when they discover new channels and updates
// in the graph.
aliceUpdates, aQuit := subscribeGraphNotifications(t, ctxb, net.Alice)
defer close(aQuit)
bobUpdates, bQuit := subscribeGraphNotifications(t, ctxb, net.Bob)
defer close(bQuit)
chanAmt := maxFundingAmount
pushAmt := btcutil.Amount(100000)
// Create a channel Alice->Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
// Create Carol and a new channel Bob->Carol.
carol, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create new nodes: %v", err)
}
carolUpdates, cQuit := subscribeGraphNotifications(t, ctxb, carol)
defer close(cQuit)
if err := net.ConnectNodes(ctxb, carol, net.Bob); err != nil {
t.Fatalf("unable to connect dave to alice: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint2 := openChannelAndAssert(ctxt, t, net, net.Bob, carol,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint2)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(ctxt, chanPoint2)
if err != nil {
t.Fatalf("carol didn't report channel: %v", err)
}
// Update the fees for the channel Alice->Bob, and make sure
// all nodes learn about it.
const feeBase = 1000000
baseFee := int64(1500)
feeRate := int64(12)
timeLockDelta := uint32(66)
req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
}
req.Scope = &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
}
_, err = net.Alice.UpdateChannelPolicy(ctxb, req)
if err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// txStr returns the string representation of the channel's
// funding tx.
txStr := func(chanPoint *lnrpc.ChannelPoint) string {
fundingTxID, err := chainhash.NewHash(chanPoint.FundingTxid)
if err != nil {
return ""
}
cp := wire.OutPoint{
Hash: *fundingTxID,
Index: chanPoint.OutputIndex,
}
return cp.String()
}
// A closure that is used to wait for a channel updates that matches
// the channel policy update done by Alice.
waitForChannelUpdate := func(graphUpdates chan *lnrpc.GraphTopologyUpdate,
chanPoints ...*lnrpc.ChannelPoint) {
// Create a map containing all the channel points we are
// waiting for updates for.
cps := make(map[string]bool)
for _, chanPoint := range chanPoints {
cps[txStr(chanPoint)] = true
}
Loop:
for {
select {
case graphUpdate := <-graphUpdates:
if len(graphUpdate.ChannelUpdates) == 0 {
continue
}
chanUpdate := graphUpdate.ChannelUpdates[0]
fundingTxStr := txStr(chanUpdate.ChanPoint)
if _, ok := cps[fundingTxStr]; !ok {
continue
}
if chanUpdate.AdvertisingNode != net.Alice.PubKeyStr {
continue
}
policy := chanUpdate.RoutingPolicy
if policy.FeeBaseMsat != baseFee {
continue
}
if policy.FeeRateMilliMsat != feeRate*feeBase {
continue
}
if policy.TimeLockDelta != timeLockDelta {
continue
}
// We got a policy update that matched the
// values and channel point of what we
// expected, delete it from the map.
delete(cps, fundingTxStr)
// If we have no more channel points we are
// waiting for, break out of the loop.
if len(cps) == 0 {
break Loop
}
case <-time.After(20 * time.Second):
t.Fatalf("did not receive channel update")
}
}
}
// Wait for all nodes to have seen the policy update done by Alice.
waitForChannelUpdate(aliceUpdates, chanPoint)
waitForChannelUpdate(bobUpdates, chanPoint)
waitForChannelUpdate(carolUpdates, chanPoint)
// assertChannelPolicy asserts that the passed node's known channel
// policy for the passed chanPoint is consistent with Alice's current
// expected policy values.
assertChannelPolicy := func(node *lntest.HarnessNode,
chanPoint *lnrpc.ChannelPoint) {
// Get a DescribeGraph from the node.
descReq := &lnrpc.ChannelGraphRequest{}
chanGraph, err := node.DescribeGraph(ctxb, descReq)
if err != nil {
t.Fatalf("unable to query for alice's routing table: %v",
err)
}
edgeFound := false
for _, e := range chanGraph.Edges {
if e.ChanPoint == txStr(chanPoint) {
edgeFound = true
if e.Node1Pub == net.Alice.PubKeyStr {
if e.Node1Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node1Policy.FeeBaseMsat)
}
if e.Node1Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node1Policy.FeeRateMilliMsat)
}
if e.Node1Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node1Policy.TimeLockDelta)
}
} else {
if e.Node2Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node2Policy.FeeBaseMsat)
}
if e.Node2Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node2Policy.FeeRateMilliMsat)
}
if e.Node2Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node2Policy.TimeLockDelta)
}
}
}
}
if !edgeFound {
t.Fatalf("did not find edge")
}
}
// Check that all nodes now know about Alice's updated policy.
assertChannelPolicy(net.Alice, chanPoint)
assertChannelPolicy(net.Bob, chanPoint)
assertChannelPolicy(carol, chanPoint)
// Open channel to Carol.
if err := net.ConnectNodes(ctxb, net.Alice, carol); err != nil {
t.Fatalf("unable to connect dave to alice: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint3 := openChannelAndAssert(ctxt, t, net, net.Alice, carol,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint3)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(ctxt, chanPoint3)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
// Make a global update, and check that both channels'
// new policies get propagated.
baseFee = int64(800)
feeRate = int64(123)
timeLockDelta = uint32(22)
req = &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
}
req.Scope = &lnrpc.PolicyUpdateRequest_Global{}
_, err = net.Alice.UpdateChannelPolicy(ctxb, req)
if err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// Wait for all nodes to have seen the policy updates
// for both of Alice's channels.
waitForChannelUpdate(aliceUpdates, chanPoint, chanPoint3)
waitForChannelUpdate(bobUpdates, chanPoint, chanPoint3)
waitForChannelUpdate(carolUpdates, chanPoint, chanPoint3)
// And finally check that all nodes remembers the policy
// update they received.
assertChannelPolicy(net.Alice, chanPoint)
assertChannelPolicy(net.Bob, chanPoint)
assertChannelPolicy(carol, chanPoint)
assertChannelPolicy(net.Alice, chanPoint3)
assertChannelPolicy(net.Bob, chanPoint3)
assertChannelPolicy(carol, chanPoint3)
// Close the channels.
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Bob, chanPoint2, false)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint3, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
// Clean up carol's node.
if err := net.ShutdownNode(carol); err != nil {
t.Fatalf("unable to shutdown carol: %v", err)
}
}
// testOpenChannelAfterReorg tests that in the case where we have an open
// channel where the funding tx gets reorged out, the channel will no
// longer be present in the node's routing table.
@ -793,7 +1077,7 @@ func testChannelFundingPersistence(net *lntest.NetworkHarness, t *harnessTest) {
// confirmation before it's open, with the current set of defaults,
// we'll need to create a new node instance.
const numConfs = 5
carolArgs := []string{fmt.Sprintf("--defaultchanconfs=%v", numConfs)}
carolArgs := []string{fmt.Sprintf("--bitcoin.defaultchanconfs=%v", numConfs)}
carol, err := net.NewNode(carolArgs)
if err != nil {
t.Fatalf("unable to create new node: %v", err)
@ -4690,6 +4974,10 @@ var testsCases = []*testCase{
name: "basic funding flow",
test: testBasicChannelFunding,
},
{
name: "update channel policy",
test: testUpdateChannelPolicy,
},
{
name: "open channel reorg test",
test: testOpenChannelAfterReorg,