lnd/lntest/itest/lnd_channel_policy_test.go

918 lines
28 KiB
Go
Raw Normal View History

package itest
import (
"context"
"math"
"strings"
2021-08-07 22:50:45 +02:00
"time"
"github.com/btcsuite/btcd/btcutil"
2021-08-07 22:50:45 +02:00
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainreg"
"github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire"
2021-08-07 22:50:45 +02:00
"github.com/stretchr/testify/require"
)
// assertPolicyUpdate checks that a given policy update has been received by a
// list of given nodes.
func assertPolicyUpdate(t *harnessTest, nodes []*lntest.HarnessNode,
advertisingNode string, policy *lnrpc.RoutingPolicy,
chanPoint *lnrpc.ChannelPoint) {
for _, node := range nodes {
assertChannelPolicyUpdate(
t.t, node, advertisingNode, policy, chanPoint, false,
)
}
}
// testUpdateChannelPolicy tests that policy updates made to a channel
2022-09-10 18:04:56 +02:00
// get propagated to other nodes in the network.
func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
ctxb := context.Background()
const (
defaultFeeBase = 1000
defaultFeeRate = 1
defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta
defaultMinHtlc = 1000
)
defaultMaxHtlc := calculateMaxHtlc(funding.MaxBtcFundingAmount)
chanAmt := funding.MaxBtcFundingAmount
pushAmt := chanAmt / 2
// Create a channel Alice->Bob.
chanPoint := openChannelAndAssert(
t, net, net.Alice, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: pushAmt,
},
)
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
// default routing policies.
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: defaultFeeBase,
FeeRateMilliMsat: defaultFeeRate,
TimeLockDelta: defaultTimeLockDelta,
MinHtlc: defaultMinHtlc,
MaxHtlcMsat: defaultMaxHtlc,
}
assertPolicyUpdate(
t, nodes, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
assertPolicyUpdate(
t, nodes, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
// They should now know about the default policies.
for _, node := range nodes {
assertChannelPolicy(
t, node, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
assertChannelPolicy(
t, node, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
}
err := net.Alice.WaitForNetworkChannelOpen(chanPoint)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = net.Bob.WaitForNetworkChannelOpen(chanPoint)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
// Create Carol with options to rate limit channel updates up to 2 per
// day, and create a new channel Bob->Carol.
carol := net.NewNode(
t.t, "Carol", []string{
"--gossip.max-channel-update-burst=2",
"--gossip.channel-update-interval=24h",
},
)
// Clean up carol's node when the test finishes.
defer shutdownAndAssert(net, t, carol)
nodes = append(nodes, carol)
// Send some coins to Carol that can be used for channel funding.
2021-08-19 14:49:39 +02:00
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
net.ConnectNodes(t.t, carol, net.Bob)
// Open the channel Carol->Bob with a custom min_htlc value set. Since
// Carol is opening the channel, she will require Bob to not forward
// HTLCs smaller than this value, and hence he should advertise it as
// part of his ChannelUpdate.
const customMinHtlc = 5000
chanPoint2 := openChannelAndAssert(
t, net, carol, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: pushAmt,
MinHtlc: customMinHtlc,
},
)
defer closeChannelAndAssert(t, net, net.Bob, chanPoint2, false)
expectedPolicyBob := &lnrpc.RoutingPolicy{
FeeBaseMsat: defaultFeeBase,
FeeRateMilliMsat: defaultFeeRate,
TimeLockDelta: defaultTimeLockDelta,
MinHtlc: customMinHtlc,
MaxHtlcMsat: defaultMaxHtlc,
}
expectedPolicyCarol := &lnrpc.RoutingPolicy{
FeeBaseMsat: defaultFeeBase,
FeeRateMilliMsat: defaultFeeRate,
TimeLockDelta: defaultTimeLockDelta,
MinHtlc: defaultMinHtlc,
MaxHtlcMsat: defaultMaxHtlc,
}
assertPolicyUpdate(
t, nodes, net.Bob.PubKeyStr, expectedPolicyBob, chanPoint2,
)
assertPolicyUpdate(
t, nodes, carol.PubKeyStr, expectedPolicyCarol, chanPoint2,
)
// Check that all nodes now know about the updated policies.
for _, node := range nodes {
assertChannelPolicy(
t, node, net.Bob.PubKeyStr, expectedPolicyBob,
chanPoint2,
)
assertChannelPolicy(
t, node, carol.PubKeyStr, expectedPolicyCarol,
chanPoint2,
)
}
err = net.Alice.WaitForNetworkChannelOpen(chanPoint2)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = net.Bob.WaitForNetworkChannelOpen(chanPoint2)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(chanPoint2)
if err != nil {
t.Fatalf("carol didn't report channel: %v", err)
}
// First we'll try to send a payment from Alice to Carol with an amount
// less than the min_htlc value required by Carol. This payment should
// fail, as the channel Bob->Carol cannot carry HTLCs this small.
payAmt := btcutil.Amount(4)
invoice := &lnrpc.Invoice{
Memo: "testing",
Value: int64(payAmt),
}
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := carol.AddInvoice(ctxt, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}
err = completePaymentRequests(
net.Alice, net.Alice.RouterClient,
[]string{resp.PaymentRequest}, true,
)
// Alice knows about the channel policy of Carol and should therefore
// not be able to find a path during routing.
expErr := lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE
if err.Error() != expErr.String() {
t.Fatalf("expected %v, instead got %v", expErr, err)
}
// Now we try to send a payment over the channel with a value too low
// to be accepted. First we query for a route to route a payment of
// 5000 mSAT, as this is accepted.
payAmt = btcutil.Amount(5)
routesReq := &lnrpc.QueryRoutesRequest{
PubKey: carol.PubKeyStr,
Amt: int64(payAmt),
FinalCltvDelta: defaultTimeLockDelta,
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
routes, err := net.Alice.QueryRoutes(ctxt, routesReq)
if err != nil {
t.Fatalf("unable to get route: %v", err)
}
if len(routes.Routes) != 1 {
t.Fatalf("expected to find 1 route, got %v", len(routes.Routes))
}
// We change the route to carry a payment of 4000 mSAT instead of 5000
// mSAT.
payAmt = btcutil.Amount(4)
amtSat := int64(payAmt)
amtMSat := int64(lnwire.NewMSatFromSatoshis(payAmt))
2021-06-28 23:10:16 +02:00
routes.Routes[0].Hops[0].AmtToForward = amtSat // nolint:staticcheck
routes.Routes[0].Hops[0].AmtToForwardMsat = amtMSat
2021-06-28 23:10:16 +02:00
routes.Routes[0].Hops[1].AmtToForward = amtSat // nolint:staticcheck
routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat
// Send the payment with the modified value.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
2021-06-28 23:10:16 +02:00
alicePayStream, err := net.Alice.SendToRoute(ctxt) // nolint:staticcheck
if err != nil {
t.Fatalf("unable to create payment stream for alice: %v", err)
}
sendReq := &lnrpc.SendToRouteRequest{
PaymentHash: resp.RHash,
Route: routes.Routes[0],
}
err = alicePayStream.Send(sendReq)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
// We expect this payment to fail, and that the min_htlc value is
// communicated back to us, since the attempted HTLC value was too low.
sendResp, err := alicePayStream.Recv()
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
// Expected as part of the error message.
substrs := []string{
"AmountBelowMinimum",
"HtlcMinimumMsat: (lnwire.MilliSatoshi) 5000 mSAT",
}
for _, s := range substrs {
if !strings.Contains(sendResp.PaymentError, s) {
t.Fatalf("expected error to contain \"%v\", instead "+
"got %v", s, sendResp.PaymentError)
}
}
// Make sure sending using the original value succeeds.
payAmt = btcutil.Amount(5)
amtSat = int64(payAmt)
amtMSat = int64(lnwire.NewMSatFromSatoshis(payAmt))
2021-06-28 23:10:16 +02:00
routes.Routes[0].Hops[0].AmtToForward = amtSat // nolint:staticcheck
routes.Routes[0].Hops[0].AmtToForwardMsat = amtMSat
2021-06-28 23:10:16 +02:00
routes.Routes[0].Hops[1].AmtToForward = amtSat // nolint:staticcheck
routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat
// Manually set the MPP payload a new for each payment since
// the payment addr will change with each invoice, although we
// can re-use the route itself.
route := routes.Routes[0]
route.Hops[len(route.Hops)-1].TlvPayload = true
route.Hops[len(route.Hops)-1].MppRecord = &lnrpc.MPPRecord{
PaymentAddr: resp.PaymentAddr,
TotalAmtMsat: amtMSat,
}
sendReq = &lnrpc.SendToRouteRequest{
PaymentHash: resp.RHash,
Route: route,
}
err = alicePayStream.Send(sendReq)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
sendResp, err = alicePayStream.Recv()
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
if sendResp.PaymentError != "" {
t.Fatalf("expected payment to succeed, instead got %v",
sendResp.PaymentError)
}
// With our little cluster set up, we'll update the fees and the max htlc
// size for the Bob side of the Alice->Bob channel, and make sure
// all nodes learn about it.
baseFee := int64(1500)
feeRate := int64(12)
timeLockDelta := uint32(66)
maxHtlc := uint64(500000)
expectedPolicy = &lnrpc.RoutingPolicy{
FeeBaseMsat: baseFee,
FeeRateMilliMsat: testFeeBase * feeRate,
TimeLockDelta: timeLockDelta,
MinHtlc: defaultMinHtlc,
MaxHtlcMsat: maxHtlc,
}
req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
MaxHtlcMsat: maxHtlc,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
},
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if _, err := net.Bob.UpdateChannelPolicy(ctxt, req); err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// Wait for all nodes to have seen the policy update done by Bob.
assertPolicyUpdate(
t, nodes, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
// Check that all nodes now know about Bob's updated policy.
for _, node := range nodes {
assertChannelPolicy(
t, node, net.Bob.PubKeyStr, expectedPolicy, chanPoint,
)
}
// Now that all nodes have received the new channel update, we'll try
// to send a payment from Alice to Carol to ensure that Alice has
// internalized this fee update. This shouldn't affect the route that
// Alice takes though: we updated the Alice -> Bob channel and she
// doesn't pay for transit over that channel as it's direct.
// Note that the payment amount is >= the min_htlc value for the
// channel Bob->Carol, so it should successfully be forwarded.
payAmt = btcutil.Amount(5)
invoice = &lnrpc.Invoice{
Memo: "testing",
Value: int64(payAmt),
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
resp, err = carol.AddInvoice(ctxt, invoice)
if err != nil {
t.Fatalf("unable to add invoice: %v", err)
}
err = completePaymentRequests(
net.Alice, net.Alice.RouterClient,
[]string{resp.PaymentRequest}, true,
)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
// We'll now open a channel from Alice directly to Carol.
net.ConnectNodes(t.t, net.Alice, carol)
chanPoint3 := openChannelAndAssert(
t, net, net.Alice, carol,
lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: pushAmt,
},
)
defer closeChannelAndAssert(t, net, net.Alice, chanPoint3, false)
err = net.Alice.WaitForNetworkChannelOpen(chanPoint3)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(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)
maxHtlc *= 2
expectedPolicy.FeeBaseMsat = baseFee
expectedPolicy.FeeRateMilliMsat = testFeeBase * feeRate
expectedPolicy.TimeLockDelta = timeLockDelta
expectedPolicy.MaxHtlcMsat = maxHtlc
req = &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
MaxHtlcMsat: maxHtlc,
}
req.Scope = &lnrpc.PolicyUpdateRequest_Global{}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = net.Alice.UpdateChannelPolicy(ctxt, req)
if err != nil {
t.Fatalf("unable to update alice's channel policy: %v", err)
}
// Wait for all nodes to have seen the policy updates for both of
// Alice's channels.
assertPolicyUpdate(
t, nodes, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
assertPolicyUpdate(
t, nodes, net.Alice.PubKeyStr, expectedPolicy, chanPoint3,
)
// And finally check that all nodes remembers the policy update they
// received.
for _, node := range nodes {
assertChannelPolicy(
t, node, net.Alice.PubKeyStr, expectedPolicy,
chanPoint, chanPoint3,
)
}
// Now, to test that Carol is properly rate limiting incoming updates,
// we'll send two more update from Alice. Carol should accept the first,
// but not the second, as she only allows two updates per day and a day
// has yet to elapse from the previous update.
const numUpdatesTilRateLimit = 2
for i := 0; i < numUpdatesTilRateLimit; i++ {
prevAlicePolicy := *expectedPolicy
baseFee *= 2
expectedPolicy.FeeBaseMsat = baseFee
req.BaseFeeMsat = baseFee
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
_, err = net.Alice.UpdateChannelPolicy(ctxt, req)
require.NoError(t.t, err)
// Wait for all nodes to have seen the policy updates for both
// of Alice's channels. Carol will not see the last update as
// the limit has been reached.
assertPolicyUpdate(
t, []*lntest.HarnessNode{net.Alice, net.Bob},
net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
assertPolicyUpdate(
t, []*lntest.HarnessNode{net.Alice, net.Bob},
net.Alice.PubKeyStr, expectedPolicy, chanPoint3,
)
2022-09-10 18:04:56 +02:00
// Check that all nodes remember the policy update
// they received.
assertChannelPolicy(
t, net.Alice, net.Alice.PubKeyStr,
expectedPolicy, chanPoint, chanPoint3,
)
assertChannelPolicy(
t, net.Bob, net.Alice.PubKeyStr,
expectedPolicy, chanPoint, chanPoint3,
)
// Carol was added last, which is why we check the last index.
// Since Carol didn't receive the last update, she still has
// Alice's old policy.
if i == numUpdatesTilRateLimit-1 {
expectedPolicy = &prevAlicePolicy
}
assertPolicyUpdate(
t, []*lntest.HarnessNode{carol},
net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
assertPolicyUpdate(
t, []*lntest.HarnessNode{carol},
net.Alice.PubKeyStr, expectedPolicy, chanPoint3,
)
assertChannelPolicy(
t, carol, net.Alice.PubKeyStr,
expectedPolicy, chanPoint, chanPoint3,
)
}
}
2021-08-07 22:50:45 +02:00
// testSendUpdateDisableChannel ensures that a channel update with the disable
// flag set is sent once a channel has been either unilaterally or cooperatively
// closed.
func testSendUpdateDisableChannel(net *lntest.NetworkHarness, t *harnessTest) {
const (
chanAmt = 100000
)
// Open a channel between Alice and Bob and Alice and Carol. These will
// be closed later on in order to trigger channel update messages
// marking the channels as disabled.
chanPointAliceBob := openChannelAndAssert(
t, net, net.Alice, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
carol := net.NewNode(
t.t, "Carol", []string{
"--minbackoff=10s",
"--chan-enable-timeout=1.5s",
"--chan-disable-timeout=3s",
"--chan-status-sample-interval=.5s",
})
defer shutdownAndAssert(net, t, carol)
net.ConnectNodes(t.t, net.Alice, carol)
chanPointAliceCarol := openChannelAndAssert(
t, net, net.Alice, carol,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
// We create a new node Eve that has an inactive channel timeout of
// just 2 seconds (down from the default 20m). It will be used to test
// channel updates for channels going inactive.
eve := net.NewNode(
t.t, "Eve", []string{
"--minbackoff=10s",
"--chan-enable-timeout=1.5s",
"--chan-disable-timeout=3s",
"--chan-status-sample-interval=.5s",
})
defer shutdownAndAssert(net, t, eve)
// Give Eve some coins.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, eve)
// Connect Eve to Carol and Bob, and open a channel to carol.
net.ConnectNodes(t.t, eve, carol)
net.ConnectNodes(t.t, eve, net.Bob)
chanPointEveCarol := openChannelAndAssert(
t, net, eve, carol,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
// Launch a node for Dave which will connect to Bob in order to receive
// graph updates from. This will ensure that the channel updates are
// propagated throughout the network.
dave := net.NewNode(t.t, "Dave", nil)
defer shutdownAndAssert(net, t, dave)
net.ConnectNodes(t.t, net.Bob, dave)
// We should expect to see a channel update with the default routing
// policy, except that it should indicate the channel is disabled.
expectedPolicy := &lnrpc.RoutingPolicy{
2021-08-07 22:50:45 +02:00
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
MinHtlc: 1000, // default value
2021-08-07 22:50:45 +02:00
MaxHtlcMsat: calculateMaxHtlc(chanAmt),
Disabled: true,
}
// assertPolicyUpdate checks that the required policy update has
// happened on the given node.
assertPolicyUpdate := func(node *lntest.HarnessNode,
policy *lnrpc.RoutingPolicy, chanPoint *lnrpc.ChannelPoint) {
require.NoError(
t.t, dave.WaitForChannelPolicyUpdate(
node.PubKeyStr, policy, chanPoint, false,
), "error while waiting for channel update",
)
}
2021-08-07 22:50:45 +02:00
// Let Carol go offline. Since Eve has an inactive timeout of 2s, we
// expect her to send an update disabling the channel.
restartCarol, err := net.SuspendNode(carol)
if err != nil {
t.Fatalf("unable to suspend carol: %v", err)
}
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
// We restart Carol. Since the channel now becomes active again, Eve
// should send a ChannelUpdate setting the channel no longer disabled.
if err := restartCarol(); err != nil {
t.Fatalf("unable to restart carol: %v", err)
}
2021-08-07 22:50:45 +02:00
expectedPolicy.Disabled = false
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
// Wait until Carol and Eve are reconnected before we disconnect them
// again.
net.EnsureConnected(t.t, eve, carol)
2021-08-07 22:50:45 +02:00
// Now we'll test a long disconnection. Disconnect Carol and Eve and
// ensure they both detect each other as disabled. Their min backoffs
// are high enough to not interfere with disabling logic.
if err := net.DisconnectNodes(carol, eve); err != nil {
t.Fatalf("unable to disconnect Carol from Eve: %v", err)
}
2021-08-07 22:50:45 +02:00
// Wait for a disable from both Carol and Eve to come through.
expectedPolicy.Disabled = true
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
assertPolicyUpdate(carol, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
// Reconnect Carol and Eve, this should cause them to reenable the
// channel from both ends after a short delay.
net.EnsureConnected(t.t, carol, eve)
expectedPolicy.Disabled = false
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
assertPolicyUpdate(carol, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
// Now we'll test a short disconnection. Disconnect Carol and Eve, then
// reconnect them after one second so that their scheduled disables are
// aborted. One second is twice the status sample interval, so this
// should allow for the disconnect to be detected, but still leave time
// to cancel the announcement before the 3 second inactive timeout is
// hit.
if err := net.DisconnectNodes(carol, eve); err != nil {
t.Fatalf("unable to disconnect Carol from Eve: %v", err)
}
time.Sleep(time.Second)
net.EnsureConnected(t.t, eve, carol)
// Since the disable should have been canceled by both Carol and Eve, we
// expect no channel updates to appear on the network, which means we
// expect the polices stay unchanged(Disable == false).
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
assertPolicyUpdate(carol, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
// Close Alice's channels with Bob and Carol cooperatively and
// unilaterally respectively.
_, _, err = net.CloseChannel(net.Alice, chanPointAliceBob, false)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
_, _, err = net.CloseChannel(net.Alice, chanPointAliceCarol, true)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
// Now that the channel close processes have been started, we should
// receive an update marking each as disabled.
expectedPolicy.Disabled = true
assertPolicyUpdate(net.Alice, expectedPolicy, chanPointAliceBob)
assertPolicyUpdate(net.Alice, expectedPolicy, chanPointAliceCarol)
2021-08-07 22:50:45 +02:00
// Finally, close the channels by mining the closing transactions.
mineBlocks(t, net, 1, 2)
// Also do this check for Eve's channel with Carol.
_, _, err = net.CloseChannel(eve, chanPointEveCarol, false)
if err != nil {
t.Fatalf("unable to close channel: %v", err)
}
assertPolicyUpdate(eve, expectedPolicy, chanPointEveCarol)
2021-08-07 22:50:45 +02:00
mineBlocks(t, net, 1, 1)
// And finally, clean up the force closed channel by mining the
// sweeping transaction.
cleanupForceClose(t, net, net.Alice, chanPointAliceCarol)
}
// testUpdateChannelPolicyForPrivateChannel tests when a private channel
// updates its channel edge policy, we will use the updated policy to send our
// payment.
// The topology is created as: Alice -> Bob -> Carol, where Alice -> Bob is
// public and Bob -> Carol is private. After an invoice is created by Carol,
// Bob will update the base fee via UpdateChannelPolicy, we will test that
// Alice will not fail the payment and send it using the updated channel
// policy.
func testUpdateChannelPolicyForPrivateChannel(net *lntest.NetworkHarness,
t *harnessTest) {
ctxb := context.Background()
defer ctxb.Done()
// We'll create the following topology first,
// Alice <--public:100k--> Bob <--private:100k--> Carol
const chanAmt = btcutil.Amount(100000)
// Open a channel with 100k satoshis between Alice and Bob.
chanPointAliceBob := openChannelAndAssert(
t, net, net.Alice, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
},
)
defer closeChannelAndAssert(t, net, net.Alice, chanPointAliceBob, false)
// Get Alice's funding point.
aliceChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointAliceBob)
require.NoError(t.t, err, "unable to get txid")
aliceFundPoint := wire.OutPoint{
Hash: *aliceChanTXID,
Index: chanPointAliceBob.OutputIndex,
}
// Create a new node Carol.
carol := net.NewNode(t.t, "Carol", nil)
defer shutdownAndAssert(net, t, carol)
// Connect Carol to Bob.
net.ConnectNodes(t.t, carol, net.Bob)
// Open a channel with 100k satoshis between Bob and Carol.
chanPointBobCarol := openChannelAndAssert(
t, net, net.Bob, carol,
lntest.OpenChannelParams{
Amt: chanAmt,
Private: true,
},
)
defer closeChannelAndAssert(t, net, net.Bob, chanPointBobCarol, false)
// Carol should be aware of the channel between Alice and Bob.
err = carol.WaitForNetworkChannelOpen(chanPointAliceBob)
require.NoError(t.t, err)
2021-08-07 22:50:45 +02:00
// Get Bob's funding point.
bobChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointBobCarol)
require.NoError(t.t, err, "unable to get txid")
bobFundPoint := wire.OutPoint{
Hash: *bobChanTXID,
Index: chanPointBobCarol.OutputIndex,
}
// We should have the following topology now,
// Alice <--public:100k--> Bob <--private:100k--> Carol
//
// Now we will create an invoice for Carol.
const paymentAmt = 20000
invoice := &lnrpc.Invoice{
Memo: "routing hints",
Value: paymentAmt,
Private: true,
}
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
resp, err := carol.AddInvoice(ctxt, invoice)
require.NoError(t.t, err, "unable to create invoice for carol")
// Bob now updates the channel edge policy for the private channel.
const (
baseFeeMSat = 33000
)
timeLockDelta := uint32(chainreg.DefaultBitcoinTimeLockDelta)
updateFeeReq := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFeeMSat,
TimeLockDelta: timeLockDelta,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPointBobCarol,
},
}
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
_, err = net.Bob.UpdateChannelPolicy(ctxt, updateFeeReq)
require.NoError(t.t, err, "unable to update chan policy")
// Alice pays the invoices. She will use the updated baseFeeMSat in the
// payment
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
payReqs := []string{resp.PaymentRequest}
require.NoError(t.t,
completePaymentRequests(
net.Alice, net.Alice.RouterClient, payReqs, true,
), "unable to send payment",
)
// Check that Alice did make the payment with two HTLCs, one failed and
// one succeeded.
ctxt, _ = context.WithTimeout(ctxt, defaultTimeout)
paymentsResp, err := net.Alice.ListPayments(
ctxt, &lnrpc.ListPaymentsRequest{},
)
require.NoError(t.t, err, "failed to obtain payments for Alice")
require.Equal(t.t, 1, len(paymentsResp.Payments), "expected 1 payment")
htlcs := paymentsResp.Payments[0].Htlcs
require.Equal(t.t, 2, len(htlcs), "expected to have 2 HTLCs")
require.Equal(
t.t, lnrpc.HTLCAttempt_FAILED, htlcs[0].Status,
"the first HTLC attempt should fail",
)
require.Equal(
t.t, lnrpc.HTLCAttempt_SUCCEEDED, htlcs[1].Status,
"the second HTLC attempt should succeed",
)
// Carol should have received 20k satoshis from Bob.
assertAmountPaid(t, "Carol(remote) [<=private] Bob(local)",
carol, bobFundPoint, 0, paymentAmt)
// Bob should have sent 20k satoshis to Carol.
assertAmountPaid(t, "Bob(local) [private=>] Carol(remote)",
net.Bob, bobFundPoint, paymentAmt, 0)
2022-01-13 17:29:43 +01:00
// Calculate the amount in satoshis.
2021-08-07 22:50:45 +02:00
amtExpected := int64(paymentAmt + baseFeeMSat/1000)
// Bob should have received 20k satoshis + fee from Alice.
assertAmountPaid(t, "Bob(remote) <= Alice(local)",
net.Bob, aliceFundPoint, 0, amtExpected)
// Alice should have sent 20k satoshis + fee to Bob.
assertAmountPaid(t, "Alice(local) => Bob(remote)",
net.Alice, aliceFundPoint, amtExpected, 0)
}
// testUpdateChannelPolicyFeeRateAccuracy tests that updating the channel policy
// rounds fee rate values correctly as well as setting fee rate with ppm works
// as expected.
func testUpdateChannelPolicyFeeRateAccuracy(net *lntest.NetworkHarness,
t *harnessTest) {
chanAmt := funding.MaxBtcFundingAmount
pushAmt := chanAmt / 2
// Create a channel Alice -> Bob.
chanPoint := openChannelAndAssert(
t, net, net.Alice, net.Bob,
lntest.OpenChannelParams{
Amt: chanAmt,
PushAmt: pushAmt,
},
)
defer closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
// Nodes that we need to make sure receive the channel updates.
nodes := []*lntest.HarnessNode{net.Alice, net.Bob}
baseFee := int64(1500)
timeLockDelta := uint32(66)
maxHtlc := uint64(500000)
defaultMinHtlc := int64(1000)
// Originally LND did not properly round up fee rates which caused
// inaccuracy where fee rates were simply rounded down due to the
// integer conversion.
//
// We'll use a fee rate of 0.031337 which without rounding up would
// have resulted in a fee rate ppm of 31336.
feeRate := 0.031337
// Expected fee rate will be rounded up.
expectedFeeRateMilliMsat := int64(math.Round(testFeeBase * feeRate))
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: baseFee,
FeeRateMilliMsat: expectedFeeRateMilliMsat,
TimeLockDelta: timeLockDelta,
MinHtlc: defaultMinHtlc,
MaxHtlcMsat: maxHtlc,
}
req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: feeRate,
TimeLockDelta: timeLockDelta,
MaxHtlcMsat: maxHtlc,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
},
}
ctxb := context.Background()
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
if _, err := net.Alice.UpdateChannelPolicy(ctxt, req); err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// Make sure that both Alice and Bob sees the same policy after update.
assertPolicyUpdate(
t, nodes, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
// Now use the new PPM feerate field and make sure that the feerate is
// correctly set.
feeRatePPM := uint32(32337)
req.FeeRate = 0 // Can't set both at the same time.
req.FeeRatePpm = feeRatePPM
expectedPolicy.FeeRateMilliMsat = int64(feeRatePPM)
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if _, err := net.Alice.UpdateChannelPolicy(ctxt, req); err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// Make sure that both Alice and Bob sees the same policy after update.
assertPolicyUpdate(
t, nodes, net.Alice.PubKeyStr, expectedPolicy, chanPoint,
)
}