From f535210068ba45fed63fdb782d90fd9b135a9216 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 9 Sep 2021 14:33:01 +0200 Subject: [PATCH 1/2] multi: bump version of Pool client --- admin_rpcserver.go | 11 ++++++++++- go.mod | 6 +++--- go.sum | 11 ++++++----- internal/test/walletkit_mock.go | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/admin_rpcserver.go b/admin_rpcserver.go index 53cbddd7a..d8dba053c 100644 --- a/admin_rpcserver.go +++ b/admin_rpcserver.go @@ -9,6 +9,7 @@ import ( "net" "sync" "sync/atomic" + "time" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" @@ -33,6 +34,12 @@ import ( "google.golang.org/grpc" ) +var ( + // defaultLeaseDuration is the default lease duration we use for locking + // additional inputs to our batch. + defaultLeaseDuration = time.Minute * 10 +) + // adminRPCServer is a server that implements the admin server RPC interface and // serves administrative and super user content. type adminRPCServer struct { @@ -1077,7 +1084,9 @@ func (s *adminRPCServer) MoveFunds(ctx context.Context, } for i, in := range io.Inputs { - _, err := s.wallet.LeaseOutput(ctx, s.lockID, in.PrevOutPoint) + _, err := s.wallet.LeaseOutput( + ctx, s.lockID, in.PrevOutPoint, defaultLeaseDuration, + ) if err != nil { releaseOutputs(i) return nil, fmt.Errorf("unable to lease output %v: %v", diff --git a/go.mod b/go.mod index 41acb9820..a6b09acfc 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,11 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/jessevdk/go-flags v1.4.0 github.com/lightninglabs/aperture v0.1.9-beta.0.20210730071214-beed396b0ef6 - github.com/lightninglabs/lndclient v0.12.0-9 - github.com/lightninglabs/pool v0.5.1-alpha + github.com/lightninglabs/lndclient v0.13.0-7 + github.com/lightninglabs/pool v0.5.1-alpha.0.20210930001328-53df9a308491 github.com/lightninglabs/pool/auctioneerrpc v1.0.3 github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display - github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210728112744-ebabda671786 + github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210802115842-44971f0c46c9 github.com/lightningnetwork/lnd/cert v1.0.3 github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/prometheus/client_golang v1.11.0 diff --git a/go.sum b/go.sum index d8963d72d..a8161cee2 100644 --- a/go.sum +++ b/go.sum @@ -400,16 +400,16 @@ github.com/lightninglabs/aperture v0.1.9-beta.0.20210730071214-beed396b0ef6/go.m github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/lndclient v0.11.0-4/go.mod h1:8/cTKNwgL87NX123gmlv3Xh6p1a7pvzu+40Un3PhHiI= -github.com/lightninglabs/lndclient v0.12.0-8/go.mod h1:L0R2VOaLxMylGbxgnfiZGc0hMDIIgj91cfgwGuFz9kU= -github.com/lightninglabs/lndclient v0.12.0-9 h1:w5Ozl74o7hJElxSk4hRu2enCNzdJfshDbJsJzhBaI3I= github.com/lightninglabs/lndclient v0.12.0-9/go.mod h1:L0R2VOaLxMylGbxgnfiZGc0hMDIIgj91cfgwGuFz9kU= +github.com/lightninglabs/lndclient v0.13.0-7 h1:diIMNzbe8pMF1jf6tysOLLkeo1Q6KzyQNsNvLs+g3xg= +github.com/lightninglabs/lndclient v0.13.0-7/go.mod h1:Ia/UARBUq9Y8CCegm/Nrdi/z+NZsH85JzxetAdnC37Q= github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= github.com/lightninglabs/neutrino v0.11.1-0.20201210023533-e1978372d15e/go.mod h1:KDWfQDKp+CFBxO1t2NRmWuagTY2sYIjpHB1k5vrojTI= github.com/lightninglabs/neutrino v0.12.1 h1:9umzk5kKNc/l3bAyak8ClSRP1qSulnjc6kppLYDnuqk= github.com/lightninglabs/neutrino v0.12.1/go.mod h1:GlKninWpRBbL7b8G0oQ36/8downfnFwKsr0hbRA6E/E= -github.com/lightninglabs/pool v0.5.1-alpha h1:9JpH5nclgqMgK6m03lkEakLhh/Q1kCgeFp+TtjmUuHc= -github.com/lightninglabs/pool v0.5.1-alpha/go.mod h1:eR+h308Vxf6UK5UbVJsnSYo7ygO6ouONCKg8rS4V2Ss= +github.com/lightninglabs/pool v0.5.1-alpha.0.20210930001328-53df9a308491 h1:Isx7ObvpmpqQPcEsx9P1Pro5csi1WjUaEPq8RkLluMs= +github.com/lightninglabs/pool v0.5.1-alpha.0.20210930001328-53df9a308491/go.mod h1:l8eNmAeT50mu6mzkvOMDZAztmoGpgEew06uqvxyoslg= github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ9pFtx5wqsuD3B/DezrewJeVwDKKYY8E= github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE= @@ -419,8 +419,9 @@ github.com/lightningnetwork/lightning-onion v1.0.2-0.20210520211913-522b799e65b1 github.com/lightningnetwork/lnd v0.11.0-beta/go.mod h1:CzArvT7NFDLhVyW06+NJWSuWFmE6Ea+AjjA3txUBqTM= github.com/lightningnetwork/lnd v0.11.1-beta/go.mod h1:PGIgxy8aH70Li33YVYkHSaCM8m8LjEevk5h1Dpldrr4= github.com/lightningnetwork/lnd v0.12.0-beta/go.mod h1:2GyP1IG1kXV5Af/LOCxnXfux1OP3fAGr8zptS5PB2YI= -github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210728112744-ebabda671786 h1:DOZ16XjuSJgmgV0jXYcagxg19fRgad3DbzpNNkWuOsk= github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210728112744-ebabda671786/go.mod h1:3cmukt9wR4PX1va9Q78gmqSPYd6yhV1wcFemM5F+kT8= +github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210802115842-44971f0c46c9 h1:kFyyJRNFAUnQl5G7b3stUF2KIfdUJu/KCKXvPlBJqNA= +github.com/lightningnetwork/lnd v0.13.0-beta.rc5.0.20210802115842-44971f0c46c9/go.mod h1:3cmukt9wR4PX1va9Q78gmqSPYd6yhV1wcFemM5F+kT8= github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo= github.com/lightningnetwork/lnd/cert v1.0.3 h1:/K2gjzLgVI8we2IIPKc0ztWTEa85uds5sWXi1K6mOT0= github.com/lightningnetwork/lnd/cert v1.0.3/go.mod h1:3MWXVLLPI0Mg0XETm9fT4N9Vyy/8qQLmaM5589bEggM= diff --git a/internal/test/walletkit_mock.go b/internal/test/walletkit_mock.go index ba3f80052..ae0013b78 100644 --- a/internal/test/walletkit_mock.go +++ b/internal/test/walletkit_mock.go @@ -45,7 +45,7 @@ func (m *MockWalletKit) ListUnspent(context.Context, int32, } func (m *MockWalletKit) LeaseOutput(context.Context, wtxmgr.LockID, - wire.OutPoint) (time.Time, error) { + wire.OutPoint, time.Duration) (time.Time, error) { return time.Now(), nil } From e7d409410de77b153fe53d7f5243bb3f0f5df7bb Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 9 Sep 2021 14:32:46 +0200 Subject: [PATCH 2/2] itest: add itest for sidecar cancellation --- itest/self_chan_balance_test.go | 3 +- itest/sidecar_channels_test.go | 186 ++++++++++++++++-- itest/test_harness.go | 48 ++++- ...test_list_off.go => test_list_off_test.go} | 0 .../{test_list_on.go => test_list_on_test.go} | 4 + 5 files changed, 217 insertions(+), 24 deletions(-) rename itest/{test_list_off.go => test_list_off_test.go} (100%) rename itest/{test_list_on.go => test_list_on_test.go} (96%) diff --git a/itest/self_chan_balance_test.go b/itest/self_chan_balance_test.go index 23f51967c..edb73581b 100644 --- a/itest/self_chan_balance_test.go +++ b/itest/self_chan_balance_test.go @@ -26,8 +26,7 @@ func testSelfChanBalance(t *harnessTest) { t.lndHarness.SendCoins(ctx, t.t, 5_000_000, charlie) // 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. + // for both traders. makerAccount := openAccountAndAssert( t, t.trader, &poolrpc.InitAccountRequest{ AccountValue: defaultAccountValue, diff --git a/itest/sidecar_channels_test.go b/itest/sidecar_channels_test.go index f4a364866..5b17ce9ec 100644 --- a/itest/sidecar_channels_test.go +++ b/itest/sidecar_channels_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "testing" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" @@ -37,8 +38,7 @@ func sidecarChannelsHappyPath(ctx context.Context, t *harnessTest, auto bool) { defer shutdownAndAssert(t, dave, recipientTrader) // 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. + // for both traders. makerAccount := openAccountAndAssert( t, t.trader, &poolrpc.InitAccountRequest{ AccountValue: defaultAccountValue, @@ -73,7 +73,7 @@ func sidecarChannelsHappyPath(ctx context.Context, t *harnessTest, auto bool) { // Create the sidecar channel ticket now, including the offer, order and // channel expectation. - firstSidecarBid := makeSidecar( + firstSidecarBid, _ := makeSidecar( t, providerTrader, recipientTrader, providerAccount.TraderKey, orderFixedRate, askAmt, selfChanBalance, auto, ) @@ -178,7 +178,7 @@ func sidecarChannelsHappyPath(ctx context.Context, t *harnessTest, auto bool) { // Create the sidecar channel ticket now, including the offer, order and // channel expectation. - secondSidecarBid := makeSidecar( + secondSidecarBid, _ := makeSidecar( t, providerTrader, recipientTrader, providerAccount.TraderKey, orderFixedRate, askAmt, 0, auto, ) @@ -262,8 +262,7 @@ func testSidecarChannelsRejectNewNodesOnly(t *harnessTest) { defer shutdownAndAssert(t, dave, recipientTrader) // 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. + // for both traders. makerAccount := openAccountAndAssert( t, t.trader, &poolrpc.InitAccountRequest{ AccountValue: defaultAccountValue, @@ -307,7 +306,7 @@ func testSidecarChannelsRejectNewNodesOnly(t *harnessTest) { // Create the sidecar channel ticket now, including the offer, order and // channel expectation. - firstSidecarBid := makeSidecar( + firstSidecarBid, _ := makeSidecar( t, providerTrader, recipientTrader, providerAccount.TraderKey, orderFixedRate, askAmt, 0, false, ) @@ -368,8 +367,7 @@ func testSidecarChannelsRejectMinChanSize(t *harnessTest) { defer shutdownAndAssert(t, dave, recipientTrader) // 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. + // for both traders. makerAccount := openAccountAndAssert( t, t.trader, &poolrpc.InitAccountRequest{ AccountValue: defaultAccountValue, @@ -424,13 +422,141 @@ func testSidecarChannelsRejectMinChanSize(t *harnessTest) { _, _ = executeBatch(t, 0) } +// testSidecarTicketCancellation makes sure sidecar tickets can be canceled in +// the various states they can be in during their lifecycle. +func testSidecarTicketCancellation(t *harnessTest) { + ctx := context.Background() + + // We need a third and fourth lnd node for the additional participants. + // Charlie is the sidecar channel provider (has an account, submits the + // bid order) and Dave is the sidecar channel recipient (has no account + // and only receives the channel). + charlie := t.lndHarness.NewNode(t.t, "charlie", nil) + providerTrader := setupTraderHarness( + t.t, t.lndHarness.BackendCfg, charlie, t.auctioneer, + ) + defer shutdownAndAssert(t, charlie, providerTrader) + t.lndHarness.SendCoins(ctx, t.t, 5_000_000, charlie) + + dave := t.lndHarness.NewNode(t.t, "dave", nil) + recipientTrader := setupTraderHarness( + t.t, t.lndHarness.BackendCfg, dave, t.auctioneer, + ) + defer shutdownAndAssert(t, dave, recipientTrader) + + // Create an account over 2M sats that is valid for the next 1000 blocks + // for both traders. + makerAccount := openAccountAndAssert( + t, t.trader, &poolrpc.InitAccountRequest{ + AccountValue: defaultAccountValue, + AccountExpiry: &poolrpc.InitAccountRequest_RelativeHeight{ + RelativeHeight: 1_000, + }, + }, + ) + providerAccount := openAccountAndAssert( + t, providerTrader, &poolrpc.InitAccountRequest{ + AccountValue: defaultAccountValue, + AccountExpiry: &poolrpc.InitAccountRequest_RelativeHeight{ + RelativeHeight: 1_000, + }, + }, + ) + + // Now that the accounts are confirmed, submit an ask order from our + // maker, selling 200 units (200k sats) of liquidity. + const ( + orderFixedRate = 100 + askAmt = 200_000 + selfChanBalance = 100_000 + ) + _, err := submitAskOrder( + t.trader, makerAccount.TraderKey, orderFixedRate, askAmt, + func(ask *poolrpc.SubmitOrderRequest_Ask) { + ask.Ask.Version = uint32(orderT.VersionSidecarChannel) + }, + ) + require.NoError(t.t, err) + + // Create an offer from the provider account. + offerResp, err := providerTrader.OfferSidecar( + ctx, &poolrpc.OfferSidecarRequest{ + AutoNegotiate: false, + Bid: &poolrpc.Bid{ + Details: &poolrpc.Order{ + TraderKey: providerAccount.TraderKey, + Amt: uint64(askAmt), + }, + SelfChanBalance: selfChanBalance, + LeaseDurationBlocks: defaultOrderDuration, + }, + }, + ) + require.NoError(t.t, err) + + offeredTicket, err := sidecar.DecodeString(offerResp.Ticket) + require.NoError(t.t, err) + + // The offer should now be listed and in the correct state. + assertSidecarState( + t.t, providerTrader, 1, offeredTicket.ID[:], + offeredTicket.State, + ) + + // Let's cancel the ticket. + _, err = providerTrader.CancelSidecar( + ctx, &poolrpc.CancelSidecarRequest{ + SidecarId: offeredTicket.ID[:], + }, + ) + require.NoError(t.t, err) + + // Make sure the state is properly reflected. + assertSidecarState( + t.t, providerTrader, 1, offeredTicket.ID[:], + sidecar.StateCanceled, + ) + + // Create a full sidecar channel ticket now, including the offer, order + // and channel expectation. Then we cancel it on the offer side and + // check that the order is canceled as well. + sidecarBid, ticketID := makeSidecar( + t, providerTrader, recipientTrader, providerAccount.TraderKey, + orderFixedRate, askAmt, 0, true, + ) + assertSidecarState( + t.t, providerTrader, 2, ticketID, sidecar.StateExpectingChannel, + ) + assertSidecarState( + t.t, recipientTrader, 1, ticketID, + sidecar.StateExpectingChannel, + ) + + // Let's cancel the ticket. + _, err = providerTrader.CancelSidecar( + ctx, &poolrpc.CancelSidecarRequest{ + SidecarId: ticketID, + }, + ) + require.NoError(t.t, err) + assertSidecarState( + t.t, providerTrader, 2, ticketID, sidecar.StateCanceled, + ) + + // The order should be canceled as well. + assertBidOrderState( + t, providerTrader, sidecarBid, + auctioneerrpc.OrderState_ORDER_CANCELED, + ) +} + // makeSidecar creates a sidecar ticket with an offer on the provider node, // registers the offer with the recipient node, creates a bid order for the // provider and finally adds the channel expectation to the recipient's node. func makeSidecar(t *harnessTest, providerTrader, recipientTrader *traderHarness, providerAccountKey []byte, orderFixedRate uint32, // nolint:unparam askAmt btcutil.Amount, selfChanBalance uint64, // nolint:unparam - auto bool) orderT.Nonce { // nolint:unparam + auto bool) (orderT.Nonce, []byte) { ctx := context.Background() @@ -479,6 +605,8 @@ func makeSidecar(t *harnessTest, providerTrader, recipientTrader *traderHarness, }, ) require.NoError(t.t, err) + registeredTicket, err := sidecar.DecodeString(registerResp.Ticket) + require.NoError(t.t, err) // If we're using automated negotiation, then the last two steps will // be done automatically, so we can exit here. @@ -488,18 +616,13 @@ func makeSidecar(t *harnessTest, providerTrader, recipientTrader *traderHarness, if auto { // If this is an auto negotiated sidecar ticket, then we'll // wait for the sidecar responder to connect as a new trader. - registeredTicket, err := sidecar.DecodeString( - registerResp.Ticket, - ) - require.NoError(t.t, err) - assertSidecarTraderSubscribed( t, registeredTicket.Recipient.MultiSigPubKey, ) var bidNonce orderT.Nonce copy(bidNonce[:], registeredTicket.Order.BidNonce[:]) - return bidNonce + return bidNonce, registeredTicket.ID[:] } // Step 3/4 is for the provider to submit a bid order referencing the @@ -534,7 +657,7 @@ func makeSidecar(t *harnessTest, providerTrader, recipientTrader *traderHarness, ) require.NoError(t.t, err) - return bidNonce + return bidNonce, registeredTicket.ID[:] } // assertSidecarLease makes sure that the leased sidecar channel can be found @@ -629,3 +752,32 @@ func remoteActiveBalanceCheck(balance int64) activeChanCheck { return nil } } + +// assertSidecarState asserts the number of sidecar tickets and the state of a +// single ticket within the list. +func assertSidecarState(t *testing.T, trader *traderHarness, numTickets int, + id []byte, state sidecar.State) { + + t.Helper() + + sidecars, err := trader.ListSidecars( + context.Background(), &poolrpc.ListSidecarsRequest{}, + ) + require.NoError(t, err) + require.Len(t, sidecars.Tickets, numTickets) + + found := false + for _, ticket := range sidecars.Tickets { + if !bytes.Equal(ticket.Id, id) { + continue + } + + found = true + require.Equal(t, state.String(), ticket.State) + break + } + + if !found { + require.Fail(t, "ticket with ID %x not found", id) + } +} diff --git a/itest/test_harness.go b/itest/test_harness.go index da69ceea9..32597470a 100644 --- a/itest/test_harness.go +++ b/itest/test_harness.go @@ -1347,8 +1347,9 @@ func assertAskOrderState(t *harnessTest, trader *traderHarness, var orderFound bool for _, order := range resp.Asks { - if !bytes.Equal(order.Details.OrderNonce, - orderNonce[:]) { + if !bytes.Equal( + order.Details.OrderNonce, orderNonce[:], + ) { continue } @@ -1360,6 +1361,7 @@ func assertAskOrderState(t *harnessTest, trader *traderHarness, } orderFound = true + break } if !orderFound { @@ -1368,9 +1370,45 @@ func assertAskOrderState(t *harnessTest, trader *traderHarness, return nil }, defaultWaitTimeout) - if err != nil { - t.Fatalf("order state doesn't match: %v", err) - } + require.NoError(t.t, err) +} + +func assertBidOrderState(t *harnessTest, trader *traderHarness, + orderNonce orderT.Nonce, expectedState auctioneerrpc.OrderState) { + + err := wait.NoError(func() error { + req := &poolrpc.ListOrdersRequest{} + resp, err := trader.ListOrders(context.Background(), req) + if err != nil { + return err + } + + var orderFound bool + for _, order := range resp.Bids { + if !bytes.Equal( + order.Details.OrderNonce, orderNonce[:], + ) { + continue + } + + if order.Details.State != expectedState { + return fmt.Errorf("order has state %v, "+ + " expected %v", + order.Details.State, + expectedState) + } + + orderFound = true + break + } + + if !orderFound { + return fmt.Errorf("order not found") + } + + return nil + }, defaultWaitTimeout) + require.NoError(t.t, err) } // assertChannelClosed asserts that the channel is properly cleaned up after diff --git a/itest/test_list_off.go b/itest/test_list_off_test.go similarity index 100% rename from itest/test_list_off.go rename to itest/test_list_off_test.go diff --git a/itest/test_list_on.go b/itest/test_list_on_test.go similarity index 96% rename from itest/test_list_on.go rename to itest/test_list_on_test.go index 8afbd635a..19b92a3cd 100644 --- a/itest/test_list_on.go +++ b/itest/test_list_on_test.go @@ -104,6 +104,10 @@ var testCases = []*testCase{ name: "sidecar channels reject min chan size", test: testSidecarChannelsRejectMinChanSize, }, + { + name: "sidecar channels cancellation", + test: testSidecarTicketCancellation, + }, { name: "hashmail server",