mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
itest: move testDataLossProtection
to backup test file
This commit is contained in:
parent
8cd5a9b6b1
commit
1fafabe012
@ -15,6 +15,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/chanbackup"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lntemp"
|
||||
@ -1162,6 +1163,305 @@ func testChanRestoreScenario(ht *lntemp.HarnessTest,
|
||||
)
|
||||
}
|
||||
|
||||
// testDataLossProtection tests that if one of the nodes in a channel
|
||||
// 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
|
||||
// opportunity to sweep its output.
|
||||
func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
const (
|
||||
chanAmt = funding.MaxBtcFundingAmount
|
||||
paymentAmt = 10000
|
||||
numInvoices = 6
|
||||
)
|
||||
|
||||
// Carol will be the up-to-date party. We set --nolisten to ensure Dave
|
||||
// won't be able to connect to her and trigger the channel data
|
||||
// protection logic automatically. We also can't have Carol
|
||||
// automatically re-connect too early, otherwise DLP would be initiated
|
||||
// at the wrong moment.
|
||||
carol := net.NewNode(
|
||||
t.t, "Carol", []string{"--nolisten", "--minbackoff=1h"},
|
||||
)
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
// Dave will be the party losing his state.
|
||||
dave := net.NewNode(t.t, "Dave", nil)
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
// Before we make a channel, we'll load up Carol with some coins sent
|
||||
// directly from the miner.
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
|
||||
|
||||
// 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
|
||||
// to the state before the payments happened. When this method returns
|
||||
// the node will be unaware of the new state updates. The returned
|
||||
// function can be used to restart the node in this state.
|
||||
timeTravel := func(node *lntest.HarnessNode) (func() error,
|
||||
*lnrpc.ChannelPoint, int64, error) {
|
||||
|
||||
// We must let the node communicate with Carol before they are
|
||||
// able to open channel, so we connect them.
|
||||
net.EnsureConnected(t.t, carol, node)
|
||||
|
||||
// We'll first open up a channel between them with a 0.5 BTC
|
||||
// value.
|
||||
chanPoint := openChannelAndAssert(
|
||||
t, net, carol, node,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// With the channel open, we'll create a few invoices for the
|
||||
// node that Carol will pay to in order to advance the state of
|
||||
// the channel.
|
||||
// TODO(halseth): have dangling HTLCs on the commitment, able to
|
||||
// retrieve funds?
|
||||
payReqs, _, _, err := createPayReqs(
|
||||
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
|
||||
// generated above.
|
||||
err = completePaymentRequests(
|
||||
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
|
||||
// payments of 10k satoshis each, it should now see his balance
|
||||
// as being 30k satoshis.
|
||||
var nodeChan *lnrpc.Channel
|
||||
var predErr error
|
||||
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
|
||||
// later revert him to this state after additional updates to
|
||||
// revoke this state.
|
||||
stateNumPreCopy := nodeChan.NumUpdates
|
||||
|
||||
// With the temporary file created, copy the current state into
|
||||
// the temporary file we created above. Later after more
|
||||
// updates, we'll restore this state.
|
||||
if err := net.BackupDb(node); err != nil {
|
||||
t.Fatalf("unable to copy database files: %v", err)
|
||||
}
|
||||
|
||||
// Reconnect the peers after the restart that was needed for the db
|
||||
// backup.
|
||||
net.EnsureConnected(t.t, carol, node)
|
||||
|
||||
// Finally, send more payments from , using the remaining
|
||||
// payment hashes.
|
||||
err = completePaymentRequests(
|
||||
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
|
||||
// database state which has the *prior* channel state over his
|
||||
// current most up to date state. With this, we essentially
|
||||
// force the node to travel back in time within the channel's
|
||||
// history.
|
||||
if err = net.RestartNode(node, func() error {
|
||||
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
|
||||
// node.
|
||||
assertNodeNumChannels(t, node, 1)
|
||||
|
||||
// Now query for the channel state, it should show that it's at
|
||||
// a state number in the past, not the *latest* state.
|
||||
nodeChan, err = getChanInfo(node)
|
||||
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{}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
balResp, err := node.WalletBalance(ctxt, balReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get dave's balance: %v", err)
|
||||
}
|
||||
|
||||
restart, err := net.SuspendNode(node)
|
||||
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.
|
||||
restartDave, _, daveStartingBalance, err := 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
|
||||
// they are able to retrieve the channel funds eventually,
|
||||
balReq := &lnrpc.WalletBalanceRequest{}
|
||||
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
|
||||
|
||||
// Restart Dave to trigger a channel resync.
|
||||
if err := restartDave(); err != nil {
|
||||
t.Fatalf("unable to restart dave: %v", err)
|
||||
}
|
||||
|
||||
// Assert that once Dave comes up, they reconnect, Carol force closes
|
||||
// on chain, and both of them properly carry out the DLP protocol.
|
||||
assertDLPExecutedOld(
|
||||
net, t, carol, carolStartingBalance, dave, daveStartingBalance,
|
||||
lnrpc.CommitmentType_STATIC_REMOTE_KEY,
|
||||
)
|
||||
|
||||
// As a second part of this test, we will test the scenario where a
|
||||
// channel is closed while Dave is offline, loses his state and comes
|
||||
// back online. In this case the node should attempt to resync the
|
||||
// channel, and the peer should resend a channel sync message for the
|
||||
// closed channel, such that Dave can retrieve his funds.
|
||||
//
|
||||
// We start by letting Dave time travel back to an outdated state.
|
||||
restartDave, chanPoint2, daveStartingBalance, err := timeTravel(dave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to time travel eve: %v", err)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Now let Carol force close the channel while Dave is offline.
|
||||
closeChannelAndAssert(t, net, carol, chanPoint2, true)
|
||||
|
||||
// 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.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
carolBalResp, err = carol.WalletBalance(ctxt, balReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get carol's balance: %v", err)
|
||||
}
|
||||
carolBalance := carolBalResp.ConfirmedBalance
|
||||
if carolBalance <= carolStartingBalance {
|
||||
t.Fatalf("expected carol to have balance above %d, "+
|
||||
"instead had %v", carolStartingBalance,
|
||||
carolBalance)
|
||||
}
|
||||
|
||||
assertNodeNumChannels(t, carol, 0)
|
||||
|
||||
// When Dave comes online, he will reconnect to Carol, try to resync
|
||||
// the channel, but it will already be closed. Carol should resend the
|
||||
// information Dave needs to sweep his funds.
|
||||
if err := restartDave(); err != nil {
|
||||
t.Fatalf("unable to restart Eve: %v", err)
|
||||
}
|
||||
|
||||
// Dave should sweep his funds.
|
||||
_, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
||||
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
|
||||
// balance back.
|
||||
mineBlocks(t, net, 1, 1)
|
||||
assertNodeNumChannels(t, 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)
|
||||
}
|
||||
|
||||
daveBalance := daveBalResp.ConfirmedBalance
|
||||
if daveBalance <= daveStartingBalance {
|
||||
return fmt.Errorf("expected dave to have balance "+
|
||||
"above %d, instead had %v", daveStartingBalance,
|
||||
daveBalance)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, defaultTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// createLegacyRevocationChannel creates a single channel using the legacy
|
||||
// revocation producer format by using PSBT to signal a special pending channel
|
||||
// ID.
|
||||
|
@ -821,305 +821,6 @@ func testGarbageCollectLinkNodes(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
closeChannelAndAssert(t, net, net.Alice, persistentChanPoint, false)
|
||||
}
|
||||
|
||||
// testDataLossProtection tests that if one of the nodes in a channel
|
||||
// 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
|
||||
// opportunity to sweep its output.
|
||||
func testDataLossProtection(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
const (
|
||||
chanAmt = funding.MaxBtcFundingAmount
|
||||
paymentAmt = 10000
|
||||
numInvoices = 6
|
||||
)
|
||||
|
||||
// Carol will be the up-to-date party. We set --nolisten to ensure Dave
|
||||
// won't be able to connect to her and trigger the channel data
|
||||
// protection logic automatically. We also can't have Carol
|
||||
// automatically re-connect too early, otherwise DLP would be initiated
|
||||
// at the wrong moment.
|
||||
carol := net.NewNode(
|
||||
t.t, "Carol", []string{"--nolisten", "--minbackoff=1h"},
|
||||
)
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
// Dave will be the party losing his state.
|
||||
dave := net.NewNode(t.t, "Dave", nil)
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
// Before we make a channel, we'll load up Carol with some coins sent
|
||||
// directly from the miner.
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, carol)
|
||||
|
||||
// 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
|
||||
// to the state before the payments happened. When this method returns
|
||||
// the node will be unaware of the new state updates. The returned
|
||||
// function can be used to restart the node in this state.
|
||||
timeTravel := func(node *lntest.HarnessNode) (func() error,
|
||||
*lnrpc.ChannelPoint, int64, error) {
|
||||
|
||||
// We must let the node communicate with Carol before they are
|
||||
// able to open channel, so we connect them.
|
||||
net.EnsureConnected(t.t, carol, node)
|
||||
|
||||
// We'll first open up a channel between them with a 0.5 BTC
|
||||
// value.
|
||||
chanPoint := openChannelAndAssert(
|
||||
t, net, carol, node,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// With the channel open, we'll create a few invoices for the
|
||||
// node that Carol will pay to in order to advance the state of
|
||||
// the channel.
|
||||
// TODO(halseth): have dangling HTLCs on the commitment, able to
|
||||
// retrieve funds?
|
||||
payReqs, _, _, err := createPayReqs(
|
||||
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
|
||||
// generated above.
|
||||
err = completePaymentRequests(
|
||||
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
|
||||
// payments of 10k satoshis each, it should now see his balance
|
||||
// as being 30k satoshis.
|
||||
var nodeChan *lnrpc.Channel
|
||||
var predErr error
|
||||
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
|
||||
// later revert him to this state after additional updates to
|
||||
// revoke this state.
|
||||
stateNumPreCopy := nodeChan.NumUpdates
|
||||
|
||||
// With the temporary file created, copy the current state into
|
||||
// the temporary file we created above. Later after more
|
||||
// updates, we'll restore this state.
|
||||
if err := net.BackupDb(node); err != nil {
|
||||
t.Fatalf("unable to copy database files: %v", err)
|
||||
}
|
||||
|
||||
// Reconnect the peers after the restart that was needed for the db
|
||||
// backup.
|
||||
net.EnsureConnected(t.t, carol, node)
|
||||
|
||||
// Finally, send more payments from , using the remaining
|
||||
// payment hashes.
|
||||
err = completePaymentRequests(
|
||||
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
|
||||
// database state which has the *prior* channel state over his
|
||||
// current most up to date state. With this, we essentially
|
||||
// force the node to travel back in time within the channel's
|
||||
// history.
|
||||
if err = net.RestartNode(node, func() error {
|
||||
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
|
||||
// node.
|
||||
assertNodeNumChannels(t, node, 1)
|
||||
|
||||
// Now query for the channel state, it should show that it's at
|
||||
// a state number in the past, not the *latest* state.
|
||||
nodeChan, err = getChanInfo(node)
|
||||
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{}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
balResp, err := node.WalletBalance(ctxt, balReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get dave's balance: %v", err)
|
||||
}
|
||||
|
||||
restart, err := net.SuspendNode(node)
|
||||
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.
|
||||
restartDave, _, daveStartingBalance, err := 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
|
||||
// they are able to retrieve the channel funds eventually,
|
||||
balReq := &lnrpc.WalletBalanceRequest{}
|
||||
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
|
||||
|
||||
// Restart Dave to trigger a channel resync.
|
||||
if err := restartDave(); err != nil {
|
||||
t.Fatalf("unable to restart dave: %v", err)
|
||||
}
|
||||
|
||||
// Assert that once Dave comes up, they reconnect, Carol force closes
|
||||
// on chain, and both of them properly carry out the DLP protocol.
|
||||
assertDLPExecutedOld(
|
||||
net, t, carol, carolStartingBalance, dave, daveStartingBalance,
|
||||
lnrpc.CommitmentType_STATIC_REMOTE_KEY,
|
||||
)
|
||||
|
||||
// As a second part of this test, we will test the scenario where a
|
||||
// channel is closed while Dave is offline, loses his state and comes
|
||||
// back online. In this case the node should attempt to resync the
|
||||
// channel, and the peer should resend a channel sync message for the
|
||||
// closed channel, such that Dave can retrieve his funds.
|
||||
//
|
||||
// We start by letting Dave time travel back to an outdated state.
|
||||
restartDave, chanPoint2, daveStartingBalance, err := timeTravel(dave)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to time travel eve: %v", err)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Now let Carol force close the channel while Dave is offline.
|
||||
closeChannelAndAssert(t, net, carol, chanPoint2, true)
|
||||
|
||||
// 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.
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
carolBalResp, err = carol.WalletBalance(ctxt, balReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get carol's balance: %v", err)
|
||||
}
|
||||
carolBalance := carolBalResp.ConfirmedBalance
|
||||
if carolBalance <= carolStartingBalance {
|
||||
t.Fatalf("expected carol to have balance above %d, "+
|
||||
"instead had %v", carolStartingBalance,
|
||||
carolBalance)
|
||||
}
|
||||
|
||||
assertNodeNumChannels(t, carol, 0)
|
||||
|
||||
// When Dave comes online, he will reconnect to Carol, try to resync
|
||||
// the channel, but it will already be closed. Carol should resend the
|
||||
// information Dave needs to sweep his funds.
|
||||
if err := restartDave(); err != nil {
|
||||
t.Fatalf("unable to restart Eve: %v", err)
|
||||
}
|
||||
|
||||
// Dave should sweep his funds.
|
||||
_, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
||||
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
|
||||
// balance back.
|
||||
mineBlocks(t, net, 1, 1)
|
||||
assertNodeNumChannels(t, 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)
|
||||
}
|
||||
|
||||
daveBalance := daveBalResp.ConfirmedBalance
|
||||
if daveBalance <= daveStartingBalance {
|
||||
return fmt.Errorf("expected dave to have balance "+
|
||||
"above %d, instead had %v", daveStartingBalance,
|
||||
daveBalance)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, defaultTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// testRejectHTLC tests that a node can be created with the flag --rejecthtlc.
|
||||
// This means that the node will reject all forwarded HTLCs but can still
|
||||
// accept direct HTLCs as well as send HTLCs.
|
||||
|
Loading…
Reference in New Issue
Block a user