mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-21 14:10:35 +02:00
lntest/itest: add session deletion itest
This commit is contained in:
@@ -515,4 +515,8 @@ var allTestCases = []*lntest.TestCase{
|
||||
Name: "lookup htlc resolution",
|
||||
TestFunc: testLookupHtlcResolution,
|
||||
},
|
||||
{
|
||||
Name: "watchtower session management",
|
||||
TestFunc: testWatchtowerSessionManagement,
|
||||
},
|
||||
}
|
||||
|
172
itest/lnd_watchtower_test.go
Normal file
172
itest/lnd_watchtower_test.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/node"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testWatchtowerSessionManagement tests that session deletion is done
|
||||
// correctly.
|
||||
func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
||||
const (
|
||||
chanAmt = funding.MaxBtcFundingAmount
|
||||
paymentAmt = 10_000
|
||||
numInvoices = 5
|
||||
maxUpdates = numInvoices * 2
|
||||
externalIP = "1.2.3.4"
|
||||
sessionCloseRange = 1
|
||||
)
|
||||
|
||||
// Set up Wallis the watchtower who will be used by Dave to watch over
|
||||
// his channel commitment transactions.
|
||||
wallis := ht.NewNode("Wallis", []string{
|
||||
"--watchtower.active",
|
||||
"--watchtower.externalip=" + externalIP,
|
||||
})
|
||||
|
||||
wallisInfo := wallis.RPC.GetInfoWatchtower()
|
||||
|
||||
// Assert that Wallis has one listener and it is 0.0.0.0:9911 or
|
||||
// [::]:9911. Since no listener is explicitly specified, one of these
|
||||
// should be the default depending on whether the host supports IPv6 or
|
||||
// not.
|
||||
require.Len(ht, wallisInfo.Listeners, 1)
|
||||
listener := wallisInfo.Listeners[0]
|
||||
require.True(ht, listener == "0.0.0.0:9911" || listener == "[::]:9911")
|
||||
|
||||
// Assert the Wallis's URIs properly display the chosen external IP.
|
||||
require.Len(ht, wallisInfo.Uris, 1)
|
||||
require.Contains(ht, wallisInfo.Uris[0], externalIP)
|
||||
|
||||
// Dave will be the tower client.
|
||||
daveArgs := []string{
|
||||
"--wtclient.active",
|
||||
fmt.Sprintf("--wtclient.max-updates=%d", maxUpdates),
|
||||
fmt.Sprintf(
|
||||
"--wtclient.session-close-range=%d", sessionCloseRange,
|
||||
),
|
||||
}
|
||||
dave := ht.NewNode("Dave", daveArgs)
|
||||
|
||||
addTowerReq := &wtclientrpc.AddTowerRequest{
|
||||
Pubkey: wallisInfo.Pubkey,
|
||||
Address: listener,
|
||||
}
|
||||
dave.RPC.AddTower(addTowerReq)
|
||||
|
||||
// Assert that there exists a session between Dave and Wallis.
|
||||
err := wait.NoError(func() error {
|
||||
info := dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
||||
Pubkey: wallisInfo.Pubkey,
|
||||
IncludeSessions: true,
|
||||
})
|
||||
|
||||
var numSessions uint32
|
||||
for _, sessionType := range info.SessionInfo {
|
||||
numSessions += sessionType.NumSessions
|
||||
}
|
||||
if numSessions > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("expected a non-zero number of sessions")
|
||||
}, defaultTimeout)
|
||||
require.NoError(ht, err)
|
||||
|
||||
// Before we make a channel, we'll load up Dave with some coins sent
|
||||
// directly from the miner.
|
||||
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||
|
||||
// Connect Dave and Alice.
|
||||
ht.ConnectNodes(dave, ht.Alice)
|
||||
|
||||
// Open a channel between Dave and Alice.
|
||||
params := lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
}
|
||||
chanPoint := ht.OpenChannel(dave, ht.Alice, params)
|
||||
|
||||
// Since there are 2 updates made for every payment and the maximum
|
||||
// number of updates per session has been set to 10, make 5 payments
|
||||
// between the pair so that the session is exhausted.
|
||||
alicePayReqs, _, _ := ht.CreatePayReqs(
|
||||
ht.Alice, paymentAmt, numInvoices,
|
||||
)
|
||||
|
||||
send := func(node *node.HarnessNode, payReq string) {
|
||||
stream := node.RPC.SendPayment(&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
})
|
||||
|
||||
ht.AssertPaymentStatusFromStream(
|
||||
stream, lnrpc.Payment_SUCCEEDED,
|
||||
)
|
||||
}
|
||||
|
||||
for i := 0; i < numInvoices; i++ {
|
||||
send(dave, alicePayReqs[i])
|
||||
}
|
||||
|
||||
// assertNumBackups is a closure that asserts that Dave has a certain
|
||||
// number of backups backed up to the tower. If mineOnFail is true,
|
||||
// then a block will be mined each time the assertion fails.
|
||||
assertNumBackups := func(expected int, mineOnFail bool) {
|
||||
err = wait.NoError(func() error {
|
||||
info := dave.RPC.GetTowerInfo(
|
||||
&wtclientrpc.GetTowerInfoRequest{
|
||||
Pubkey: wallisInfo.Pubkey,
|
||||
IncludeSessions: true,
|
||||
},
|
||||
)
|
||||
|
||||
var numBackups uint32
|
||||
for _, sessionType := range info.SessionInfo {
|
||||
for _, session := range sessionType.Sessions {
|
||||
numBackups += session.NumBackups
|
||||
}
|
||||
}
|
||||
|
||||
if numBackups == uint32(expected) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if mineOnFail {
|
||||
ht.Miner.MineBlocksSlow(1)
|
||||
}
|
||||
|
||||
return fmt.Errorf("expected %d backups, got %d",
|
||||
expected, numBackups)
|
||||
}, defaultTimeout)
|
||||
require.NoError(ht, err)
|
||||
}
|
||||
|
||||
// Assert that one of the sessions now has 10 backups.
|
||||
assertNumBackups(10, false)
|
||||
|
||||
// Now close the channel and wait for the close transaction to appear
|
||||
// in the mempool so that it is included in a block when we mine.
|
||||
ht.CloseChannelAssertPending(dave, chanPoint, false)
|
||||
|
||||
// Mine enough blocks to surpass the session-close-range. This should
|
||||
// trigger the session to be deleted.
|
||||
ht.MineBlocksAndAssertNumTxes(sessionCloseRange+6, 1)
|
||||
|
||||
// Wait for the session to be deleted. We know it has been deleted once
|
||||
// the number of backups is back to zero. We check for number of backups
|
||||
// instead of number of sessions because it is expected that the client
|
||||
// would immediately negotiate another session after deleting the
|
||||
// exhausted one. This time we set the "mineOnFail" parameter to true to
|
||||
// ensure that the session deleting logic is run.
|
||||
assertNumBackups(0, true)
|
||||
}
|
Reference in New Issue
Block a user