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

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/decred/dcrd/dcrutil/v4"
     9  	"github.com/decred/dcrlnd/lnrpc"
    10  	"github.com/decred/dcrlnd/lntest"
    11  	"github.com/stretchr/testify/require"
    12  	"matheusd.com/testctx"
    13  )
    14  
    15  // testAddInvoiceMaxInboundAmt tests whether trying to add an invoice fails
    16  // under various circumstances when taking into account the maximum inbound
    17  // amount available in directly connected channels.
    18  func testAddInvoiceMaxInboundAmt(net *lntest.NetworkHarness, t *harnessTest) {
    19  	ctxb := context.Background()
    20  
    21  	// Create a Carol node to use in tests. All invoices will be created on
    22  	// her node.
    23  	carol := net.NewNode(t.t, "Carol", []string{"--nolisten"})
    24  	defer shutdownAndAssert(net, t, carol)
    25  
    26  	net.ConnectNodes(t.t, carol, net.Bob)
    27  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol)
    28  
    29  	// Closure to help on tests.
    30  	addInvoice := func(value int64, ignoreMaxInbound bool) error {
    31  		invoice := &lnrpc.Invoice{
    32  			Value:               value,
    33  			IgnoreMaxInboundAmt: ignoreMaxInbound,
    34  		}
    35  		ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
    36  		_, err := carol.AddInvoice(ctxt, invoice)
    37  		return err
    38  	}
    39  
    40  	// Test that adding an invoice when Carol doesn't have any open channels
    41  	// fails.
    42  	err := addInvoice(0, false)
    43  	if err == nil {
    44  		t.Fatalf("adding invoice without open channels should return an error")
    45  	}
    46  	if !strings.Contains(err.Error(), "no open channels") {
    47  		t.Fatalf("adding invoice without open channels should return " +
    48  			"correct error")
    49  	}
    50  
    51  	// Same test, but ignoring inbound amounts succeeds.
    52  	err = addInvoice(0, true)
    53  	if err != nil {
    54  		t.Fatalf("adding invoice ignoring inbound should succeed without open " +
    55  			"channels")
    56  	}
    57  
    58  	// Now open a channel from Carol -> Bob.
    59  	chanAmt := int64(1000000)
    60  	pushAmt := chanAmt / 2
    61  	chanReserve := chanAmt / 100
    62  	channelParam := lntest.OpenChannelParams{
    63  		Amt:     dcrutil.Amount(chanAmt),
    64  		PushAmt: dcrutil.Amount(pushAmt),
    65  	}
    66  	chanPoint := openChannelAndAssert(
    67  		t, net, carol, net.Bob, channelParam)
    68  
    69  	// Test various scenarios with the channel open. The maximum amount receivable
    70  	// from a channel should be the remote channel balance minus our required
    71  	// channel reserve to it.
    72  	testCasesWithChan := []struct {
    73  		value         int64
    74  		ignoreInbound bool
    75  		valid         bool
    76  		descr         string
    77  	}{
    78  		// Test accounting for max inbound amount.
    79  		{0, false, true, "zero amount"},
    80  		{1, false, true, "one amount"},
    81  		{pushAmt - chanReserve, false, true, "maximum amount"},
    82  		{pushAmt - chanReserve + 1, false, false, "maximum amount +1"},
    83  		{pushAmt, false, false, "total push amount"},
    84  		{chanAmt, false, false, "total chan amount"},
    85  
    86  		// Test ignoring max inbound amount.
    87  		{pushAmt - chanReserve + 1, true, true, "maximum amount +1"},
    88  		{pushAmt, true, true, "total push amount"},
    89  		{chanAmt, true, true, "total chan amount"},
    90  	}
    91  
    92  	for _, tc := range testCasesWithChan {
    93  		err := addInvoice(tc.value, tc.ignoreInbound)
    94  		if tc.valid && err != nil {
    95  			t.Fatalf("case %s with ignore %v returned error '%v' but should "+
    96  				"have returned nil", tc.descr, tc.ignoreInbound, err)
    97  		}
    98  		if !tc.valid && err == nil {
    99  			t.Fatalf("case %s with ignore %v returned valid but should "+
   100  				"have returned error", tc.descr, tc.ignoreInbound)
   101  		}
   102  		if !tc.valid && !strings.Contains(err.Error(), "not enough inbound capacity") {
   103  			t.Fatalf("case %s with ignore %v did not return expected "+
   104  				"'not enough capacity' error (returned '%v')", tc.descr,
   105  				tc.ignoreInbound, err)
   106  		}
   107  	}
   108  
   109  	peerReq := &lnrpc.PeerEventSubscription{}
   110  	carolPeerClient, err := carol.SubscribePeerEvents(ctxb, peerReq)
   111  	require.NoError(t.t, err)
   112  
   113  	// Disconnect the nodes from one another. While their channel remains open,
   114  	// carol cannot receive payments (since bob is offline from her POV).
   115  	err = net.DisconnectNodes(carol, net.Bob)
   116  	if err != nil {
   117  		t.Fatalf("unable to disconnect carol and bob: %v", err)
   118  	}
   119  
   120  	// Wait to receive the PEER_OFFLINE event before trying to create the
   121  	// invoice.
   122  	peerEvent, err := carolPeerClient.Recv()
   123  	require.NoError(t.t, err)
   124  	require.Equal(t.t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType())
   125  
   126  	// Now trying to add an invoice with an offline peer should fail.
   127  	err = addInvoice(0, false)
   128  	if err == nil {
   129  		t.Fatalf("adding invoice without online peer should return an error")
   130  	}
   131  	if !strings.Contains(err.Error(), "no online channels found") {
   132  		t.Fatalf("adding invoice without online channels should return " +
   133  			"correct error")
   134  	}
   135  
   136  	// But adding an invoice ignoring inbound capacity should still work
   137  	err = addInvoice(0, true)
   138  	if err != nil {
   139  		t.Fatalf("adding invoice ignoring inbound should succeed without open " +
   140  			"channels")
   141  	}
   142  
   143  	// Force-close and cleanup the channel, since bob & carol are disconnected.
   144  	closeChannelAndAssert(t, net, carol, chanPoint, true)
   145  	cleanupForceClose(t, net, carol, chanPoint)
   146  }
   147  
   148  // testAddReceiveInvoiceMaxInboundAmt tests whether, after verifying that an
   149  // invoice can be added _without_ ignoring the maximum inbound capacity, that
   150  // same invoice can actually be paid by another node.
   151  //
   152  // In particular, it tests invoices at the limit of draining the channel.
   153  func testAddReceiveInvoiceMaxInboundAmt(net *lntest.NetworkHarness, t *harnessTest) {
   154  	ctxb := context.Background()
   155  
   156  	// Create and fund a Carol node to use in tests. All invoices will be
   157  	// created on her node.
   158  	carol := net.NewNode(t.t, "Carol", nil)
   159  	defer shutdownAndAssert(net, t, carol)
   160  
   161  	net.ConnectNodes(t.t, carol, net.Bob)
   162  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol)
   163  
   164  	// Now open a channel from Carol -> Bob.
   165  	chanAmt := int64(1000000)
   166  	pushAmt := chanAmt / 2
   167  	chanReserve := chanAmt / 100
   168  	channelParam := lntest.OpenChannelParams{
   169  		Amt:     dcrutil.Amount(chanAmt),
   170  		PushAmt: dcrutil.Amount(pushAmt),
   171  	}
   172  	chanPointCarol := openChannelAndAssert(
   173  		t, net, carol, net.Bob, channelParam)
   174  
   175  	// Also open a channel from Alice -> bob. Alice will attempt the payments.
   176  	channelParam = lntest.OpenChannelParams{
   177  		Amt: dcrutil.Amount(chanAmt),
   178  	}
   179  	chanPointAlice := openChannelAndAssert(
   180  		t, net, net.Alice, net.Bob, channelParam)
   181  
   182  	// Sanity check that payments one atom larger than the channel capacity -
   183  	// reserve cannot be paid.
   184  	maxInboundCap := pushAmt - chanReserve
   185  	ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
   186  	_, err := carol.AddInvoice(ctxt, &lnrpc.Invoice{Value: maxInboundCap + 1})
   187  	if err == nil {
   188  		t.Fatalf("adding an invoice for maxInboundCap + 1 should fail")
   189  	}
   190  
   191  	// Generate a valid invoice with the maximum amount possible.
   192  	ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   193  	invoice, err := carol.AddInvoice(ctxt, &lnrpc.Invoice{Value: maxInboundCap})
   194  	if err != nil {
   195  		t.Fatalf("unable to add invoice at maxInboundCap: %v", err)
   196  	}
   197  
   198  	// Try and pay this invoice from Alice. It should succeed.
   199  	sendReq := &lnrpc.SendRequest{PaymentRequest: invoice.PaymentRequest}
   200  	ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   201  	resp, err := net.Alice.SendPaymentSync(ctxt, sendReq)
   202  	if err != nil {
   203  		t.Fatalf("unable to send payment: %v", err)
   204  	}
   205  	if resp.PaymentError != "" {
   206  		t.Fatalf("error when attempting recv: %v", resp.PaymentError)
   207  	}
   208  
   209  	// Verify the inbound channel balance is now 0.
   210  	ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   211  	balances, err := carol.ChannelBalance(ctxt, &lnrpc.ChannelBalanceRequest{})
   212  	if err != nil {
   213  		t.Fatalf("unable to obtain balances: %v", err)
   214  	}
   215  	if balances.MaxInboundAmount != 0 {
   216  		t.Fatalf("max inbound amount not drained (%d remaining)",
   217  			balances.MaxInboundAmount)
   218  	}
   219  
   220  	// Try to generate a new invoice with value of one atom. It should fail,
   221  	// since we have now drained the inbound capacity of Carol.
   222  	ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   223  	_, err = carol.AddInvoice(ctxt, &lnrpc.Invoice{Value: 1})
   224  	if err == nil {
   225  		t.Fatalf("invoice sould not be generated after draining " +
   226  			"inbound capacity")
   227  	}
   228  
   229  	closeChannelAndAssert(t, net, carol, chanPointCarol, false)
   230  	closeChannelAndAssert(t, net, net.Alice, chanPointAlice, false)
   231  }
   232  
   233  // testSendPaymentMaxAmt tests whether trying to send an invoice fails under
   234  // various circumstances when taking into account the maximum outbound amount
   235  // available in directly connected channels.
   236  func testSendPaymentMaxOutboundAmt(net *lntest.NetworkHarness, t *harnessTest) {
   237  	ctxb := context.Background()
   238  
   239  	// Create a Carol node to use in tests. All invoices will be created on
   240  	// her node.
   241  	carol := net.NewNode(t.t, "Carol", []string{"--nolisten"})
   242  	defer shutdownAndAssert(net, t, carol)
   243  
   244  	net.ConnectNodes(t.t, carol, net.Bob)
   245  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol)
   246  
   247  	// Create an invoice with zero amount on Bob for the next tests.
   248  	invoice, err := net.Bob.AddInvoice(testctx.New(t), &lnrpc.Invoice{IgnoreMaxInboundAmt: true})
   249  	if err != nil {
   250  		t.Fatalf("unable to create invoice in Bob: %v", err)
   251  	}
   252  
   253  	// Closure to help on tests.
   254  	sendPayment := func(value int64) error {
   255  		// Dummy send request to Bob node that fails due to wrong
   256  		// payment hash.
   257  		sendReq := &lnrpc.SendRequest{
   258  			PaymentRequest: invoice.PaymentRequest,
   259  			Amt:            value,
   260  		}
   261  		ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
   262  		resp, err := carol.SendPaymentSync(ctxt, sendReq)
   263  		if err != nil {
   264  			return err
   265  		}
   266  		if resp.PaymentError != "" {
   267  			return errors.New(resp.PaymentError)
   268  		}
   269  		return nil
   270  	}
   271  
   272  	// Test that adding an invoice when Carol doesn't have any open channels
   273  	// fails.
   274  	err = sendPayment(1)
   275  	if err == nil {
   276  		t.Fatalf("sending payment without open channels should return an error")
   277  	}
   278  	if !strings.Contains(err.Error(), "no open channels") {
   279  		t.Fatalf("sending payment without open channels should return " +
   280  			"correct error")
   281  	}
   282  
   283  	// Now open a channel from Carol -> Bob.
   284  	ctype := lnrpc.CommitmentType_LEGACY
   285  	chanAmt := int64(1000000)
   286  	pushAmt := chanAmt / 2
   287  	chanReserve := chanAmt / 100
   288  	txFee := calcStaticFee(ctype, 0)
   289  	peerHtlcFee := calcStaticFee(ctype, 1) - calcStaticFee(ctype, 0)
   290  	localAmt := chanAmt - pushAmt - int64(txFee)
   291  	maxPayAmt := localAmt - chanReserve
   292  	channelParam := lntest.OpenChannelParams{
   293  		Amt:     dcrutil.Amount(chanAmt),
   294  		PushAmt: dcrutil.Amount(pushAmt),
   295  	}
   296  	chanPoint := openChannelAndAssert(
   297  		t, net, carol, net.Bob, channelParam)
   298  
   299  	// Try to send a payment for one atom more than the maximum possible
   300  	// amount.  It should fail due to not enough outbound capacity.
   301  	err = sendPayment(maxPayAmt + 1)
   302  	if err == nil {
   303  		t.Fatalf("payment higher than max amount should fail, but suceeded.")
   304  	}
   305  	if !strings.Contains(err.Error(), "not enough outbound capacity") {
   306  		t.Fatalf("payment failed for the wrong reason: %v", err)
   307  	}
   308  
   309  	// Try to send a payment for exactly the maximum possible amount. It
   310  	// should succeed.
   311  	err = sendPayment(maxPayAmt - int64(peerHtlcFee))
   312  	if err != nil {
   313  		t.Fatalf("payment should have suceeded, but failed with %v", err)
   314  	}
   315  
   316  	// Wait until Bob and Carol show no pending HTLCs before proceding.
   317  	for _, node := range []*lntest.HarnessNode{net.Bob, carol} {
   318  		err := waitForPendingHtlcs(node, chanPoint, 0)
   319  		if err != nil {
   320  			t.Fatalf("node %s still showing pending HTLCs: %v",
   321  				node.Name(), err)
   322  		}
   323  	}
   324  
   325  	peerReq := &lnrpc.PeerEventSubscription{}
   326  	carolPeerClient, err := carol.SubscribePeerEvents(ctxb, peerReq)
   327  	require.NoError(t.t, err)
   328  
   329  	// Disconnect the nodes from one another. While their channel remains
   330  	// open, carol cannot send payments (since bob is offline from her
   331  	// POV).
   332  	err = net.DisconnectNodes(carol, net.Bob)
   333  	if err != nil {
   334  		t.Fatalf("unable to disconnect carol and bob: %v", err)
   335  	}
   336  
   337  	// Wait to receive the PEER_OFFLINE event before trying to create the
   338  	// invoice.
   339  	peerEvent, err := carolPeerClient.Recv()
   340  	require.NoError(t.t, err)
   341  	require.Equal(t.t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType())
   342  
   343  	// Now trying to send a payment with an offline peer should fail.
   344  	err = sendPayment(1)
   345  	if err == nil {
   346  		t.Fatalf("sending payment without online peer should return an error")
   347  	}
   348  	if !strings.Contains(err.Error(), "no online channels found") {
   349  		t.Fatalf("sending payment without online channels should return " +
   350  			"correct error")
   351  	}
   352  
   353  	// Stop Carol to perform the forced channel close.
   354  	if err := net.StopNode(carol); err != nil {
   355  		t.Fatalf("unable to stop Carol: %v", err)
   356  	}
   357  
   358  	// Force-close and cleanup the channel, since bob & carol are
   359  	// disconnected.
   360  	closeChannelAndAssert(t, net, net.Bob, chanPoint, true)
   361  	cleanupForceClose(t, net, net.Bob, chanPoint)
   362  }
   363  
   364  // testMaxIOChannelBalances tests whether the max inbound/outbound amounts for
   365  // channel balances are consistent.
   366  func testMaxIOChannelBalances(net *lntest.NetworkHarness, t *harnessTest) {
   367  	ctxb := context.Background()
   368  
   369  	// Create a new Carol node to ensure no older channels interfere with
   370  	// the test. Connect Carol to Bob and fund her.
   371  	carol := net.NewNode(t.t, "Carol", nil)
   372  	defer shutdownAndAssert(net, t, carol)
   373  
   374  	net.ConnectNodes(t.t, carol, net.Bob)
   375  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, carol)
   376  
   377  	// Closures to help with tests.
   378  
   379  	openChan := func(chanAmt, pushAmt int64, reverse bool) *lnrpc.ChannelPoint {
   380  		channelParam := lntest.OpenChannelParams{
   381  			Amt:     dcrutil.Amount(chanAmt),
   382  			PushAmt: dcrutil.Amount(pushAmt),
   383  		}
   384  		if reverse {
   385  			return openChannelAndAssert(
   386  				t, net, net.Bob, carol, channelParam,
   387  			)
   388  		}
   389  		return openChannelAndAssert(
   390  			t, net, carol, net.Bob, channelParam,
   391  		)
   392  	}
   393  
   394  	assertBalances := func(balance, maxInbound, maxOutbound, otherBalance,
   395  		otherMaxInbound, otherMaxOutbound int64) {
   396  
   397  		ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
   398  		b, err := carol.ChannelBalance(ctxt, &lnrpc.ChannelBalanceRequest{})
   399  		if err != nil {
   400  			t.t.Errorf("unable to read channel balance: %v", err)
   401  			return
   402  		}
   403  		if b.Balance != balance {
   404  			t.t.Errorf("balance mismatch; expected=%d actual=%d ",
   405  				balance, b.Balance)
   406  		}
   407  		if b.MaxInboundAmount != maxInbound {
   408  			t.t.Errorf("maxInbound mismatch: expected=%d actual=%d",
   409  				maxInbound, b.MaxInboundAmount)
   410  		}
   411  		if b.MaxOutboundAmount != maxOutbound {
   412  			t.t.Errorf("maxOutbound mismatch: expected=%d, actual=%d",
   413  				maxOutbound, b.MaxOutboundAmount)
   414  		}
   415  
   416  		// Now check the balance from bob's pov
   417  		ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   418  		b, err = net.Bob.ChannelBalance(ctxt, &lnrpc.ChannelBalanceRequest{})
   419  		if err != nil {
   420  			t.t.Errorf("unable to read bob channel balance: %v", err)
   421  			return
   422  		}
   423  		if b.Balance != otherBalance {
   424  			t.t.Errorf("otherBalance mismatch; expected=%d actual=%d ",
   425  				otherBalance, b.Balance)
   426  		}
   427  		if b.MaxInboundAmount != otherMaxInbound {
   428  			t.t.Errorf("otherMaxInbound mismatch: expected=%d actual=%d",
   429  				otherMaxInbound, b.MaxInboundAmount)
   430  		}
   431  		if b.MaxOutboundAmount != otherMaxOutbound {
   432  			t.t.Errorf("otherMaxOutbound mismatch: expected=%d, actual=%d",
   433  				otherMaxOutbound, b.MaxOutboundAmount)
   434  		}
   435  	}
   436  
   437  	chanAmt := int64(1000000)
   438  	ctype := lnrpc.CommitmentType_LEGACY
   439  	txFee := int64(calcStaticFee(ctype, 0))
   440  	chanReserve := chanAmt / 100
   441  	dustLimit := int64(6030) // dust limit at the network relay fee rate
   442  
   443  	// Define the test scenarios. All of them use the same total channel
   444  	// amount (chanAmt) and reserve. For each test, a new channel will be
   445  	// opened, the balance will be verified, then the channel will be
   446  	// closed.
   447  	testCases := []struct {
   448  		pushAmt          int64
   449  		balance          int64
   450  		maxInbound       int64
   451  		maxOutbound      int64
   452  		otherBalance     int64
   453  		otherMaxInbound  int64
   454  		otherMaxOutbound int64
   455  		reverse          bool
   456  		descr            string
   457  	}{
   458  		{
   459  			balance:         chanAmt - txFee,
   460  			maxOutbound:     chanAmt - txFee - chanReserve,
   461  			otherMaxInbound: chanAmt - txFee - chanReserve,
   462  			descr:           "all funds remain in Carol",
   463  		},
   464  		{
   465  			pushAmt:         chanReserve / 2,
   466  			balance:         chanAmt - txFee - chanReserve/2,
   467  			maxOutbound:     chanAmt - txFee - chanReserve/2 - chanReserve,
   468  			otherBalance:    chanReserve / 2,
   469  			otherMaxInbound: chanAmt - txFee - chanReserve - chanReserve/2,
   470  			descr:           "carol sent half the remote reserve amount",
   471  		},
   472  		{
   473  			pushAmt:         chanReserve,
   474  			balance:         chanAmt - txFee - chanReserve,
   475  			maxOutbound:     chanAmt - txFee - chanReserve - chanReserve,
   476  			otherBalance:    chanReserve,
   477  			otherMaxInbound: chanAmt - txFee - chanReserve - chanReserve,
   478  			descr:           "carol sent exactly the remote chan reserve",
   479  		},
   480  		{
   481  			pushAmt:          chanReserve + 1,
   482  			balance:          chanAmt - txFee - chanReserve - 1,
   483  			maxOutbound:      chanAmt - txFee - chanReserve - chanReserve - 1,
   484  			maxInbound:       1,
   485  			otherBalance:     chanReserve + 1,
   486  			otherMaxOutbound: 1,
   487  			otherMaxInbound:  chanAmt - txFee - chanReserve - chanReserve - 1,
   488  			descr:            "carol sent one more than the remote chan reserve",
   489  		},
   490  		{
   491  			pushAmt:          chanReserve + 20000,
   492  			balance:          chanAmt - txFee - chanReserve - 20000,
   493  			maxOutbound:      chanAmt - txFee - chanReserve - chanReserve - 20000,
   494  			maxInbound:       20000,
   495  			otherBalance:     chanReserve + 20000,
   496  			otherMaxOutbound: 20000,
   497  			otherMaxInbound:  chanAmt - txFee - chanReserve - chanReserve - 20000,
   498  			descr:            "carol sent 20k atoms over the chan reserve",
   499  		},
   500  		{
   501  			pushAmt:          chanAmt - txFee - chanReserve - 2*dustLimit - 1,
   502  			balance:          chanReserve + 2*dustLimit + 1,
   503  			maxOutbound:      2*dustLimit + 1,
   504  			maxInbound:       chanAmt - txFee - chanReserve*2 - 2*dustLimit - 1,
   505  			otherBalance:     chanAmt - txFee - chanReserve - 2*dustLimit - 1,
   506  			otherMaxOutbound: chanAmt - txFee - chanReserve*2 - 2*dustLimit - 1,
   507  			otherMaxInbound:  2*dustLimit + 1,
   508  			descr:            "carol sent one less than the maximum pushable during funding",
   509  		},
   510  		{
   511  			pushAmt:          chanAmt - txFee - chanReserve - 2*dustLimit,
   512  			balance:          chanReserve + 2*dustLimit,
   513  			maxOutbound:      2 * dustLimit,
   514  			maxInbound:       chanAmt - txFee - chanReserve*2 - 2*dustLimit,
   515  			otherBalance:     chanAmt - txFee - chanReserve - 2*dustLimit,
   516  			otherMaxOutbound: chanAmt - txFee - chanReserve*2 - 2*dustLimit,
   517  			otherMaxInbound:  2 * dustLimit,
   518  			descr:            "carol sent exactly the maximum pushable during funding",
   519  		},
   520  	}
   521  
   522  	for _, tc := range testCases {
   523  		// Sanity check before opening the channel that all balances
   524  		// are zero.
   525  		assertBalances(0, 0, 0, 0, 0, 0)
   526  		if t.t.Failed() {
   527  			t.Fatalf("case '%s' returned error in zero sanity check",
   528  				tc.descr)
   529  		}
   530  
   531  		// Open the channel, perform the balance check, then close the
   532  		// channel again.
   533  		chanPoint := openChan(chanAmt, tc.pushAmt, tc.reverse)
   534  		assertBalances(
   535  			tc.balance, tc.maxInbound, tc.maxOutbound,
   536  			tc.otherBalance, tc.otherMaxInbound,
   537  			tc.otherMaxOutbound,
   538  		)
   539  		if t.t.Failed() {
   540  			t.Fatalf("Case '%s' failed", tc.descr)
   541  		}
   542  		closeChannelAndAssert(t, net, carol, chanPoint, false)
   543  	}
   544  }