From 2ab3a4b552633c582ba109aa10aeb221ff1cec59 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 16 Mar 2021 14:22:01 +0100 Subject: [PATCH] itest: add test for self channel balance --- .gitignore | 4 +- itest/self_chan_balance_test.go | 190 ++++++++++++++++++++++++++++++++ itest/test_harness.go | 27 ++++- itest/test_list_on.go | 4 + 4 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 itest/self_chan_balance_test.go diff --git a/.gitignore b/.gitignore index d4d5a4270..d9b96f209 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,6 @@ profile.tmp /auctioncli /itest/lnd-itest /itest/btcd-itest -/itest/regtest/*.log +/itest/regtest/* +/itest/.backendlogs/* +/itest/.minerlogs/* diff --git a/itest/self_chan_balance_test.go b/itest/self_chan_balance_test.go new file mode 100644 index 000000000..cb60521fc --- /dev/null +++ b/itest/self_chan_balance_test.go @@ -0,0 +1,190 @@ +package itest + +import ( + "context" + "fmt" + + "github.com/btcsuite/btcutil" + orderT "github.com/lightninglabs/pool/order" + "github.com/lightninglabs/pool/poolrpc" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/stretchr/testify/require" +) + +// testSelfChanBalance tests that opening a channel with a self channel balance +// through a bid order is possible. +func testSelfChanBalance(t *harnessTest) { + ctx := context.Background() + + // We need a third lnd node, Charlie that is used for the second trader. + charlie, err := t.lndHarness.NewNode("charlie", nil) + require.NoError(t.t, err) + secondTrader := setupTraderHarness( + t.t, t.lndHarness.BackendCfg, charlie, t.auctioneer, + ) + defer shutdownAndAssert(t, charlie, secondTrader) + err = t.lndHarness.SendCoins(ctx, 5_000_000, charlie) + require.NoError(t.t, err) + + // Create an account over 2M sats that is valid for the next 1000 blocks + // for both traders. To test the message multi-plexing between token IDs + // and accounts, we add a secondary account to the second trader. + makerAccount := openAccountAndAssert( + t, t.trader, &poolrpc.InitAccountRequest{ + AccountValue: defaultAccountValue, + AccountExpiry: &poolrpc.InitAccountRequest_RelativeHeight{ + RelativeHeight: 1_000, + }, + }, + ) + takerAccount := openAccountAndAssert( + t, secondTrader, &poolrpc.InitAccountRequest{ + AccountValue: defaultAccountValue, + AccountExpiry: &poolrpc.InitAccountRequest_RelativeHeight{ + RelativeHeight: 1_000, + }, + }, + ) + + // Now that the accounts are confirmed, submit an ask order from our + // default trader, selling 2 units (200k sats) of liquidity. This ask + // has the wrong order version and won't be matched with the self chan + // balance bid. + const orderFixedRate = 100 + ask1Amt := btcutil.Amount(200_000) + _, err = submitAskOrder( + t.trader, makerAccount.TraderKey, orderFixedRate, ask1Amt, + ) + require.NoError(t.t, err) + + // From the secondary account of the second trader, we also create an + // order to buy some units. The order has a self channel balance set. + const selfChanBalance = 20_000 + bidAmt := btcutil.Amount(100_000) + _, err = submitBidOrder( + secondTrader, takerAccount.TraderKey, orderFixedRate, bidAmt, + func(bid *poolrpc.SubmitOrderRequest_Bid) { + bid.Bid.SelfChanBalance = selfChanBalance + bid.Bid.Version = uint32(orderT.VersionSelfChanBalance) + }, + ) + require.NoError(t.t, err) + + // Since the ask is of the wrong version, a batch should not be + // cleared, so a batch transaction should not be broadcast. Kick the + // auctioneer and wait for it to return back to the order submit state. + // No tx should be in the mempool as no market should be possible. + _, _ = executeBatch(t, 0) + + // Let's add a second ask with the correct version now so it can be + // matched against the bid. + ask2Amt := btcutil.Amount(300_000) + _, err = submitAskOrder( + t.trader, makerAccount.TraderKey, orderFixedRate, ask2Amt, + func(ask *poolrpc.SubmitOrderRequest_Ask) { + ask.Ask.Version = uint32(orderT.VersionSelfChanBalance) + }, + ) + require.NoError(t.t, err) + + // Let's kick the auctioneer again to try and create a batch. + _, batchTXIDs := executeBatch(t, 1) + firstBatchTXID := batchTXIDs[0] + + // At this point, the lnd nodes backed by each trader should have a + // single pending channel, which matches the amount of the order + // executed above. + // + // In our case, Bob is the maker so he should be marked as the + // initiator of the channel. + assertPendingChannel( + t, t.trader.cfg.LndNode, bidAmt+selfChanBalance, true, + charlie.PubKey, + func(c *lnrpc.PendingChannelsResponse_PendingChannel) error { + if c.RemoteBalance != selfChanBalance { + return fmt.Errorf("unexpected remote balance "+ + "%d, wanted %d", c.RemoteBalance, + selfChanBalance) + } + + return nil + }, + ) + assertPendingChannel( + t, charlie, bidAmt+selfChanBalance, false, + t.trader.cfg.LndNode.PubKey, + func(c *lnrpc.PendingChannelsResponse_PendingChannel) error { + if c.LocalBalance != selfChanBalance { + return fmt.Errorf("unexpected local balance "+ + "%d, wanted %d", c.LocalBalance, + selfChanBalance) + } + + return nil + }, + ) + + // We'll now mine a block to confirm the channel. We should find the + // channel in the listchannels output for both nodes, and the + // thaw_height should be set accordingly. + blocks := mineBlocks(t, t.lndHarness, 1, 1) + + // The block above should contain the batch transaction found in the + // mempool above. + assertTxInBlock(t, blocks[0], firstBatchTXID) + + // We'll now mine another 3 blocks to ensure the channel itself is + // fully confirmed and the accounts in the open state again. + _ = mineBlocks(t, t.lndHarness, 3, 0) + + // Now that the channels are confirmed, they should both be active, and + // we should be able to make a payment between this new channel + // established. + assertActiveChannel( + t, t.trader.cfg.LndNode, int64(bidAmt+selfChanBalance), + *firstBatchTXID, charlie.PubKey, defaultOrderDuration, + func(c *lnrpc.Channel) error { + if c.RemoteBalance != selfChanBalance { + return fmt.Errorf("unexpected remote balance "+ + "%d, wanted %d", c.RemoteBalance, + selfChanBalance) + } + + return nil + }, + ) + assertActiveChannel( + t, charlie, int64(bidAmt+selfChanBalance), *firstBatchTXID, + t.trader.cfg.LndNode.PubKey, defaultOrderDuration, + func(c *lnrpc.Channel) error { + if c.LocalBalance != selfChanBalance { + return fmt.Errorf("unexpected local balance "+ + "%d, wanted %d", c.LocalBalance, + selfChanBalance) + } + + return nil + }, + ) + + // Finally make sure the accounts were charged correctly. The base order + // fee is 1 satoshi and the rate is 1/1000. + submissionFee := 1 + (bidAmt / 1000) + premium := orderT.FixedRatePremium(orderFixedRate).LumpSumPremium( + bidAmt, defaultOrderDuration, + ) + chainFees := orderT.EstimateTraderFee(1, chainfee.SatPerKWeight(12_500)) + makerBalance := btcutil.Amount(defaultAccountValue) - submissionFee - + chainFees - bidAmt + premium + takerBalance := btcutil.Amount(defaultAccountValue) - submissionFee - + chainFees - selfChanBalance - premium + assertTraderAccount( + t, t.trader, makerAccount.TraderKey, makerBalance, + makerAccount.ExpirationHeight, poolrpc.AccountState_OPEN, + ) + assertTraderAccount( + t, secondTrader, takerAccount.TraderKey, takerBalance, + takerAccount.ExpirationHeight, poolrpc.AccountState_OPEN, + ) +} diff --git a/itest/test_harness.go b/itest/test_harness.go index 2d6b05afa..0222f0067 100644 --- a/itest/test_harness.go +++ b/itest/test_harness.go @@ -795,8 +795,12 @@ func traderOutputScript(t *harnessTest, traderNode *lntest.HarnessNode) []byte { return addrScript } +type pendingChanCheck func( + channel *lnrpc.PendingChannelsResponse_PendingChannel) error + func assertPendingChannel(t *harnessTest, node *lntest.HarnessNode, - chanAmt btcutil.Amount, initiator bool, chanPeer [33]byte) { + chanAmt btcutil.Amount, initiator bool, chanPeer [33]byte, + moreChecks ...pendingChanCheck) { req := &lnrpc.PendingChannelsRequest{} err := wait.NoError(func() error { @@ -845,6 +849,13 @@ func assertPendingChannel(t *harnessTest, node *lntest.HarnessNode, "got %v", initiator, channel.Initiator) } + for _, chk := range moreChecks { + if err := chk(channel); err != nil { + return fmt.Errorf("custom check failed: %v", + err) + } + } + return nil }, defaultWaitTimeout) if err != nil { @@ -958,9 +969,12 @@ func completePaymentRequests(ctx context.Context, client lnrpc.LightningClient, return nil } +type activeChanCheck func(channel *lnrpc.Channel) error + func assertActiveChannel(t *harnessTest, node *lntest.HarnessNode, chanAmt int64, fundingTXID chainhash.Hash, chanPeer [33]byte, - chanDuration uint32) *lnrpc.ChannelPoint { // nolint:unparam + chanDuration uint32, + moreChecks ...activeChanCheck) *lnrpc.ChannelPoint { // nolint:unparam var chanPointStr string req := &lnrpc.ListChannelsRequest{} @@ -1009,6 +1023,13 @@ func assertActiveChannel(t *harnessTest, node *lntest.HarnessNode, "got %v", chanDuration, pendingChan.ThawHeight) } + for _, chk := range moreChecks { + if err := chk(pendingChan); err != nil { + return fmt.Errorf("custom check failed: %v", + err) + } + } + chanPointStr = pendingChan.ChannelPoint return nil }, defaultWaitTimeout) @@ -1158,7 +1179,7 @@ func assertNoOrders(t *harnessTest, trader *traderHarness) { func assertAskOrderState(t *harnessTest, trader *traderHarness, unfilledUnits uint32, orderNonce orderT.Nonce) { - // TODO(roasbeef): add LookupORder method for client RPC + // TODO(roasbeef): add LookupOrder method for client RPC err := wait.NoError(func() error { req := &poolrpc.ListOrdersRequest{} resp, err := trader.ListOrders(context.Background(), req) diff --git a/itest/test_list_on.go b/itest/test_list_on.go index 09f0bcd54..c8a9f3d7d 100644 --- a/itest/test_list_on.go +++ b/itest/test_list_on.go @@ -88,4 +88,8 @@ var testCases = []*testCase{ name: "batch extra inputs outputs", test: testBatchIO, }, + { + name: "self channel balance", + test: testSelfChanBalance, + }, }