mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
itest: refactor testDataLossProtection
This commit is contained in:
parent
e8dc15dae4
commit
36c84bbd43
4 changed files with 50 additions and 371 deletions
|
@ -1101,198 +1101,6 @@ func assertNumPendingChannels(t *harnessTest, node *lntest.HarnessNode,
|
||||||
require.NoErrorf(t.t, err, "got err: %v", predErr)
|
require.NoErrorf(t.t, err, "got err: %v", predErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// assertDLPExecuted asserts that Dave is a node that has recovered their state
|
|
||||||
// form scratch. Carol should then force close on chain, with Dave sweeping his
|
|
||||||
// funds immediately, and Carol sweeping her fund after her CSV delay is up. If
|
|
||||||
// the blankSlate value is true, then this means that Dave won't need to sweep
|
|
||||||
// on chain as he has no funds in the channel.
|
|
||||||
func assertDLPExecutedOld(net *lntest.NetworkHarness, t *harnessTest,
|
|
||||||
carol *lntest.HarnessNode, carolStartingBalance int64,
|
|
||||||
dave *lntest.HarnessNode, daveStartingBalance int64,
|
|
||||||
commitType lnrpc.CommitmentType) {
|
|
||||||
|
|
||||||
// Increase the fee estimate so that the following force close tx will
|
|
||||||
// be cpfp'ed.
|
|
||||||
net.SetFeeEstimate(30000)
|
|
||||||
|
|
||||||
// We disabled auto-reconnect for some tests to avoid timing issues.
|
|
||||||
// To make sure the nodes are initiating DLP now, we have to manually
|
|
||||||
// re-connect them.
|
|
||||||
ctxb := context.Background()
|
|
||||||
net.EnsureConnected(t.t, carol, dave)
|
|
||||||
|
|
||||||
// Upon reconnection, the nodes should detect that Dave is out of sync.
|
|
||||||
// Carol should force close the channel using her latest commitment.
|
|
||||||
expectedTxes := 1
|
|
||||||
if commitTypeHasAnchors(commitType) {
|
|
||||||
expectedTxes = 2
|
|
||||||
}
|
|
||||||
_, err := waitForNTxsInMempool(
|
|
||||||
net.Miner.Client, expectedTxes, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(
|
|
||||||
t.t, err,
|
|
||||||
"unable to find Carol's force close tx in mempool",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Channel should be in the state "waiting close" for Carol since she
|
|
||||||
// broadcasted the force close tx.
|
|
||||||
assertNumPendingChannels(t, carol, 1, 0)
|
|
||||||
|
|
||||||
// Dave should also consider the channel "waiting close", as he noticed
|
|
||||||
// the channel was out of sync, and is now waiting for a force close to
|
|
||||||
// hit the chain.
|
|
||||||
assertNumPendingChannels(t, dave, 1, 0)
|
|
||||||
|
|
||||||
// Restart Dave to make sure he is able to sweep the funds after
|
|
||||||
// shutdown.
|
|
||||||
require.NoError(t.t, net.RestartNode(dave, nil), "Node restart failed")
|
|
||||||
|
|
||||||
// Generate a single block, which should confirm the closing tx.
|
|
||||||
_ = mineBlocks(t, net, 1, expectedTxes)[0]
|
|
||||||
|
|
||||||
// Dave should consider the channel pending force close (since he is
|
|
||||||
// waiting for his sweep to confirm).
|
|
||||||
assertNumPendingChannels(t, dave, 0, 1)
|
|
||||||
|
|
||||||
// Carol is considering it "pending force close", as we must wait
|
|
||||||
// before she can sweep her outputs.
|
|
||||||
assertNumPendingChannels(t, carol, 0, 1)
|
|
||||||
|
|
||||||
if commitType == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
|
|
||||||
// Dave should sweep his anchor only, since he still has the
|
|
||||||
// lease CLTV constraint on his commitment output.
|
|
||||||
_, err = waitForNTxsInMempool(
|
|
||||||
net.Miner.Client, 1, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err, "unable to find Dave's anchor sweep "+
|
|
||||||
"tx in mempool")
|
|
||||||
|
|
||||||
// Mine Dave's anchor sweep tx.
|
|
||||||
_ = mineBlocks(t, net, 1, 1)[0]
|
|
||||||
|
|
||||||
// After Carol's output matures, she should also reclaim her
|
|
||||||
// funds.
|
|
||||||
//
|
|
||||||
// The commit sweep resolver publishes the sweep tx at
|
|
||||||
// defaultCSV-1 and we already mined one block after the
|
|
||||||
// commitmment was published, so take that into account.
|
|
||||||
mineBlocks(t, net, defaultCSV-1-1, 0)
|
|
||||||
carolSweep, err := waitForTxInMempool(
|
|
||||||
net.Miner.Client, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err, "unable to find Carol's sweep tx in "+
|
|
||||||
"mempool")
|
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
|
||||||
assertTxInBlock(t, block, carolSweep)
|
|
||||||
|
|
||||||
// Now the channel should be fully closed also from Carol's POV.
|
|
||||||
assertNumPendingChannels(t, carol, 0, 0)
|
|
||||||
|
|
||||||
// We'll now mine the remaining blocks to prompt Dave to sweep
|
|
||||||
// his CLTV-constrained output.
|
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
defer cancel()
|
|
||||||
resp, err := dave.PendingChannels(
|
|
||||||
ctxt, &lnrpc.PendingChannelsRequest{},
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
blocksTilMaturity :=
|
|
||||||
resp.PendingForceClosingChannels[0].BlocksTilMaturity
|
|
||||||
require.Positive(t.t, blocksTilMaturity)
|
|
||||||
|
|
||||||
mineBlocks(t, net, uint32(blocksTilMaturity), 0)
|
|
||||||
daveSweep, err := waitForTxInMempool(
|
|
||||||
net.Miner.Client, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err, "unable to find Dave's sweep tx in "+
|
|
||||||
"mempool")
|
|
||||||
block = mineBlocks(t, net, 1, 1)[0]
|
|
||||||
assertTxInBlock(t, block, daveSweep)
|
|
||||||
|
|
||||||
// Now Dave should consider the channel fully closed.
|
|
||||||
assertNumPendingChannels(t, dave, 0, 0)
|
|
||||||
} else {
|
|
||||||
// Dave should sweep his funds immediately, as they are not
|
|
||||||
// timelocked. We also expect Dave to sweep his anchor, if
|
|
||||||
// present.
|
|
||||||
_, err = waitForNTxsInMempool(
|
|
||||||
net.Miner.Client, expectedTxes, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err, "unable to find Dave's sweep tx in "+
|
|
||||||
"mempool")
|
|
||||||
|
|
||||||
// Mine the sweep tx.
|
|
||||||
_ = mineBlocks(t, net, 1, expectedTxes)[0]
|
|
||||||
|
|
||||||
// Now Dave should consider the channel fully closed.
|
|
||||||
assertNumPendingChannels(t, dave, 0, 0)
|
|
||||||
|
|
||||||
// After Carol's output matures, she should also reclaim her
|
|
||||||
// funds.
|
|
||||||
//
|
|
||||||
// The commit sweep resolver publishes the sweep tx at
|
|
||||||
// defaultCSV-1 and we already mined one block after the
|
|
||||||
// commitmment was published, so take that into account.
|
|
||||||
mineBlocks(t, net, defaultCSV-1-1, 0)
|
|
||||||
carolSweep, err := waitForTxInMempool(
|
|
||||||
net.Miner.Client, minerMempoolTimeout,
|
|
||||||
)
|
|
||||||
require.NoError(t.t, err, "unable to find Carol's sweep tx in "+
|
|
||||||
"mempool")
|
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
|
||||||
assertTxInBlock(t, block, carolSweep)
|
|
||||||
|
|
||||||
// Now the channel should be fully closed also from Carol's POV.
|
|
||||||
assertNumPendingChannels(t, carol, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We query Dave's balance to make sure it increased after the channel
|
|
||||||
// closed. This checks that he was able to sweep the funds he had in
|
|
||||||
// the channel.
|
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
balReq := &lnrpc.WalletBalanceRequest{}
|
|
||||||
daveBalResp, err := dave.WalletBalance(ctxt, balReq)
|
|
||||||
require.NoError(t.t, err, "unable to get dave's balance")
|
|
||||||
|
|
||||||
daveBalance := daveBalResp.ConfirmedBalance
|
|
||||||
require.Greater(
|
|
||||||
t.t, daveBalance, daveStartingBalance, "balance not increased",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Make sure Carol got her balance back.
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
carolBalResp, err := carol.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get carol's balance: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
carolBalance := carolBalResp.ConfirmedBalance
|
|
||||||
|
|
||||||
// With Neutrino we don't get a backend error when trying to
|
|
||||||
// publish an orphan TX (which is what the sweep for the remote
|
|
||||||
// anchor is since the remote commitment TX was not broadcast).
|
|
||||||
// That's why the wallet still sees that as unconfirmed and we
|
|
||||||
// need to count the total balance instead of the confirmed.
|
|
||||||
if net.BackendCfg.Name() == lntest.NeutrinoBackendName {
|
|
||||||
carolBalance = carolBalResp.TotalBalance
|
|
||||||
}
|
|
||||||
|
|
||||||
if carolBalance <= carolStartingBalance {
|
|
||||||
return fmt.Errorf("expected carol to have balance "+
|
|
||||||
"above %d, instead had %v", carolStartingBalance,
|
|
||||||
carolBalance)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
assertNodeNumChannels(t, dave, 0)
|
|
||||||
assertNodeNumChannels(t, carol, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyCloseUpdate is used to verify that a closed channel update is of the
|
// verifyCloseUpdate is used to verify that a closed channel update is of the
|
||||||
// expected type.
|
// expected type.
|
||||||
func verifyCloseUpdate(chanUpdate *lnrpc.ChannelEventUpdate,
|
func verifyCloseUpdate(chanUpdate *lnrpc.ChannelEventUpdate,
|
||||||
|
|
|
@ -23,4 +23,8 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
Name: "channel backup restore",
|
Name: "channel backup restore",
|
||||||
TestFunc: testChannelBackupRestore,
|
TestFunc: testChannelBackupRestore,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "data loss protection",
|
||||||
|
TestFunc: testDataLossProtection,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1167,8 +1167,7 @@ func testChanRestoreScenario(ht *lntemp.HarnessTest,
|
||||||
// relationship lost state, they will detect this during channel sync, and the
|
// relationship lost state, they will detect this during channel sync, and the
|
||||||
// up-to-date party will force close the channel, giving the outdated party the
|
// up-to-date party will force close the channel, giving the outdated party the
|
||||||
// opportunity to sweep its output.
|
// opportunity to sweep its output.
|
||||||
func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
func testDataLossProtection(ht *lntemp.HarnessTest) {
|
||||||
ctxb := context.Background()
|
|
||||||
const (
|
const (
|
||||||
chanAmt = funding.MaxBtcFundingAmount
|
chanAmt = funding.MaxBtcFundingAmount
|
||||||
paymentAmt = 10000
|
paymentAmt = 10000
|
||||||
|
@ -1180,36 +1179,31 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
// protection logic automatically. We also can't have Carol
|
// protection logic automatically. We also can't have Carol
|
||||||
// automatically re-connect too early, otherwise DLP would be initiated
|
// automatically re-connect too early, otherwise DLP would be initiated
|
||||||
// at the wrong moment.
|
// at the wrong moment.
|
||||||
carol := net.NewNode(
|
carol := ht.NewNode("Carol", []string{"--nolisten", "--minbackoff=1h"})
|
||||||
t.t, "Carol", []string{"--nolisten", "--minbackoff=1h"},
|
|
||||||
)
|
|
||||||
defer shutdownAndAssert(net, t, carol)
|
|
||||||
|
|
||||||
// Dave will be the party losing his state.
|
// Dave will be the party losing his state.
|
||||||
dave := net.NewNode(t.t, "Dave", nil)
|
dave := ht.NewNode("Dave", nil)
|
||||||
defer shutdownAndAssert(net, t, dave)
|
|
||||||
|
|
||||||
// Before we make a channel, we'll load up Carol with some coins sent
|
// Before we make a channel, we'll load up Carol with some coins sent
|
||||||
// directly from the miner.
|
// directly from the miner.
|
||||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, carol)
|
||||||
|
|
||||||
// timeTravel is a method that will make Carol open a channel to the
|
// timeTravel is a method that will make Carol open a channel to the
|
||||||
// passed node, settle a series of payments, then reset the node back
|
// passed node, settle a series of payments, then reset the node back
|
||||||
// to the state before the payments happened. When this method returns
|
// to the state before the payments happened. When this method returns
|
||||||
// the node will be unaware of the new state updates. The returned
|
// the node will be unaware of the new state updates. The returned
|
||||||
// function can be used to restart the node in this state.
|
// function can be used to restart the node in this state.
|
||||||
timeTravel := func(node *lntest.HarnessNode) (func() error,
|
timeTravel := func(node *node.HarnessNode) (func() error,
|
||||||
*lnrpc.ChannelPoint, int64, error) {
|
*lnrpc.ChannelPoint, int64) {
|
||||||
|
|
||||||
// We must let the node communicate with Carol before they are
|
// We must let the node communicate with Carol before they are
|
||||||
// able to open channel, so we connect them.
|
// able to open channel, so we connect them.
|
||||||
net.EnsureConnected(t.t, carol, node)
|
ht.EnsureConnected(carol, node)
|
||||||
|
|
||||||
// We'll first open up a channel between them with a 0.5 BTC
|
// We'll first open up a channel between them with a 0.5 BTC
|
||||||
// value.
|
// value.
|
||||||
chanPoint := openChannelAndAssert(
|
chanPoint := ht.OpenChannel(
|
||||||
t, net, carol, node,
|
carol, node, lntemp.OpenChannelParams{
|
||||||
lntest.OpenChannelParams{
|
|
||||||
Amt: chanAmt,
|
Amt: chanAmt,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1219,54 +1213,18 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
// the channel.
|
// the channel.
|
||||||
// TODO(halseth): have dangling HTLCs on the commitment, able to
|
// TODO(halseth): have dangling HTLCs on the commitment, able to
|
||||||
// retrieve funds?
|
// retrieve funds?
|
||||||
payReqs, _, _, err := createPayReqs(
|
payReqs, _, _ := ht.CreatePayReqs(node, paymentAmt, numInvoices)
|
||||||
node, paymentAmt, numInvoices,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to create pay reqs: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for Carol to receive the channel edge from the funding
|
|
||||||
// manager.
|
|
||||||
err = carol.WaitForNetworkChannelOpen(chanPoint)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("carol didn't see the carol->%s channel "+
|
|
||||||
"before timeout: %v", node.Name(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send payments from Carol using 3 of the payment hashes
|
// Send payments from Carol using 3 of the payment hashes
|
||||||
// generated above.
|
// generated above.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequests(carol, payReqs[:numInvoices/2])
|
||||||
carol, carol.RouterClient,
|
|
||||||
payReqs[:numInvoices/2], true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to send payments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next query for the node's channel state, as we sent 3
|
// Next query for the node's channel state, as we sent 3
|
||||||
// payments of 10k satoshis each, it should now see his balance
|
// payments of 10k satoshis each, it should now see his balance
|
||||||
// as being 30k satoshis.
|
// as being 30k satoshis.
|
||||||
var nodeChan *lnrpc.Channel
|
nodeChan := ht.AssertChannelLocalBalance(
|
||||||
var predErr error
|
node, chanPoint, 30_000,
|
||||||
err = wait.Predicate(func() bool {
|
)
|
||||||
bChan, err := getChanInfo(node)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get channel info: %v", err)
|
|
||||||
}
|
|
||||||
if bChan.LocalBalance != 30000 {
|
|
||||||
predErr = fmt.Errorf("balance is incorrect, "+
|
|
||||||
"got %v, expected %v",
|
|
||||||
bChan.LocalBalance, 30000)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeChan = bChan
|
|
||||||
return true
|
|
||||||
}, defaultTimeout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v", predErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the current commitment height (update number), we'll
|
// Grab the current commitment height (update number), we'll
|
||||||
// later revert him to this state after additional updates to
|
// later revert him to this state after additional updates to
|
||||||
|
@ -1276,94 +1234,53 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
// With the temporary file created, copy the current state into
|
// With the temporary file created, copy the current state into
|
||||||
// the temporary file we created above. Later after more
|
// the temporary file we created above. Later after more
|
||||||
// updates, we'll restore this state.
|
// updates, we'll restore this state.
|
||||||
if err := net.BackupDb(node); err != nil {
|
ht.BackupDB(node)
|
||||||
t.Fatalf("unable to copy database files: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconnect the peers after the restart that was needed for the db
|
// Reconnect the peers after the restart that was needed for
|
||||||
// backup.
|
// the db backup.
|
||||||
net.EnsureConnected(t.t, carol, node)
|
ht.EnsureConnected(carol, node)
|
||||||
|
|
||||||
// Finally, send more payments from , using the remaining
|
// Finally, send more payments from , using the remaining
|
||||||
// payment hashes.
|
// payment hashes.
|
||||||
err = completePaymentRequests(
|
ht.CompletePaymentRequests(carol, payReqs[numInvoices/2:])
|
||||||
carol, carol.RouterClient, payReqs[numInvoices/2:], true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to send payments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeChan, err = getChanInfo(node)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get dave chan info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we shutdown the node, copying over the its temporary
|
// Now we shutdown the node, copying over the its temporary
|
||||||
// database state which has the *prior* channel state over his
|
// database state which has the *prior* channel state over his
|
||||||
// current most up to date state. With this, we essentially
|
// current most up to date state. With this, we essentially
|
||||||
// force the node to travel back in time within the channel's
|
// force the node to travel back in time within the channel's
|
||||||
// history.
|
// history.
|
||||||
if err = net.RestartNode(node, func() error {
|
ht.RestartNodeAndRestoreDB(node)
|
||||||
return net.RestoreDb(node)
|
|
||||||
}); err != nil {
|
|
||||||
t.Fatalf("unable to restart node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the channel is still there from the PoV of the
|
// Make sure the channel is still there from the PoV of the
|
||||||
// node.
|
// node.
|
||||||
assertNodeNumChannels(t, node, 1)
|
ht.AssertNodeNumChannels(node, 1)
|
||||||
|
|
||||||
// Now query for the channel state, it should show that it's at
|
// Now query for the channel state, it should show that it's at
|
||||||
// a state number in the past, not the *latest* state.
|
// a state number in the past, not the *latest* state.
|
||||||
nodeChan, err = getChanInfo(node)
|
ht.AssertChannelNumUpdates(node, stateNumPreCopy, chanPoint)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get dave chan info: %v", err)
|
|
||||||
}
|
|
||||||
if nodeChan.NumUpdates != stateNumPreCopy {
|
|
||||||
t.Fatalf("db copy failed: %v", nodeChan.NumUpdates)
|
|
||||||
}
|
|
||||||
|
|
||||||
balReq := &lnrpc.WalletBalanceRequest{}
|
balResp := node.RPC.WalletBalance()
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
restart := ht.SuspendNode(node)
|
||||||
balResp, err := node.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get dave's balance: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
restart, err := net.SuspendNode(node)
|
return restart, chanPoint, balResp.ConfirmedBalance
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to suspend node: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return restart, chanPoint, balResp.ConfirmedBalance, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset Dave to a state where he has an outdated channel state.
|
// Reset Dave to a state where he has an outdated channel state.
|
||||||
restartDave, _, daveStartingBalance, err := timeTravel(dave)
|
restartDave, _, daveStartingBalance := timeTravel(dave)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to time travel dave: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We make a note of the nodes' current on-chain balances, to make sure
|
// We make a note of the nodes' current on-chain balances, to make sure
|
||||||
// they are able to retrieve the channel funds eventually,
|
// they are able to retrieve the channel funds eventually,
|
||||||
balReq := &lnrpc.WalletBalanceRequest{}
|
carolBalResp := carol.RPC.WalletBalance()
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
carolBalResp, err := carol.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol's balance: %v", err)
|
|
||||||
}
|
|
||||||
carolStartingBalance := carolBalResp.ConfirmedBalance
|
carolStartingBalance := carolBalResp.ConfirmedBalance
|
||||||
|
|
||||||
// Restart Dave to trigger a channel resync.
|
// Restart Dave to trigger a channel resync.
|
||||||
if err := restartDave(); err != nil {
|
require.NoError(ht, restartDave(), "unable to restart dave")
|
||||||
t.Fatalf("unable to restart dave: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that once Dave comes up, they reconnect, Carol force closes
|
// Assert that once Dave comes up, they reconnect, Carol force closes
|
||||||
// on chain, and both of them properly carry out the DLP protocol.
|
// on chain, and both of them properly carry out the DLP protocol.
|
||||||
assertDLPExecutedOld(
|
assertDLPExecuted(
|
||||||
net, t, carol, carolStartingBalance, dave, daveStartingBalance,
|
ht, carol, carolStartingBalance, dave,
|
||||||
lnrpc.CommitmentType_STATIC_REMOTE_KEY,
|
daveStartingBalance, lnrpc.CommitmentType_STATIC_REMOTE_KEY,
|
||||||
)
|
)
|
||||||
|
|
||||||
// As a second part of this test, we will test the scenario where a
|
// As a second part of this test, we will test the scenario where a
|
||||||
|
@ -1373,93 +1290,47 @@ func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
// closed channel, such that Dave can retrieve his funds.
|
// closed channel, such that Dave can retrieve his funds.
|
||||||
//
|
//
|
||||||
// We start by letting Dave time travel back to an outdated state.
|
// We start by letting Dave time travel back to an outdated state.
|
||||||
restartDave, chanPoint2, daveStartingBalance, err := timeTravel(dave)
|
restartDave, chanPoint2, daveStartingBalance := timeTravel(dave)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to time travel eve: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
carolBalResp = carol.RPC.WalletBalance()
|
||||||
carolBalResp, err = carol.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol's balance: %v", err)
|
|
||||||
}
|
|
||||||
carolStartingBalance = carolBalResp.ConfirmedBalance
|
carolStartingBalance = carolBalResp.ConfirmedBalance
|
||||||
|
|
||||||
// Now let Carol force close the channel while Dave is offline.
|
// Now let Carol force close the channel while Dave is offline.
|
||||||
closeChannelAndAssert(t, net, carol, chanPoint2, true)
|
ht.ForceCloseChannel(carol, chanPoint2)
|
||||||
|
|
||||||
// Wait for the channel to be marked pending force close.
|
|
||||||
err = waitForChannelPendingForceClose(carol, chanPoint2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("channel not pending force close: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine enough blocks for Carol to sweep her funds.
|
|
||||||
mineBlocks(t, net, defaultCSV-1, 0)
|
|
||||||
|
|
||||||
carolSweep, err := waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to find Carol's sweep tx in mempool: %v", err)
|
|
||||||
}
|
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
|
||||||
assertTxInBlock(t, block, carolSweep)
|
|
||||||
|
|
||||||
// Now the channel should be fully closed also from Carol's POV.
|
|
||||||
assertNumPendingChannels(t, carol, 0, 0)
|
|
||||||
|
|
||||||
// Make sure Carol got her balance back.
|
// Make sure Carol got her balance back.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
carolBalResp = carol.RPC.WalletBalance()
|
||||||
carolBalResp, err = carol.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to get carol's balance: %v", err)
|
|
||||||
}
|
|
||||||
carolBalance := carolBalResp.ConfirmedBalance
|
carolBalance := carolBalResp.ConfirmedBalance
|
||||||
if carolBalance <= carolStartingBalance {
|
require.Greater(ht, carolBalance, carolStartingBalance,
|
||||||
t.Fatalf("expected carol to have balance above %d, "+
|
"expected carol to have balance increased")
|
||||||
"instead had %v", carolStartingBalance,
|
|
||||||
carolBalance)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNodeNumChannels(t, carol, 0)
|
ht.AssertNodeNumChannels(carol, 0)
|
||||||
|
|
||||||
// When Dave comes online, he will reconnect to Carol, try to resync
|
// When Dave comes online, he will reconnect to Carol, try to resync
|
||||||
// the channel, but it will already be closed. Carol should resend the
|
// the channel, but it will already be closed. Carol should resend the
|
||||||
// information Dave needs to sweep his funds.
|
// information Dave needs to sweep his funds.
|
||||||
if err := restartDave(); err != nil {
|
require.NoError(ht, restartDave(), "unable to restart Eve")
|
||||||
t.Fatalf("unable to restart Eve: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dave should sweep his funds.
|
// Dave should sweep his funds.
|
||||||
_, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
ht.Miner.AssertNumTxsInMempool(1)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to find Dave's sweep tx in mempool: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine a block to confirm the sweep, and make sure Dave got his
|
// Mine a block to confirm the sweep, and make sure Dave got his
|
||||||
// balance back.
|
// balance back.
|
||||||
mineBlocks(t, net, 1, 1)
|
ht.Miner.MineBlocksAndAssertNumTxes(1, 1)
|
||||||
assertNodeNumChannels(t, dave, 0)
|
ht.AssertNodeNumChannels(dave, 0)
|
||||||
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
daveBalResp, err := dave.WalletBalance(ctxt, balReq)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get dave's balance: %v",
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
daveBalResp := dave.RPC.WalletBalance()
|
||||||
daveBalance := daveBalResp.ConfirmedBalance
|
daveBalance := daveBalResp.ConfirmedBalance
|
||||||
if daveBalance <= daveStartingBalance {
|
if daveBalance <= daveStartingBalance {
|
||||||
return fmt.Errorf("expected dave to have balance "+
|
return fmt.Errorf("expected dave to have balance "+
|
||||||
"above %d, instead had %v", daveStartingBalance,
|
"above %d, intead had %v", daveStartingBalance,
|
||||||
daveBalance)
|
daveBalance)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, defaultTimeout)
|
}, defaultTimeout)
|
||||||
if err != nil {
|
require.NoError(ht, err, "timeout while checking dave's balance")
|
||||||
t.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createLegacyRevocationChannel creates a single channel using the legacy
|
// createLegacyRevocationChannel creates a single channel using the legacy
|
||||||
|
|
|
@ -223,10 +223,6 @@ var allTestCases = []*testCase{
|
||||||
name: "revoked uncooperative close retribution altruist watchtower",
|
name: "revoked uncooperative close retribution altruist watchtower",
|
||||||
test: testRevokedCloseRetributionAltruistWatchtower,
|
test: testRevokedCloseRetributionAltruistWatchtower,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "data loss protection",
|
|
||||||
test: testDataLossProtection,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "query routes",
|
name: "query routes",
|
||||||
test: testQueryRoutes,
|
test: testQueryRoutes,
|
||||||
|
|
Loading…
Add table
Reference in a new issue