github.com/decred/dcrlnd@v0.7.6/lntest/itest/dcrlnd_revoked_retribution_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/decred/dcrd/chaincfg/chainhash"
    10  	"github.com/decred/dcrd/dcrutil/v4"
    11  	jsonrpctypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4"
    12  	"github.com/decred/dcrd/wire"
    13  	"github.com/decred/dcrlnd/lnrpc"
    14  	"github.com/decred/dcrlnd/lntest"
    15  	"github.com/decred/dcrlnd/lntest/wait"
    16  	"github.com/decred/dcrlnd/routing"
    17  	"github.com/stretchr/testify/require"
    18  	"matheusd.com/testctx"
    19  )
    20  
    21  // testRevokedCloseRetributionRemoteHodlSecondLevel tests that Dave properly
    22  // responds to a channel breach made by the remote party, specifically in the
    23  // case that the remote party breaches before settling extended HTLCs.
    24  //
    25  // In this test we specifically wait for Carol to redeem her HTLCs via a second
    26  // level transaction before initiating Dave's retribution detection and justice
    27  // service.
    28  //
    29  // This is a dcrlnd-only test, split off from the original
    30  // testRevokedCloseRetributionRemoteHodl.
    31  func testRevokedCloseRetributionRemoteHodlSecondLevel(net *lntest.NetworkHarness,
    32  	t *harnessTest) {
    33  
    34  	// Currently disabled in SPV due to
    35  	// https://github.com/decred/dcrlnd/issues/96. Re-assess after that is
    36  	// fixed.
    37  	if net.BackendCfg.Name() == "spv" {
    38  		t.Skipf("Skipping for SPV for the moment")
    39  	}
    40  
    41  	const (
    42  		initialBalance = int64(dcrutil.AtomsPerCoin)
    43  		chanAmt        = defaultChanAmt
    44  		pushAmt        = 400000
    45  		paymentAmt     = 20000
    46  		numInvoices    = 6
    47  	)
    48  
    49  	// In order to make the test non-flaky and easier to reason about,
    50  	// we'll redefine the CSV and CLTV limits for Carol and Dave. We'll
    51  	// define these limits such that, after force-closing the channel (with
    52  	// carol's older, revoked state) we can mine a few blocks before she'll
    53  	// attempt to sweep the HTLCs.
    54  
    55  	var (
    56  		// The new CLTV delta will be the minimum the node will accept
    57  		// + 4 so that it doesn't immediately close the channel after
    58  		// restarting.
    59  		cltvDelta = routing.MinCLTVDelta + 4
    60  
    61  		// The new CSV delay will be the previous delta + 2 so that
    62  		// Carol doesn't attempt to redeem the CSV-encumbered
    63  		// commitment output before Dave can send the justice tx.
    64  		csvDelay = cltvDelta + 2 + int(routing.BlockPadding)
    65  	)
    66  
    67  	// Since this test will result in the counterparty being left in a
    68  	// weird state, we will introduce another node into our test network:
    69  	// Carol.
    70  	carol := net.NewNode(t.t, "Carol", []string{
    71  		"--hodl.exit-settle",
    72  		fmt.Sprintf("--timelockdelta=%d", cltvDelta),
    73  		fmt.Sprintf("--defaultremotedelay=%d", csvDelay),
    74  	})
    75  	defer shutdownAndAssert(net, t, carol)
    76  
    77  	// We'll also create a new node Dave, who will have a channel with
    78  	// Carol, and also use similar settings so we can broadcast a commit
    79  	// with active HTLCs. Dave will be the breached party. We set
    80  	// --nolisten to ensure Carol won't be able to connect to him and
    81  	// trigger the channel data protection logic automatically.
    82  	dave := net.NewNode(t.t, "Dave", []string{
    83  		"--hodl.exit-settle",
    84  		"--nolisten",
    85  		fmt.Sprintf("--timelockdelta=%d", cltvDelta),
    86  		fmt.Sprintf("--defaultremotedelay=%d", csvDelay),
    87  	})
    88  	defer shutdownAndAssert(net, t, dave)
    89  
    90  	// We must let Dave communicate with Carol before they are able to open
    91  	// channel, so we connect Dave and Carol,
    92  	net.ConnectNodes(t.t, dave, carol)
    93  
    94  	// Before we make a channel, we'll load up Dave with some coins sent
    95  	// directly from the miner.
    96  	net.SendCoins(t.t, dcrutil.Amount(initialBalance), dave)
    97  
    98  	// In order to test Dave's response to an uncooperative channel closure
    99  	// by Carol, we'll first open up a channel between them with a
   100  	// defaultChanAmt (2^24) atoms value.
   101  	chanPoint := openChannelAndAssert(
   102  		t, net, dave, carol,
   103  		lntest.OpenChannelParams{
   104  			Amt:     chanAmt,
   105  			PushAmt: pushAmt,
   106  		},
   107  	)
   108  
   109  	// Store the channel type for later.
   110  	cType, err := channelCommitType(carol, chanPoint)
   111  	if err != nil {
   112  		t.Fatalf("unable to get channel type: %v", err)
   113  	}
   114  
   115  	// With the channel open, we'll create a few invoices for Carol that
   116  	// Dave will pay to in order to advance the state of the channel.
   117  	carolPayReqs, _, _, err := createPayReqs(
   118  		carol, paymentAmt, numInvoices,
   119  	)
   120  	if err != nil {
   121  		t.Fatalf("unable to create pay reqs: %v", err)
   122  	}
   123  
   124  	// We'll introduce a closure to validate that Carol's current balance
   125  	// matches the given expected amount.
   126  	checkCarolBalance := func(expectedAmt int64) {
   127  		carolChan, err := getChanInfo(carol)
   128  		if err != nil {
   129  			t.Fatalf("unable to get carol's channel info: %v", err)
   130  		}
   131  		if carolChan.LocalBalance != expectedAmt {
   132  			t.Fatalf("carol's balance is incorrect, "+
   133  				"got %v, expected %v", carolChan.LocalBalance,
   134  				expectedAmt)
   135  		}
   136  	}
   137  
   138  	// We'll introduce another closure to validate that Carol's current
   139  	// number of updates is at least as large as the provided minimum
   140  	// number.
   141  	checkCarolNumUpdatesAtLeast := func(minimum uint64) {
   142  		carolChan, err := getChanInfo(carol)
   143  		if err != nil {
   144  			t.Fatalf("unable to get carol's channel info: %v", err)
   145  		}
   146  		if carolChan.NumUpdates < minimum {
   147  			t.Fatalf("carol's numupdates is incorrect, want %v "+
   148  				"to be at least %v", carolChan.NumUpdates,
   149  				minimum)
   150  		}
   151  	}
   152  
   153  	// Wait for Dave to receive the channel edge from the funding manager.
   154  	err = dave.WaitForNetworkChannelOpen(chanPoint)
   155  	if err != nil {
   156  		t.Fatalf("dave didn't see the dave->carol channel before "+
   157  			"timeout: %v", err)
   158  	}
   159  
   160  	// Ensure that carol's balance starts with the amount we pushed to her.
   161  	checkCarolBalance(pushAmt)
   162  
   163  	// Send payments from Dave to Carol using 3 of Carol's payment hashes
   164  	// generated above.
   165  	err = completePaymentRequests(
   166  		dave, dave.RouterClient, carolPayReqs[:numInvoices/2], false,
   167  	)
   168  	if err != nil {
   169  		t.Fatalf("unable to send payments: %v", err)
   170  	}
   171  
   172  	// At this point, we'll also send over a set of HTLC's from Carol to
   173  	// Dave. This ensures that the final revoked transaction has HTLC's in
   174  	// both directions.
   175  	davePayReqs, _, _, err := createPayReqs(
   176  		dave, paymentAmt, numInvoices,
   177  	)
   178  	if err != nil {
   179  		t.Fatalf("unable to create pay reqs: %v", err)
   180  	}
   181  
   182  	// Send payments from Carol to Dave using 3 of Dave's payment hashes
   183  	// generated above.
   184  	err = completePaymentRequests(
   185  		carol, carol.RouterClient, davePayReqs[:numInvoices/2], false,
   186  	)
   187  	if err != nil {
   188  		t.Fatalf("unable to send payments: %v", err)
   189  	}
   190  
   191  	// Next query for Carol's channel state, as we sent 3 payments of 10k
   192  	// atoms each, however Carol should now see her balance as being
   193  	// equal to the push amount in atoms since she has not settled.
   194  	carolChan, err := getChanInfo(carol)
   195  	if err != nil {
   196  		t.Fatalf("unable to get carol's channel info: %v", err)
   197  	}
   198  
   199  	// Grab Carol's current commitment height (update number), we'll later
   200  	// revert her to this state after additional updates to force her to
   201  	// broadcast this soon to be revoked state.
   202  	carolStateNumPreCopy := carolChan.NumUpdates
   203  
   204  	// Ensure that carol's balance still reflects the original amount we
   205  	// pushed to her, minus the HTLCs she just sent to Dave.
   206  	checkCarolBalance(pushAmt - 3*paymentAmt)
   207  
   208  	// Since Carol has not settled, she should only see at least one update
   209  	// to her channel.
   210  	checkCarolNumUpdatesAtLeast(1)
   211  
   212  	// With the temporary file created, copy Carol's current state into the
   213  	// temporary file we created above. Later after more updates, we'll
   214  	// restore this state.
   215  	if err := net.BackupDb(carol); err != nil {
   216  		t.Fatalf("unable to copy database files: %v", err)
   217  	}
   218  
   219  	// Ensure Carol is connected to Dave after restart.
   220  	net.EnsureConnected(t.t, dave, carol)
   221  
   222  	// Finally, send payments from Dave to Carol, consuming Carol's
   223  	// remaining payment hashes.
   224  	err = completePaymentRequests(
   225  		dave, dave.RouterClient, carolPayReqs[numInvoices/2:], false,
   226  	)
   227  	if err != nil {
   228  		t.Fatalf("unable to send payments: %v", err)
   229  	}
   230  
   231  	// Ensure that carol's balance still shows the amount we originally
   232  	// pushed to her (minus the HTLCs she sent to Bob), and that at least
   233  	// one more update has occurred.
   234  	time.Sleep(500 * time.Millisecond)
   235  	checkCarolBalance(pushAmt - 3*paymentAmt)
   236  	checkCarolNumUpdatesAtLeast(carolStateNumPreCopy + 1)
   237  
   238  	// Disconnect Carol and Dave, so that the channel isn't corrected once Carol
   239  	// is restarted.
   240  	err = net.DisconnectNodes(dave, carol)
   241  	if err != nil {
   242  		t.Fatalf("unable to disconnect dave and carol: %v", err)
   243  	}
   244  
   245  	time.Sleep(500 * time.Millisecond)
   246  
   247  	// Suspend Dave to prevent him from sending a justice tx redeeming the
   248  	// funds from the breach tx before Carol has a chance to redeem the
   249  	// second level HTLCs.
   250  	resumeDave, err := net.SuspendNode(dave)
   251  	if err != nil {
   252  		t.Fatalf("unable to suspend Dave: %v", err)
   253  	}
   254  
   255  	// Now we shutdown Carol, copying over the her temporary database state
   256  	// which has the *prior* channel state over her current most up to date
   257  	// state. With this, we essentially force Carol to travel back in time
   258  	// within the channel's history.
   259  	if err = net.RestartNode(carol, func() error {
   260  		return net.RestoreDb(carol)
   261  	}); err != nil {
   262  		t.Fatalf("unable to restart node: %v", err)
   263  	}
   264  
   265  	time.Sleep(200 * time.Millisecond)
   266  
   267  	// Ensure that Carol's view of the channel is consistent with the state
   268  	// of the channel just before it was snapshotted.
   269  	checkCarolBalance(pushAmt - 3*paymentAmt)
   270  	checkCarolNumUpdatesAtLeast(1)
   271  
   272  	// Now query for Carol's channel state, it should show that she's at a
   273  	// state number in the past, *not* the latest state.
   274  	carolChan, err = getChanInfo(carol)
   275  	if err != nil {
   276  		t.Fatalf("unable to get carol chan info: %v", err)
   277  	}
   278  	if carolChan.NumUpdates != carolStateNumPreCopy {
   279  		t.Fatalf("db copy failed: %v", carolChan.NumUpdates)
   280  	}
   281  
   282  	// Now force Carol to execute a *force* channel closure by unilaterally
   283  	// broadcasting her current channel state. This is actually the
   284  	// commitment transaction of a prior *revoked* state, so she'll soon
   285  	// feel the wrath of Dave's retribution.
   286  	force := true
   287  	closeUpdates, closeTxId, err := net.CloseChannel(carol, chanPoint, force)
   288  	require.Nil(t.t, err)
   289  
   290  	// Query the mempool for the breaching closing transaction, this should
   291  	// be broadcast by Carol when she force closes the channel above.
   292  	txid, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
   293  	if err != nil {
   294  		t.Fatalf("unable to find Carol's force close tx in mempool: %v",
   295  			err)
   296  	}
   297  	if *txid != *closeTxId {
   298  		t.Fatalf("expected closeTx(%v) in mempool, instead found %v",
   299  			closeTxId, txid)
   300  	}
   301  
   302  	// Generate a single block to mine the breach transaction.
   303  	block := mineBlocks(t, net, 1, 1)[0]
   304  
   305  	// Wait for the final close status update, then ensure that the closing
   306  	// transaction was included in the block.
   307  	breachTXID, err := net.WaitForChannelClose(closeUpdates)
   308  	require.Nil(t.t, err)
   309  	if *breachTXID != *closeTxId {
   310  		t.Fatalf("expected breach ID(%v) to be equal to close ID (%v)",
   311  			breachTXID, closeTxId)
   312  	}
   313  	assertTxInBlock(t, block, breachTXID)
   314  
   315  	// isSecondLevelSpend checks that the passed secondLevelTxid is a
   316  	// potential second level spend spending from the commit tx.
   317  	isSecondLevelSpend := func(commitTxid, secondLevelTxid *chainhash.Hash) (bool, *wire.MsgTx) {
   318  		secondLevel, err := net.Miner.Node.GetRawTransaction(
   319  			context.Background(), secondLevelTxid)
   320  		if err != nil {
   321  			t.Fatalf("unable to query for tx: %v", err)
   322  		}
   323  
   324  		// A second level spend should have only one input, and one
   325  		// output.
   326  		if len(secondLevel.MsgTx().TxIn) != 1 {
   327  			return false, nil
   328  		}
   329  		if len(secondLevel.MsgTx().TxOut) != 1 {
   330  			return false, nil
   331  		}
   332  
   333  		// The sole input should be spending from the commit tx.
   334  		txIn := secondLevel.MsgTx().TxIn[0]
   335  		fromCommit := bytes.Equal(txIn.PreviousOutPoint.Hash[:], commitTxid[:])
   336  		if !fromCommit {
   337  			return false, nil
   338  		}
   339  
   340  		return true, secondLevel.MsgTx()
   341  	}
   342  
   343  	// Grab transactions which will be required for future checks.
   344  	breachTx, err := net.Miner.Node.GetRawTransaction(context.Background(), closeTxId)
   345  	if err != nil {
   346  		t.Fatalf("unable to query for breach tx: %v", err)
   347  	}
   348  
   349  	fundingTxId, err := chainhash.NewHash(chanPoint.GetFundingTxidBytes())
   350  	if err != nil {
   351  		t.Fatalf("chainpoint id bytes not a chainhash: %v", err)
   352  	}
   353  	fundingTx, err := net.Miner.Node.GetRawTransaction(context.Background(), fundingTxId)
   354  	if err != nil {
   355  		t.Fatalf("unable to query for funding tx: %v", err)
   356  	}
   357  
   358  	// After force-closing the channel, Carol should send 3 second-level
   359  	// success htlc transactions to redeem her offered htlcs (for which she
   360  	// has the preimage).
   361  	//
   362  	// We'll ensure she has actually sent them and record the fees to
   363  	// correctly account for them in Dave's final balance.
   364  	txids, err := waitForNTxsInMempool(net.Miner.Node, 3, minerMempoolTimeout)
   365  	var numSecondLvlSuccess int
   366  	var feesSecondLvlSuccess int64
   367  	if err != nil {
   368  		t.Fatalf("unable to wait for htlc success transactions: %v", err)
   369  	}
   370  	for _, txid := range txids {
   371  		isSecondLevel, tx := isSecondLevelSpend(breachTXID, txid)
   372  		if !isSecondLevel {
   373  			continue
   374  		}
   375  		numSecondLvlSuccess++
   376  		prevOut := breachTx.MsgTx().TxOut[tx.TxIn[0].PreviousOutPoint.Index]
   377  		feesSecondLvlSuccess += prevOut.Value - tx.TxOut[0].Value
   378  	}
   379  	if numSecondLvlSuccess != 3 {
   380  		t.Fatalf("Carol did not send the expected second-level htlc "+
   381  			"success txs (found %d)", len(txids))
   382  	}
   383  
   384  	// Mine enough blocks for Carol to attempt to redeem the timed-out
   385  	// htlcs via second-level timeout txs.
   386  	numBlocks := padCLTV(uint32(cltvDelta - 1))
   387  	mineBlocks(t, net, numBlocks, 0)
   388  
   389  	// Wait for Carol to send the timeout second-level txs, then ensure
   390  	// they can be found on the mempool, that they redeem from the breach
   391  	// tx and that they are in fact second-level HTLC txs.
   392  	mempool, err := waitForNTxsInMempool(net.Miner.Node, 3, minerMempoolTimeout)
   393  	if err != nil {
   394  		t.Fatalf("unable to get mempool from miner: %v", err)
   395  	}
   396  	var numSecondLvlTimeout int
   397  	var totalSecondLvlTimeout int64
   398  	var feesSecondLvlTimeout int64
   399  	for _, txid := range mempool {
   400  		isSecondLevel, tx := isSecondLevelSpend(breachTXID, txid)
   401  		if !isSecondLevel {
   402  			continue
   403  		}
   404  		prevOut := breachTx.MsgTx().TxOut[tx.TxIn[0].PreviousOutPoint.Index]
   405  		numSecondLvlTimeout++
   406  		totalSecondLvlTimeout += tx.TxOut[0].Value
   407  		feesSecondLvlTimeout += prevOut.Value - tx.TxOut[0].Value
   408  	}
   409  	if numSecondLvlTimeout != 3 {
   410  		t.Fatalf("unable to find the 3 htlc timeout second-level txs "+
   411  			"from Carol (found %d)", numSecondLvlTimeout)
   412  	}
   413  
   414  	// The total redeemed by the second-level timeout txs should be the
   415  	// amount for 3 time-locked invoices minus the fees.
   416  	expectedSecondLvlTimeoutOut := 3*paymentAmt - feesSecondLvlTimeout
   417  	if totalSecondLvlTimeout != expectedSecondLvlTimeoutOut {
   418  		t.Fatalf("unexpected total redeemed by second-level txs; "+
   419  			"expected=%d actual=%d fees=%d", expectedSecondLvlTimeoutOut,
   420  			totalSecondLvlTimeout, feesSecondLvlTimeout)
   421  	}
   422  
   423  	// Mine a block with the timeout second-level HTLCs.
   424  	mineBlocks(t, net, 1, 3)
   425  
   426  	// Restart Dave. He should notice the breached commitment transaction
   427  	// and the second-level txs. Once he does, he will send a justice tx to
   428  	// sweep the time-locked funds from Carol's breach tx, his side of the
   429  	// commitment tx, the time-locked 3 HTLCs in the success second-level
   430  	// txs and the 3 time-locked HTLCs in the timeout second-level txs, for
   431  	// a grand total of 8 inputs.
   432  	err = resumeDave()
   433  	if err != nil {
   434  		t.Fatalf("unable to resume Dave: %v", err)
   435  	}
   436  
   437  	// Query the mempool for Dave's justice transaction.
   438  	var predErr error
   439  	var justiceTxid *chainhash.Hash
   440  	exNumInputs := 2 + numInvoices
   441  	errNotFound := fmt.Errorf("justice tx with %d inputs not found", exNumInputs)
   442  	findJusticeTx := func() (*chainhash.Hash, error) {
   443  		mempool, err := net.Miner.Node.GetRawMempool(context.Background(), jsonrpctypes.GRMRegular)
   444  		if err != nil {
   445  			return nil, fmt.Errorf("unable to get mempool from "+
   446  				"miner: %v", err)
   447  		}
   448  
   449  		for _, txid := range mempool {
   450  			// Check that the justice tx has the appropriate number
   451  			// of inputs.
   452  			tx, err := net.Miner.Node.GetRawTransaction(context.Background(), txid)
   453  			if err != nil {
   454  				return nil, fmt.Errorf("unable to query for "+
   455  					"txs: %v", err)
   456  			}
   457  
   458  			if len(tx.MsgTx().TxIn) == exNumInputs {
   459  				return txid, nil
   460  			}
   461  		}
   462  		return nil, errNotFound
   463  	}
   464  	err = wait.Predicate(func() bool {
   465  		txid, err := findJusticeTx()
   466  		if err != nil {
   467  			predErr = err
   468  			return false
   469  		}
   470  
   471  		justiceTxid = txid
   472  		return true
   473  	}, defaultTimeout)
   474  	if err != nil && predErr == errNotFound {
   475  		// If Dave is unable to broadcast his justice tx on first
   476  		// attempt because of the second layer transactions, he will
   477  		// wait until the next block epoch before trying again. Because
   478  		// of this, we'll mine a block if we cannot find the justice tx
   479  		// immediately. Since we cannot tell for sure how many
   480  		// transactions will be in the mempool at this point, we pass 0
   481  		// as the last argument, indicating we don't care what's in the
   482  		// mempool.
   483  		mineBlocks(t, net, 1, 0)
   484  		err = wait.Predicate(func() bool {
   485  			txid, err := findJusticeTx()
   486  			if err != nil {
   487  				predErr = err
   488  				return false
   489  			}
   490  
   491  			justiceTxid = txid
   492  			return true
   493  		}, defaultTimeout)
   494  	}
   495  	if err != nil {
   496  		t.Fatalf(predErr.Error())
   497  	}
   498  
   499  	justiceTx, err := net.Miner.Node.GetRawTransaction(context.Background(), justiceTxid)
   500  	if err != nil {
   501  		t.Fatalf("unable to query for justice tx: %v", err)
   502  	}
   503  
   504  	// Check that all the inputs of this transaction are spending outputs
   505  	// generated by Carol's breach transaction above or by one of the
   506  	// second-level txs.
   507  	var totalJusticeIn int64
   508  	var justiceTxNumSecondLvlIn int
   509  	for _, txIn := range justiceTx.MsgTx().TxIn {
   510  		if bytes.Equal(txIn.PreviousOutPoint.Hash[:], breachTXID[:]) {
   511  			txo := breachTx.MsgTx().TxOut[txIn.PreviousOutPoint.Index]
   512  			totalJusticeIn += txo.Value
   513  			continue
   514  		}
   515  		if is, tx := isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash); is {
   516  			totalJusticeIn += tx.TxOut[0].Value
   517  			justiceTxNumSecondLvlIn++
   518  			continue
   519  		}
   520  		t.Fatalf("justice tx not spending commitment or second-level "+
   521  			"utxo; instead is: %v", txIn.PreviousOutPoint)
   522  	}
   523  	if justiceTxNumSecondLvlIn != 6 {
   524  		t.Fatalf("justice tx does not have 3 inputs from second-level "+
   525  			"txs (found %d)", justiceTxNumSecondLvlIn)
   526  	}
   527  
   528  	// The amount returned by the justice tx must be the total channel
   529  	// amount minus the (breached) commitment tx fee, the second-level fees
   530  	// and the justice tx fee itself.
   531  	jtx := justiceTx.MsgTx()
   532  	commitFee := int64(calcStaticFee(cType, 6))
   533  	justiceFee := totalJusticeIn - jtx.TxOut[0].Value
   534  	expectedJusticeOut := int64(chanAmt) - commitFee - justiceFee -
   535  		feesSecondLvlSuccess - feesSecondLvlTimeout
   536  	if jtx.TxOut[0].Value != expectedJusticeOut {
   537  		t.Fatalf("wrong value returned by justice tx; expected=%d "+
   538  			"actual=%d chanAmt=%d commitFee=%d justiceFee=%d "+
   539  			"secondLevelSuccessFees=%d secondLevelTimeoutFees=%d",
   540  			expectedJusticeOut, jtx.TxOut[0].Value, chanAmt,
   541  			commitFee, justiceFee, feesSecondLvlSuccess,
   542  			feesSecondLvlTimeout)
   543  	}
   544  
   545  	// Now mine a block. This should include Dave's justice transaction
   546  	// which was just accepted into the mempool.
   547  	block = mineBlocks(t, net, 1, 1)[0]
   548  	assertTxInBlock(t, block, justiceTxid)
   549  
   550  	// Dave and Carol should have no open channels.
   551  	assertNodeNumChannels(t, dave, 0)
   552  	assertNodeNumChannels(t, carol, 0)
   553  
   554  	// We'll now check that the total balance of each party is the one we
   555  	// expect.  Introduce a new test closure.
   556  	checkTotalBalance := func(node *lntest.HarnessNode, expectedAmt int64) {
   557  		balances, err := node.WalletBalance(testctx.New(t), &lnrpc.WalletBalanceRequest{})
   558  		if err != nil {
   559  			t.Fatalf("unable to query node balance: %v", err)
   560  		}
   561  		if balances.TotalBalance != expectedAmt {
   562  			t.Fatalf("balance is incorrect, "+
   563  				"got %v, expected %v", balances.TotalBalance,
   564  				expectedAmt)
   565  		}
   566  	}
   567  
   568  	// Carol should not have any balance, because even though she was sent
   569  	// coins via an HTLC, she chose to broadcast an older commitment tx and
   570  	// forfeit her coins.
   571  	checkTotalBalance(carol, 0)
   572  
   573  	// Dave should have the initial amount sent to him for funding channels
   574  	// minus the fees required to broadcast the breached commitment with 6
   575  	// HTLCs and the justice tx.
   576  	fundingFee := recordedTxFee(fundingTx.MsgTx())
   577  	expectedDaveBalance := initialBalance - fundingFee - commitFee -
   578  		feesSecondLvlSuccess - feesSecondLvlTimeout - justiceFee
   579  	checkTotalBalance(dave, expectedDaveBalance)
   580  
   581  }