mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
itest: refactor testSwitchOfflineDeliveryOutgoingOffline
This commit is contained in:
parent
874689719e
commit
9943bb8539
@ -381,4 +381,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||
Name: "switch offline delivery persistence",
|
||||
TestFunc: testSwitchOfflineDeliveryPersistence,
|
||||
},
|
||||
{
|
||||
Name: "switch offline delivery outgoing offline",
|
||||
TestFunc: testSwitchOfflineDeliveryOutgoingOffline,
|
||||
},
|
||||
}
|
||||
|
@ -2,12 +2,9 @@ package itest
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntemp"
|
||||
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -250,11 +247,12 @@ func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) {
|
||||
s.assertAmoutPaid(ht, amountPaid, numPayments+1)
|
||||
}
|
||||
|
||||
// testSwitchOfflineDeliveryOutgoingOffline constructs a set of multihop payments,
|
||||
// and tests that the returning payments are not lost if a peer on the backwards
|
||||
// path is offline when the settle/fails are received AND the peer buffering the
|
||||
// responses is completely restarts. We expect the payments to be reloaded from
|
||||
// disk, and transmitted as soon as the intermediaries are reconnected.
|
||||
// testSwitchOfflineDeliveryOutgoingOffline constructs a set of multihop
|
||||
// payments, and tests that the returning payments are not lost if a peer on
|
||||
// the backwards path is offline when the settle/fails are received AND the
|
||||
// peer buffering the responses is completely restarts. We expect the payments
|
||||
// to be reloaded from disk, and transmitted as soon as the intermediaries are
|
||||
// reconnected.
|
||||
//
|
||||
// The general flow of this test:
|
||||
// 1. Carol --> Dave --> Alice --> Bob forward payment
|
||||
@ -262,239 +260,88 @@ func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) {
|
||||
// 3. Carol --- Dave X Alice <-- Bob settle last hop
|
||||
// 4. Carol --- Dave X X shutdown Bob, restart Alice
|
||||
// 5. Carol <-- Dave <-- Alice X expect settle to propagate
|
||||
func testSwitchOfflineDeliveryOutgoingOffline(
|
||||
net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
const chanAmt = btcutil.Amount(1000000)
|
||||
const pushAmt = btcutil.Amount(900000)
|
||||
var networkChans []*lnrpc.ChannelPoint
|
||||
|
||||
// Open a channel with 100k satoshis between Alice and Bob with Alice
|
||||
// being the sole funder of the channel.
|
||||
chanPointAlice := openChannelAndAssert(
|
||||
t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
PushAmt: pushAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointAlice)
|
||||
|
||||
aliceChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointAlice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
aliceFundPoint := wire.OutPoint{
|
||||
Hash: *aliceChanTXID,
|
||||
Index: chanPointAlice.OutputIndex,
|
||||
}
|
||||
|
||||
// As preliminary setup, we'll create two new nodes: Carol and Dave,
|
||||
// such that we now have a 4 ndoe, 3 channel topology. Dave will make
|
||||
// a channel with Alice, and Carol with Dave. After this setup, the
|
||||
// network topology should now look like:
|
||||
// Carol -> Dave -> Alice -> Bob
|
||||
//
|
||||
// First, we'll create Dave and establish a channel to Alice.
|
||||
dave := net.NewNode(t.t, "Dave", nil)
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
net.ConnectNodes(t.t, dave, net.Alice)
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave)
|
||||
|
||||
chanPointDave := openChannelAndAssert(
|
||||
t, net, dave, net.Alice,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
PushAmt: pushAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointDave)
|
||||
daveChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
daveFundPoint := wire.OutPoint{
|
||||
Hash: *daveChanTXID,
|
||||
Index: chanPointDave.OutputIndex,
|
||||
}
|
||||
|
||||
// Next, we'll create Carol and establish a channel to from her to
|
||||
// Dave. Carol is started in htlchodl mode so that we can disconnect the
|
||||
// intermediary hops before starting the settle.
|
||||
carol := net.NewNode(t.t, "Carol", []string{"--hodl.exit-settle"})
|
||||
net.ConnectNodes(t.t, carol, dave)
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
|
||||
|
||||
chanPointCarol := openChannelAndAssert(
|
||||
t, net, carol, dave,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
PushAmt: pushAmt,
|
||||
},
|
||||
)
|
||||
networkChans = append(networkChans, chanPointCarol)
|
||||
|
||||
carolChanTXID, err := lnrpc.GetChanPointFundingTxid(chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
carolFundPoint := wire.OutPoint{
|
||||
Hash: *carolChanTXID,
|
||||
Index: chanPointCarol.OutputIndex,
|
||||
}
|
||||
|
||||
// Wait for all nodes to have seen all channels.
|
||||
nodes := []*lntest.HarnessNode{net.Alice, net.Bob, carol, dave}
|
||||
nodeNames := []string{"Alice", "Bob", "Carol", "Dave"}
|
||||
for _, chanPoint := range networkChans {
|
||||
for i, node := range nodes {
|
||||
txid, err := lnrpc.GetChanPointFundingTxid(chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get txid: %v", err)
|
||||
}
|
||||
point := wire.OutPoint{
|
||||
Hash: *txid,
|
||||
Index: chanPoint.OutputIndex,
|
||||
}
|
||||
|
||||
err = node.WaitForNetworkChannelOpen(chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("%s(%d): timeout waiting for "+
|
||||
"channel(%s) open: %v", nodeNames[i],
|
||||
node.NodeID, point, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create 5 invoices for Carol, which expect a payment from Bob for 1k
|
||||
// satoshis with a different preimage each time.
|
||||
const numPayments = 5
|
||||
const paymentAmt = 1000
|
||||
payReqs, _, _, err := createPayReqs(
|
||||
carol, paymentAmt, numPayments,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
}
|
||||
|
||||
// We'll wait for all parties to recognize the new channels within the
|
||||
// network.
|
||||
err = dave.WaitForNetworkChannelOpen(chanPointDave)
|
||||
if err != nil {
|
||||
t.Fatalf("dave didn't advertise his channel: %v", err)
|
||||
}
|
||||
err = carol.WaitForNetworkChannelOpen(chanPointCarol)
|
||||
if err != nil {
|
||||
t.Fatalf("carol didn't advertise her channel in time: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// Using Carol as the source, pay to the 5 invoices from Bob created
|
||||
// above.
|
||||
err = completePaymentRequests(
|
||||
net.Bob, net.Bob.RouterClient, payReqs, false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payments: %v", err)
|
||||
}
|
||||
|
||||
// Wait for all payments to reach Carol.
|
||||
var predErr error
|
||||
err = wait.Predicate(func() bool {
|
||||
return assertNumActiveHtlcs(nodes, numPayments) == nil
|
||||
}, defaultTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("htlc mismatch: %v", predErr)
|
||||
}
|
||||
func testSwitchOfflineDeliveryOutgoingOffline(ht *lntemp.HarnessTest) {
|
||||
// Setup our test scenario. We should now have four nodes running with
|
||||
// three channels. Note that we won't call the cleanUp function here as
|
||||
// we will manually stop the node Carol and her channel.
|
||||
s := setupScenarioFourNodes(ht)
|
||||
|
||||
// Disconnect the two intermediaries, Alice and Dave, so that when carol
|
||||
// restarts, the response will be held by Dave.
|
||||
if err := net.StopNode(net.Alice); err != nil {
|
||||
t.Fatalf("unable to shutdown alice: %v", err)
|
||||
}
|
||||
restartAlice := ht.SuspendNode(s.alice)
|
||||
|
||||
// Now restart carol without hodl mode, to settle back the outstanding
|
||||
// payments.
|
||||
carol.SetExtraArgs(nil)
|
||||
if err := net.RestartNode(carol, nil); err != nil {
|
||||
t.Fatalf("Node restart failed: %v", err)
|
||||
}
|
||||
s.carol.SetExtraArgs(nil)
|
||||
ht.RestartNode(s.carol)
|
||||
|
||||
// Wait for Carol to report no outstanding htlcs.
|
||||
carolNode := []*lntest.HarnessNode{carol}
|
||||
err = wait.Predicate(func() bool {
|
||||
predErr = assertNumActiveHtlcs(carolNode, 0)
|
||||
if predErr != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
predErr = assertNumActiveHtlcsChanPoint(dave, carolFundPoint, 0)
|
||||
return predErr == nil
|
||||
}, defaultTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("htlc mismatch: %v", predErr)
|
||||
}
|
||||
ht.AssertNumActiveHtlcs(s.carol, 0)
|
||||
// As an intermediate node, Dave should now have zero outgoing HTLCs
|
||||
// and 5 incoming HTLCs from Alice.
|
||||
ht.AssertNumActiveHtlcs(s.dave, numPayments)
|
||||
|
||||
// Now check that the total amount was transferred from Dave to Carol.
|
||||
// The amount transferred should be exactly equal to the invoice total
|
||||
// payment amount, 5k satsohis.
|
||||
const amountPaid = int64(5000)
|
||||
assertAmountPaid(t, "Dave(local) => Carol(remote)", carol,
|
||||
carolFundPoint, int64(0), amountPaid)
|
||||
assertAmountPaid(t, "Dave(local) => Carol(remote)", dave,
|
||||
carolFundPoint, amountPaid, int64(0))
|
||||
ht.AssertAmountPaid(
|
||||
"Dave(local) => Carol(remote)", s.carol,
|
||||
s.chanPointCarolDave, int64(0), amountPaid,
|
||||
)
|
||||
ht.AssertAmountPaid(
|
||||
"Dave(local) => Carol(remote)", s.dave,
|
||||
s.chanPointCarolDave, amountPaid, int64(0),
|
||||
)
|
||||
|
||||
// Shutdown carol and leave her offline for the rest of the test. This
|
||||
// is critical, as we wish to see if Dave can propragate settles even if
|
||||
// the outgoing link is never revived.
|
||||
shutdownAndAssert(net, t, carol)
|
||||
ht.Shutdown(s.carol)
|
||||
|
||||
// Now restart Dave, ensuring he is both persisting the settles, and is
|
||||
// able to reforward them to Alice after recovering from a restart.
|
||||
if err := net.RestartNode(dave, nil); err != nil {
|
||||
t.Fatalf("unable to restart dave: %v", err)
|
||||
}
|
||||
if err = net.RestartNode(net.Alice, nil); err != nil {
|
||||
t.Fatalf("unable to restart alice: %v", err)
|
||||
}
|
||||
ht.RestartNode(s.dave)
|
||||
require.NoErrorf(ht, restartAlice(), "restart alice failed")
|
||||
|
||||
// Ensure that Dave is reconnected to Alice before waiting for the
|
||||
// htlcs to clear.
|
||||
net.EnsureConnected(t.t, dave, net.Alice)
|
||||
ht.EnsureConnected(s.dave, s.alice)
|
||||
|
||||
// Since Carol has been shutdown permanently, we will wait until all
|
||||
// other nodes in the network report no active htlcs.
|
||||
nodesMinusCarol := []*lntest.HarnessNode{net.Bob, net.Alice, dave}
|
||||
err = wait.Predicate(func() bool {
|
||||
predErr = assertNumActiveHtlcs(nodesMinusCarol, 0)
|
||||
return predErr == nil
|
||||
}, defaultTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("htlc mismatch: %v", predErr)
|
||||
}
|
||||
ht.AssertNumActiveHtlcs(s.alice, 0)
|
||||
ht.AssertNumActiveHtlcs(s.bob, 0)
|
||||
ht.AssertNumActiveHtlcs(s.dave, 0)
|
||||
|
||||
// When asserting the amount of satoshis moved, we'll factor in the
|
||||
// default base fee, as we didn't modify the fee structure when
|
||||
// creating the seed nodes in the network.
|
||||
const baseFee = 1
|
||||
|
||||
// At this point, all channels (minus Carol, who is shutdown) should
|
||||
// show a shift of 5k satoshis towards Carol. The order of asserts
|
||||
// corresponds to increasing of time is needed to embed the HTLC in
|
||||
// commitment transaction, in channel Bob->Alice->David, order is
|
||||
// David, Alice, Bob.
|
||||
assertAmountPaid(t, "Alice(local) => Dave(remote)", dave,
|
||||
daveFundPoint, int64(0), amountPaid+(baseFee*numPayments))
|
||||
assertAmountPaid(t, "Alice(local) => Dave(remote)", net.Alice,
|
||||
daveFundPoint, amountPaid+(baseFee*numPayments), int64(0))
|
||||
assertAmountPaid(t, "Bob(local) => Alice(remote)", net.Alice,
|
||||
aliceFundPoint, int64(0), amountPaid+((baseFee*numPayments)*2))
|
||||
assertAmountPaid(t, "Bob(local) => Alice(remote)", net.Bob,
|
||||
aliceFundPoint, amountPaid+(baseFee*numPayments)*2, int64(0))
|
||||
ht.AssertAmountPaid(
|
||||
"Alice(local) => Dave(remote)", s.dave, s.chanPointDaveAlice,
|
||||
int64(0), amountPaid+(baseFee*numPayments),
|
||||
)
|
||||
ht.AssertAmountPaid(
|
||||
"Alice(local) => Dave(remote)", s.alice, s.chanPointDaveAlice,
|
||||
amountPaid+(baseFee*numPayments), int64(0),
|
||||
)
|
||||
ht.AssertAmountPaid(
|
||||
"Bob(local) => Alice(remote)", s.alice, s.chanPointAliceBob,
|
||||
int64(0), amountPaid+((baseFee*numPayments)*2),
|
||||
)
|
||||
ht.AssertAmountPaid(
|
||||
"Bob(local) => Alice(remote)", s.bob, s.chanPointAliceBob,
|
||||
amountPaid+(baseFee*numPayments)*2, int64(0),
|
||||
)
|
||||
|
||||
closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false)
|
||||
closeChannelAndAssert(t, net, dave, chanPointDave, false)
|
||||
ht.CloseChannel(s.alice, s.chanPointAliceBob)
|
||||
ht.CloseChannel(s.dave, s.chanPointDaveAlice)
|
||||
}
|
||||
|
||||
// scenarioFourNodes specifies a scenario which we have a topology that has
|
||||
|
@ -28,10 +28,6 @@ var allTestCases = []*testCase{
|
||||
name: "async bidirectional payments",
|
||||
test: testBidirectionalAsyncPayments,
|
||||
},
|
||||
{
|
||||
name: "switch offline delivery outgoing offline",
|
||||
test: testSwitchOfflineDeliveryOutgoingOffline,
|
||||
},
|
||||
{
|
||||
name: "cpfp",
|
||||
test: testCPFP,
|
||||
|
Loading…
Reference in New Issue
Block a user