diff --git a/lntest/harness_net.go b/lntest/harness_net.go index d56b979b2..358a1c0b3 100644 --- a/lntest/harness_net.go +++ b/lntest/harness_net.go @@ -92,7 +92,7 @@ type NetworkHarness struct { // NewNetworkHarness creates a new network test harness. // TODO(roasbeef): add option to use golang's build library to a binary of the // current repo. This will save developers from having to manually `go install` -// within the repo each time before changes +// within the repo each time before changes. func NewNetworkHarness(m *HarnessMiner, b BackendConfig, lndBinary string, dbBackend DatabaseBackend) (*NetworkHarness, error) { @@ -240,6 +240,7 @@ out: if aliceResp.ConfirmedBalance == expectedBalance && bobResp.ConfirmedBalance == expectedBalance { + break out } case <-balanceTimeout: @@ -764,7 +765,7 @@ func (n *NetworkHarness) connectNodes(t *testing.T, a, b *HarnessNode, } // DisconnectNodes disconnects node a from node b by sending RPC message -// from a node to b node +// from a node to b node. func (n *NetworkHarness) DisconnectNodes(a, b *HarnessNode) error { ctx, cancel := context.WithTimeout(n.runCtx, DefaultTimeout) defer cancel() @@ -998,6 +999,26 @@ type OpenChannelParams struct { // ScidAlias denotes whether the channel will be an option-scid-alias // channel type negotiation. ScidAlias bool + + // BaseFee is the channel base fee applied during the channel + // announcement phase. + BaseFee uint64 + + // FeeRate is the channel fee rate in ppm applied during the channel + // announcement phase. + FeeRate uint64 + + // UseBaseFee, if set, instructs the downstream logic to apply the + // user-specified channel base fee to the channel update announcement. + // If set to false it avoids applying a base fee of 0 and instead + // activates the default configured base fee. + UseBaseFee bool + + // UseFeeRate, if set, instructs the downstream logic to apply the + // user-specified channel fee rate to the channel update announcement. + // If set to false it avoids applying a fee rate of 0 and instead + // activates the default configured fee rate. + UseFeeRate bool } // OpenChannel attempts to open a channel between srcNode and destNode with the @@ -1038,6 +1059,10 @@ func (n *NetworkHarness) OpenChannel(srcNode, destNode *HarnessNode, CommitmentType: p.CommitmentType, ZeroConf: p.ZeroConf, ScidAlias: p.ScidAlias, + BaseFee: p.BaseFee, + FeeRate: p.FeeRate, + UseBaseFee: p.UseBaseFee, + UseFeeRate: p.UseFeeRate, } // We need to use n.runCtx here to keep the response stream alive after @@ -1223,6 +1248,7 @@ func (n *NetworkHarness) CloseChannel(lnNode *HarnessNode, // not. filterChannel := func(node *HarnessNode, op wire.OutPoint) (*lnrpc.Channel, error) { + listResp, err := node.ListChannels(ctxt, listReq) if err != nil { return nil, err diff --git a/lntest/harness_node.go b/lntest/harness_node.go index cc7a5fc86..79bacb822 100644 --- a/lntest/harness_node.go +++ b/lntest/harness_node.go @@ -1330,7 +1330,7 @@ func (hn *HarnessNode) lightningNetworkWatcher() { // WaitForNetworkChannelOpen will block until a channel with the target // outpoint is seen as being fully advertised within the network. A channel is -// considered "fully advertised" once both of its directional edges has been +// considered "fully advertised" once both of its directional edges have been // advertised within the test Lightning Network. func (hn *HarnessNode) WaitForNetworkChannelOpen( chanPoint *lnrpc.ChannelPoint) error { diff --git a/lntest/itest/lnd_channel_policy_test.go b/lntest/itest/lnd_channel_policy_test.go index cc25714bc..857fe0a1e 100644 --- a/lntest/itest/lnd_channel_policy_test.go +++ b/lntest/itest/lnd_channel_policy_test.go @@ -30,7 +30,7 @@ func assertPolicyUpdate(t *harnessTest, nodes []*lntest.HarnessNode, } // testUpdateChannelPolicy tests that policy updates made to a channel -// gets propagated to other nodes in the network. +// get propagated to other nodes in the network. func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) { ctxb := context.Background() @@ -466,7 +466,8 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) { t, []*lntest.HarnessNode{net.Alice, net.Bob}, net.Alice.PubKeyStr, expectedPolicy, chanPoint3, ) - // Check that all nodes remembers the policy update + + // Check that all nodes remember the policy update // they received. assertChannelPolicy( t, net.Alice, net.Alice.PubKeyStr, diff --git a/lntest/itest/lnd_open_channel_test.go b/lntest/itest/lnd_open_channel_test.go index 3b579214e..0a2d0b90b 100644 --- a/lntest/itest/lnd_open_channel_test.go +++ b/lntest/itest/lnd_open_channel_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/integration/rpctest" + "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" @@ -226,13 +227,15 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) chanGraph, err = net.Alice.DescribeGraph(ctxt, req) if err != nil { - predErr = fmt.Errorf("unable to query for alice's routing table: %v", err) + predErr = fmt.Errorf("unable to query for "+ + "alice's routing table: %v", err) return false } numEdges = len(chanGraph.Edges) if numEdges != 0 { - predErr = fmt.Errorf("expected to find no edge in the graph, found %d", + predErr = fmt.Errorf("expected to find "+ + "no edge in the graph, found %d", numEdges) return false } @@ -249,10 +252,161 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { closeReorgedChannelAndAssert(t, net, net.Alice, chanPoint, false) } -// testBasicChannelCreationAndUpdates tests multiple channel opening and closing, -// and ensures that if a node is subscribed to channel updates they will be -// received correctly for both cooperative and force closed channels. -func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTest) { +// testOpenChannelFeePolicy checks if different channel fee scenarios +// are correctly handled when the optional channel fee parameters +// baseFee and feeRate are provided. If the OpenChannelRequest is not +// provided with a value for baseFee/feeRate the expectation is that the +// default baseFee/feeRate is applied. +// 1.) no params provided to OpenChannelRequest +// ChannelUpdate --> defaultBaseFee, defaultFeeRate +// 2.) only baseFee provided to OpenChannelRequest +// ChannelUpdate --> provided baseFee, defaultFeeRate +// 3.) only feeRate provided to OpenChannelRequest +// ChannelUpdate --> defaultBaseFee, provided FeeRate +// 4.) baseFee and feeRate provided to OpenChannelRequest +// ChannelUpdate --> provided baseFee, provided feeRate. +func testOpenChannelUpdateFeePolicy(net *lntest.NetworkHarness, + t *harnessTest) { + + const ( + defaultBaseFee = 1000 + defaultFeeRate = 1 + defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta + defaultMinHtlc = 1000 + optionalBaseFee = 1337 + optionalFeeRate = 1337 + ) + + defaultMaxHtlc := calculateMaxHtlc(funding.MaxBtcFundingAmount) + + chanAmt := funding.MaxBtcFundingAmount + pushAmt := chanAmt / 2 + + feeScenarios := []lntest.OpenChannelParams{ + { + Amt: chanAmt, + PushAmt: pushAmt, + UseBaseFee: false, + UseFeeRate: false, + }, + { + Amt: chanAmt, + PushAmt: pushAmt, + BaseFee: optionalBaseFee, + UseBaseFee: true, + UseFeeRate: false, + }, + { + Amt: chanAmt, + PushAmt: pushAmt, + FeeRate: optionalFeeRate, + UseBaseFee: false, + UseFeeRate: true, + }, + { + Amt: chanAmt, + PushAmt: pushAmt, + BaseFee: optionalBaseFee, + FeeRate: optionalFeeRate, + UseBaseFee: true, + UseFeeRate: true, + }, + } + + expectedPolicies := []lnrpc.RoutingPolicy{ + { + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + }, + { + FeeBaseMsat: optionalBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + }, + { + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: optionalFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + }, + { + FeeBaseMsat: optionalBaseFee, + FeeRateMilliMsat: optionalFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + }, + } + + bobExpectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } + + for i, feeScenario := range feeScenarios { + // Create a channel Alice->Bob. + chanPoint := openChannelAndAssert( + t, net, net.Alice, net.Bob, + feeScenario, + ) + + defer closeChannelAndAssert(t, net, net.Alice, chanPoint, false) + + // We add all the nodes' update channels to a slice, such that we can + // make sure they all receive the expected updates. + nodes := []*lntest.HarnessNode{net.Alice, net.Bob} + + // Alice and Bob should see each other's ChannelUpdates, advertising + // the preferred routing policies. + assertPolicyUpdate( + t, nodes, net.Alice.PubKeyStr, + &expectedPolicies[i], chanPoint, + ) + assertPolicyUpdate( + t, nodes, net.Bob.PubKeyStr, + &bobExpectedPolicy, chanPoint, + ) + + // They should now know about the default policies. + for _, node := range nodes { + assertChannelPolicy( + t, node, net.Alice.PubKeyStr, + &expectedPolicies[i], chanPoint, + ) + assertChannelPolicy( + t, node, net.Bob.PubKeyStr, + &bobExpectedPolicy, chanPoint, + ) + } + + require.NoError( + t.t, net.Alice.WaitForNetworkChannelOpen(chanPoint), + "alice reports channel opening", + ) + + require.NoError( + t.t, net.Bob.WaitForNetworkChannelOpen(chanPoint), + "bob reports channel opening", + ) + } +} + +// testBasicChannelCreationAndUpdates tests multiple channel opening and +// closing, and ensures that if a node is subscribed to channel updates +// they will be received correctly for both cooperative and force closed +// channels. +func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, + t *harnessTest) { + runBasicChannelCreationAndUpdates(net, t, net.Alice, net.Bob) } diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index ac2adad83..8d12f859b 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -443,4 +443,8 @@ var allTestCases = []*testCase{ name: "trackpayments", test: testTrackPayments, }, + { + name: "open channel fee policy", + test: testOpenChannelUpdateFeePolicy, + }, }