From 509adce2adc00f11ef11be20839fd8a599a87007 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 7 Feb 2018 19:45:19 -0500 Subject: [PATCH] htlcswitch test: add TestChannelLinkBandwidthChanReserve --- htlcswitch/link_test.go | 140 +++++++++++++++++++++++++++++++++++++-- htlcswitch/test_utils.go | 16 +++-- 2 files changed, 144 insertions(+), 12 deletions(-) diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 17ee42d40..ce1491c19 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1405,8 +1405,8 @@ func (m *mockPeer) Disconnect(reason error) { var _ Peer = (*mockPeer)(nil) -func newSingleLinkTestHarness(chanAmt btcutil.Amount) (ChannelLink, - *lnwallet.LightningChannel, chan time.Time, func(), error) { +func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) ( + ChannelLink, *lnwallet.LightningChannel, chan time.Time, func(), error) { globalEpoch := &chainntnfs.BlockEpochEvent{ Epochs: make(chan *chainntnfs.BlockEpoch), Cancel: func() { @@ -1415,7 +1415,8 @@ func newSingleLinkTestHarness(chanAmt btcutil.Amount) (ChannelLink, chanID := lnwire.NewShortChanIDFromInt(4) aliceChannel, bobChannel, fCleanUp, _, err := createTestChannel( - alicePrivKey, bobPrivKey, chanAmt, chanAmt, chanID, + alicePrivKey, bobPrivKey, chanAmt, chanAmt, + chanReserve, chanReserve, chanID, ) if err != nil { return nil, nil, nil, nil, err @@ -1659,7 +1660,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // We'll start the test by creating a single instance of const chanAmt = btcutil.SatoshiPerBitcoin * 5 - link, bobChannel, tmr, cleanUp, err := newSingleLinkTestHarness(chanAmt) + link, bobChannel, tmr, cleanUp, err := newSingleLinkTestHarness(chanAmt, 0) if err != nil { t.Fatalf("unable to create link: %v", err) } @@ -1992,7 +1993,8 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) { var mockBlob [lnwire.OnionPacketSize]byte const chanAmt = btcutil.SatoshiPerBitcoin * 5 - aliceLink, bobChannel, batchTick, cleanUp, err := newSingleLinkTestHarness(chanAmt) + aliceLink, bobChannel, batchTick, cleanUp, err := + newSingleLinkTestHarness(chanAmt, 0) if err != nil { t.Fatalf("unable to create link: %v", err) } @@ -2191,6 +2193,134 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) { } } +// TestChannelLinkBandwidthChanReserve checks that the bandwidth available +// on the channel link reflects the channel reserve that must be kept +// at all times. +func TestChannelLinkBandwidthChanReserve(t *testing.T) { + t.Parallel() + + // First start a link that has a balance greater than it's + // channel reserve. + const chanAmt = btcutil.SatoshiPerBitcoin * 5 + const chanReserve = btcutil.SatoshiPerBitcoin * 1 + aliceLink, bobChannel, batchTimer, cleanUp, err := + newSingleLinkTestHarness(chanAmt, chanReserve) + if err != nil { + t.Fatalf("unable to create link: %v", err) + } + defer cleanUp() + + var ( + mockBlob [lnwire.OnionPacketSize]byte + coreLink = aliceLink.(*channelLink) + coreChan = coreLink.channel + defaultCommitFee = coreChan.StateSnapshot().CommitFee + aliceStartingBandwidth = aliceLink.Bandwidth() + aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs + ) + + estimator := &lnwallet.StaticFeeEstimator{ + FeeRate: 24, + } + feePerWeight, err := estimator.EstimateFeePerWeight(1) + if err != nil { + t.Fatalf("unable to query fee estimator: %v", err) + } + feePerKw := feePerWeight * 1000 + htlcFee := lnwire.NewMSatFromSatoshis( + btcutil.Amount((int64(feePerKw) * lnwallet.HtlcWeight) / 1000), + ) + + // The starting bandwidth of the channel should be exactly the amount + // that we created the channel between her and Bob, minus the channel + // reserve. + expectedBandwidth := lnwire.NewMSatFromSatoshis( + chanAmt - defaultCommitFee - chanReserve) + assertLinkBandwidth(t, aliceLink, expectedBandwidth) + + // Next, we'll create an HTLC worth 3 BTC, and send it into the link as + // a switch initiated payment. The resulting bandwidth should + // now be decremented to reflect the new HTLC. + htlcAmt := lnwire.NewMSatFromSatoshis(3 * btcutil.SatoshiPerBitcoin) + invoice, htlc, err := generatePayment(htlcAmt, htlcAmt, 5, mockBlob) + if err != nil { + t.Fatalf("unable to create payment: %v", err) + } + addPkt := htlcPacket{ + htlc: htlc, + } + aliceLink.HandleSwitchPacket(&addPkt) + time.Sleep(time.Millisecond * 100) + assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee) + + // Alice should send the HTLC to Bob. + var msg lnwire.Message + select { + case msg = <-aliceMsgs: + case <-time.After(2 * time.Second): + t.Fatalf("did not receive message") + } + + addHtlc, ok := msg.(*lnwire.UpdateAddHTLC) + if !ok { + t.Fatalf("expected UpdateAddHTLC, got %T", msg) + } + + bobIndex, err := bobChannel.ReceiveHTLC(addHtlc) + if err != nil { + t.Fatalf("bob failed receiving htlc: %v", err) + } + + // Lock in the HTLC. + if err := updateState(batchTimer, coreLink, bobChannel, true); err != nil { + t.Fatalf("unable to update state: %v", err) + } + + assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee) + + // If we now send in a valid HTLC settle for the prior HTLC we added, + // then the bandwidth should remain unchanged as the remote party will + // gain additional channel balance. + err = bobChannel.SettleHTLC(invoice.Terms.PaymentPreimage, bobIndex) + if err != nil { + t.Fatalf("unable to settle htlc: %v", err) + } + htlcSettle := &lnwire.UpdateFulfillHTLC{ + ID: bobIndex, + PaymentPreimage: invoice.Terms.PaymentPreimage, + } + aliceLink.HandleChannelUpdate(htlcSettle) + time.Sleep(time.Millisecond * 500) + + // Since the settle is not locked in yet, Alice's bandwidth should still + // reflect that she has to pay the fee. + assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee) + + // Lock in the settle. + if err := updateState(batchTimer, coreLink, bobChannel, false); err != nil { + t.Fatalf("unable to update state: %v", err) + } + + time.Sleep(time.Millisecond * 100) + assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt) + + // Now we create a channel that has a channel reserve that is + // greater than it's balance. In these case only payments can + // be received on this channel, not sent. The available bandwidth + // should therefore be 0. + const bobChanAmt = btcutil.SatoshiPerBitcoin * 1 + const bobChanReserve = btcutil.SatoshiPerBitcoin * 1.5 + bobLink, _, _, bobCleanUp, err := newSingleLinkTestHarness(bobChanAmt, + bobChanReserve) + if err != nil { + t.Fatalf("unable to create link: %v", err) + } + defer bobCleanUp() + + // Make sure bandwidth is reported as 0. + assertLinkBandwidth(t, bobLink, 0) +} + // TestChannelRetransmission tests the ability of the channel links to // synchronize theirs states after abrupt disconnect. func TestChannelRetransmission(t *testing.T) { diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 23a9776c3..bc3b01919 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -88,7 +88,7 @@ func generateRandomBytes(n int) ([]byte, error) { // // TODO(roasbeef): need to factor out, similar func re-used in many parts of codebase func createTestChannel(alicePrivKey, bobPrivKey []byte, - aliceAmount, bobAmount btcutil.Amount, + aliceAmount, bobAmount, aliceReserve, bobReserve btcutil.Amount, chanID lnwire.ShortChannelID) (*lnwallet.LightningChannel, *lnwallet.LightningChannel, func(), func() (*lnwallet.LightningChannel, *lnwallet.LightningChannel, error), error) { @@ -104,7 +104,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, DustLimit: btcutil.Amount(200), MaxPendingAmount: lnwire.NewMSatFromSatoshis( channelCapacity), - ChanReserve: 0, + ChanReserve: aliceReserve, MinHTLC: 0, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, } @@ -113,7 +113,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, DustLimit: btcutil.Amount(800), MaxPendingAmount: lnwire.NewMSatFromSatoshis( channelCapacity), - ChanReserve: 0, + ChanReserve: bobReserve, MinHTLC: 0, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, } @@ -668,15 +668,17 @@ func createClusterChannels(aliceToBob, bobToCarol btcutil.Amount) ( secondChanID := lnwire.NewShortChanIDFromInt(5) // Create lightning channels between Alice<->Bob and Bob<->Carol - aliceChannel, firstBobChannel, cleanAliceBob, restoreAliceBob, err := createTestChannel( - alicePrivKey, bobPrivKey, aliceToBob, aliceToBob, firstChanID) + aliceChannel, firstBobChannel, cleanAliceBob, restoreAliceBob, err := + createTestChannel(alicePrivKey, bobPrivKey, aliceToBob, + aliceToBob, 0, 0, firstChanID) if err != nil { return nil, nil, nil, errors.Errorf("unable to create "+ "alice<->bob channel: %v", err) } - secondBobChannel, carolChannel, cleanBobCarol, restoreBobCarol, err := createTestChannel( - bobPrivKey, carolPrivKey, bobToCarol, bobToCarol, secondChanID) + secondBobChannel, carolChannel, cleanBobCarol, restoreBobCarol, err := + createTestChannel(bobPrivKey, carolPrivKey, bobToCarol, + bobToCarol, 0, 0, secondChanID) if err != nil { cleanAliceBob() return nil, nil, nil, errors.Errorf("unable to create "+