itest: move tests by their category
This commit is contained in:
6 files changed
+451
-447
No files matched your search
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -94,72 +93,6 @@ func openChannelAndAssert(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
return fundingChanPoint
|
return fundingChanPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// graphSubscription houses the proxied update and error chans for a node's
|
|
||||||
// graph subscriptions.
|
|
||||||
type graphSubscription struct {
|
|
||||||
updateChan chan *lnrpc.GraphTopologyUpdate
|
|
||||||
errChan chan error
|
|
||||||
quit chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// subscribeGraphNotifications subscribes to channel graph updates and launches
|
|
||||||
// a goroutine that forwards these to the returned channel.
|
|
||||||
func subscribeGraphNotifications(ctxb context.Context, t *harnessTest,
|
|
||||||
node *lntest.HarnessNode) graphSubscription {
|
|
||||||
|
|
||||||
// We'll first start by establishing a notification client which will
|
|
||||||
// send us notifications upon detected changes in the channel graph.
|
|
||||||
req := &lnrpc.GraphTopologySubscription{}
|
|
||||||
ctx, cancelFunc := context.WithCancel(ctxb)
|
|
||||||
topologyClient, err := node.SubscribeChannelGraph(ctx, req)
|
|
||||||
require.NoError(t.t, err, "unable to create topology client")
|
|
||||||
|
|
||||||
// We'll launch a goroutine that will be responsible for proxying all
|
|
||||||
// notifications recv'd from the client into the channel below.
|
|
||||||
errChan := make(chan error, 1)
|
|
||||||
quit := make(chan struct{})
|
|
||||||
graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
defer cancelFunc()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-quit:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
graphUpdate, err := topologyClient.Recv()
|
|
||||||
select {
|
|
||||||
case <-quit:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
select {
|
|
||||||
case errChan <- err:
|
|
||||||
case <-quit:
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case graphUpdates <- graphUpdate:
|
|
||||||
case <-quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return graphSubscription{
|
|
||||||
updateChan: graphUpdates,
|
|
||||||
errChan: errChan,
|
|
||||||
quit: quit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForGraphSync(t *harnessTest, node *lntest.HarnessNode) {
|
func waitForGraphSync(t *harnessTest, node *lntest.HarnessNode) {
|
||||||
t.t.Helper()
|
t.t.Helper()
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -749,3 +750,69 @@ func testNodeAnnouncement(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
// Close the channel between Bob and Dave.
|
// Close the channel between Bob and Dave.
|
||||||
closeChannelAndAssert(t, net, net.Bob, chanPoint, false)
|
closeChannelAndAssert(t, net, net.Bob, chanPoint, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// graphSubscription houses the proxied update and error chans for a node's
|
||||||
|
// graph subscriptions.
|
||||||
|
type graphSubscription struct {
|
||||||
|
updateChan chan *lnrpc.GraphTopologyUpdate
|
||||||
|
errChan chan error
|
||||||
|
quit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribeGraphNotifications subscribes to channel graph updates and launches
|
||||||
|
// a goroutine that forwards these to the returned channel.
|
||||||
|
func subscribeGraphNotifications(ctxb context.Context, t *harnessTest,
|
||||||
|
node *lntest.HarnessNode) graphSubscription {
|
||||||
|
|
||||||
|
// We'll first start by establishing a notification client which will
|
||||||
|
// send us notifications upon detected changes in the channel graph.
|
||||||
|
req := &lnrpc.GraphTopologySubscription{}
|
||||||
|
ctx, cancelFunc := context.WithCancel(ctxb)
|
||||||
|
topologyClient, err := node.SubscribeChannelGraph(ctx, req)
|
||||||
|
require.NoError(t.t, err, "unable to create topology client")
|
||||||
|
|
||||||
|
// We'll launch a goroutine that will be responsible for proxying all
|
||||||
|
// notifications recv'd from the client into the channel below.
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
quit := make(chan struct{})
|
||||||
|
graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
defer cancelFunc()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
graphUpdate, err := topologyClient.Recv()
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
select {
|
||||||
|
case errChan <- err:
|
||||||
|
case <-quit:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case graphUpdates <- graphUpdate:
|
||||||
|
case <-quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return graphSubscription{
|
||||||
|
updateChan: graphUpdates,
|
||||||
|
errChan: errChan,
|
||||||
|
quit: quit,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,16 @@ package itest
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testUpdateChannelPolicy tests that policy updates made to a channel
|
// testUpdateChannelPolicy tests that policy updates made to a channel
|
||||||
@@ -509,47 +512,345 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
closeChannelAndAssert(t, net, net.Alice, chanPoint3, false)
|
closeChannelAndAssert(t, net, net.Alice, chanPoint3, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateChannelPolicy updates the channel policy of node to the
|
// testSendUpdateDisableChannel ensures that a channel update with the disable
|
||||||
// given fees and timelock delta. This function blocks until
|
// flag set is sent once a channel has been either unilaterally or cooperatively
|
||||||
// listenerNode has received the policy update.
|
// closed.
|
||||||
func updateChannelPolicy(t *harnessTest, node *lntest.HarnessNode,
|
func testSendUpdateDisableChannel(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64,
|
|
||||||
timeLockDelta uint32, maxHtlc uint64, listenerNode *lntest.HarnessNode) {
|
|
||||||
|
|
||||||
ctxb := context.Background()
|
ctxb := context.Background()
|
||||||
|
|
||||||
expectedPolicy := &lnrpc.RoutingPolicy{
|
const (
|
||||||
FeeBaseMsat: baseFee,
|
chanAmt = 100000
|
||||||
FeeRateMilliMsat: feeRate,
|
)
|
||||||
TimeLockDelta: timeLockDelta,
|
|
||||||
MinHtlc: 1000, // default value
|
|
||||||
MaxHtlcMsat: maxHtlc,
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFeeReq := &lnrpc.PolicyUpdateRequest{
|
// Open a channel between Alice and Bob and Alice and Carol. These will
|
||||||
BaseFeeMsat: baseFee,
|
// be closed later on in order to trigger channel update messages
|
||||||
FeeRate: float64(feeRate) / testFeeBase,
|
// marking the channels as disabled.
|
||||||
TimeLockDelta: timeLockDelta,
|
chanPointAliceBob := openChannelAndAssert(
|
||||||
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
|
t, net, net.Alice, net.Bob,
|
||||||
ChanPoint: chanPoint,
|
lntest.OpenChannelParams{
|
||||||
},
|
Amt: chanAmt,
|
||||||
MaxHtlcMsat: maxHtlc,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
if _, err := node.UpdateChannelPolicy(ctxt, updateFeeReq); err != nil {
|
|
||||||
t.Fatalf("unable to update chan policy: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for listener node to receive the channel update from node.
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
graphSub := subscribeGraphNotifications(ctxt, t, listenerNode)
|
|
||||||
defer close(graphSub.quit)
|
|
||||||
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, graphSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{node.PubKeyStr, expectedPolicy, chanPoint},
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
daveSub := subscribeGraphNotifications(ctxb, t, dave)
|
||||||
|
defer close(daveSub.quit)
|
||||||
|
|
||||||
|
// 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{
|
||||||
|
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
|
||||||
|
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
|
||||||
|
TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
|
||||||
|
MinHtlc: 1000, // default value
|
||||||
|
MaxHtlcMsat: calculateMaxHtlc(chanAmt),
|
||||||
|
Disabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPolicy.Disabled = false
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a disable from both Carol and Eve to come through.
|
||||||
|
expectedPolicy.Disabled = true
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
{carol.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
{carol.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
assertNoChannelUpdates(t, daveSub, 4*time.Second)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{net.Alice.PubKeyStr, expectedPolicy, chanPointAliceBob},
|
||||||
|
{net.Alice.PubKeyStr, expectedPolicy, chanPointAliceCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, daveSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// Calcuate the amount in satoshis.
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
@@ -1323,209 +1323,6 @@ func testNodeSignVerify(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
closeChannelAndAssert(t, net, net.Alice, aliceBobCh, false)
|
closeChannelAndAssert(t, net, net.Alice, aliceBobCh, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
|
||||||
ctxb := context.Background()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
daveSub := subscribeGraphNotifications(ctxb, t, dave)
|
|
||||||
defer close(daveSub.quit)
|
|
||||||
|
|
||||||
// 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{
|
|
||||||
FeeBaseMsat: int64(chainreg.DefaultBitcoinBaseFeeMSat),
|
|
||||||
FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate),
|
|
||||||
TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta,
|
|
||||||
MinHtlc: 1000, // default value
|
|
||||||
MaxHtlcMsat: calculateMaxHtlc(chanAmt),
|
|
||||||
Disabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPolicy.Disabled = false
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for a disable from both Carol and Eve to come through.
|
|
||||||
expectedPolicy.Disabled = true
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
{carol.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
{carol.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
assertNoChannelUpdates(t, daveSub, 4*time.Second)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{net.Alice.PubKeyStr, expectedPolicy, chanPointAliceBob},
|
|
||||||
{net.Alice.PubKeyStr, expectedPolicy, chanPointAliceCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForChannelUpdate(
|
|
||||||
t, daveSub,
|
|
||||||
[]expectedChanUpdate{
|
|
||||||
{eve.PubKeyStr, expectedPolicy, chanPointEveCarol},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
mineBlocks(t, net, 1, 1)
|
|
||||||
|
|
||||||
// And finally, clean up the force closed channel by mining the
|
|
||||||
// sweeping transaction.
|
|
||||||
cleanupForceClose(t, net, net.Alice, chanPointAliceCarol)
|
|
||||||
}
|
|
||||||
|
|
||||||
// testAbandonChannel abandones a channel and asserts that it is no
|
// testAbandonChannel abandones a channel and asserts that it is no
|
||||||
// longer open and not in one of the pending closure states. It also
|
// longer open and not in one of the pending closure states. It also
|
||||||
// verifies that the abandoned channel is reported as closed with close
|
// verifies that the abandoned channel is reported as closed with close
|
||||||
|
|||||||
@@ -384,3 +384,48 @@ func assertEventAndType(t *harnessTest, eventType routerrpc.HtlcEvent_EventType,
|
|||||||
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateChannelPolicy updates the channel policy of node to the
|
||||||
|
// given fees and timelock delta. This function blocks until
|
||||||
|
// listenerNode has received the policy update.
|
||||||
|
func updateChannelPolicy(t *harnessTest, node *lntest.HarnessNode,
|
||||||
|
chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64,
|
||||||
|
timeLockDelta uint32, maxHtlc uint64, listenerNode *lntest.HarnessNode) {
|
||||||
|
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
expectedPolicy := &lnrpc.RoutingPolicy{
|
||||||
|
FeeBaseMsat: baseFee,
|
||||||
|
FeeRateMilliMsat: feeRate,
|
||||||
|
TimeLockDelta: timeLockDelta,
|
||||||
|
MinHtlc: 1000, // default value
|
||||||
|
MaxHtlcMsat: maxHtlc,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFeeReq := &lnrpc.PolicyUpdateRequest{
|
||||||
|
BaseFeeMsat: baseFee,
|
||||||
|
FeeRate: float64(feeRate) / testFeeBase,
|
||||||
|
TimeLockDelta: timeLockDelta,
|
||||||
|
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
|
||||||
|
ChanPoint: chanPoint,
|
||||||
|
},
|
||||||
|
MaxHtlcMsat: maxHtlc,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
if _, err := node.UpdateChannelPolicy(ctxt, updateFeeReq); err != nil {
|
||||||
|
t.Fatalf("unable to update chan policy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for listener node to receive the channel update from node.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
graphSub := subscribeGraphNotifications(ctxt, t, listenerNode)
|
||||||
|
defer close(graphSub.quit)
|
||||||
|
|
||||||
|
waitForChannelUpdate(
|
||||||
|
t, graphSub,
|
||||||
|
[]expectedChanUpdate{
|
||||||
|
{node.PubKeyStr, expectedPolicy, chanPoint},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1017,145 +1017,6 @@ func testPrivateChannels(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
closeChannelAndAssert(t, net, carol, chanPointPrivate, false)
|
closeChannelAndAssert(t, net, carol, chanPointPrivate, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
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(ctxb, 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)
|
|
||||||
|
|
||||||
// Calcuate the amount in satoshis.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// testInvoiceRoutingHints tests that the routing hints for an invoice are
|
// testInvoiceRoutingHints tests that the routing hints for an invoice are
|
||||||
// created properly.
|
// created properly.
|
||||||
func testInvoiceRoutingHints(net *lntest.NetworkHarness, t *harnessTest) {
|
func testInvoiceRoutingHints(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
|
|||||||
Reference in new issue
Block a user