From 02cd3ffa285ad370dd544845094cf496588f3f65 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 7 Dec 2020 14:23:55 +0100 Subject: [PATCH] multi: move cleanup functions to funding package To delegate the pending batch cleanup to the funding manager, we first need to move the cleanup functions into the funding package. --- funding/batch.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++ order/batch.go | 133 ------------------------------------------- rpcserver.go | 6 +- 3 files changed, 146 insertions(+), 136 deletions(-) create mode 100644 funding/batch.go diff --git a/funding/batch.go b/funding/batch.go new file mode 100644 index 0000000..fa69ef9 --- /dev/null +++ b/funding/batch.go @@ -0,0 +1,143 @@ +package funding + +import ( + "context" + "fmt" + "strings" + + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/pool/order" + "github.com/lightningnetwork/lnd/lnrpc" +) + +// CancelPendingFundingShims cancels all funding shims we registered when +// preparing for the orders in a batch. This should be called if for any reason +// we need to reject the batch, so we're able to process any subsequent modified +// batches. +func CancelPendingFundingShims(matchedOrders map[order.Nonce][]*order.MatchedOrder, + lndClient lnrpc.LightningClient, fetchOrder order.Fetcher) error { + + // Since we support partial matches, a given bid of ours could've been + // matched with multiple asks, so we'll iterate through all those to + // ensure we unregister all the created shims. + ctxb := context.Background() + for ourOrderNonce, matchedOrders := range matchedOrders { + ourOrder, err := fetchOrder(ourOrderNonce) + if err != nil { + return err + } + + orderIsAsk := ourOrder.Type() == order.TypeAsk + + // If the order as an ask, then we don't need to do anything, + // as we only register funding shims for incoming channels (so + // buys). + if orderIsAsk { + continue + } + + // For each ask order that was matched with this bid, we'll + // re-derive the pending chan ID key used, then attempt to + // unregister it. + for _, matchedOrder := range matchedOrders { + bidNonce := ourOrder.Nonce() + askNonce := matchedOrder.Order.Nonce() + pendingChanID := order.PendingChanKey( + askNonce, bidNonce, + ) + + cancelShimMsg := &lnrpc.FundingTransitionMsg_ShimCancel{ + ShimCancel: &lnrpc.FundingShimCancel{ + PendingChanId: pendingChanID[:], + }, + } + + _, err = lndClient.FundingStateStep( + ctxb, &lnrpc.FundingTransitionMsg{ + Trigger: cancelShimMsg, + }, + ) + if err != nil { + log.Warnf("Unable to unregister funding shim "+ + "(pendingChanID=%x) for order=%v", + pendingChanID[:], bidNonce) + } + } + } + + return nil +} + +// AbandonCanceledChannels removes all channels from lnd's channel database that +// were created for an iteration of a batch that never made it to chain in the +// provided configuration. This should be called whenever a batch is replaced +// with an updated version because some traders were offline or rejected the +// batch. If a non-nil error is returned, something with reading the local order +// or extracting the channel outpoint went wrong and we should fail hard. If the +// channel cannot be abandoned for some reason, the error is just logged but not +// returned. +func AbandonCanceledChannels(matchedOrders map[order.Nonce][]*order.MatchedOrder, + batchTx *wire.MsgTx, wallet lndclient.WalletKitClient, + lndClient lnrpc.LightningClient, fetchOrder order.Fetcher) error { + + // Since we support partial matches, a given bid of ours could've been + // matched with multiple asks, so we'll iterate through all those to + // ensure we remove all channels that never made it to chain. + ctxb := context.Background() + txHash := batchTx.TxHash() + for ourOrderNonce, matchedOrders := range matchedOrders { + ourOrder, err := fetchOrder(ourOrderNonce) + if err != nil { + return err + } + + // For each ask order that was matched with this bid, we'll + // locate the channel outpoint then abandon it from lnd's + // channel database. + for _, matchedOrder := range matchedOrders { + _, idx, err := order.ChannelOutput( + batchTx, wallet, ourOrder, matchedOrder, + ) + if err != nil { + return fmt.Errorf("error locating channel "+ + "outpoint: %v", err) + } + + channelPoint := &lnrpc.ChannelPoint{ + OutputIndex: idx, + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: txHash[:], + }, + } + _, err = lndClient.AbandonChannel( + ctxb, &lnrpc.AbandonChannelRequest{ + ChannelPoint: channelPoint, + PendingFundingShimOnly: true, + }, + ) + const notFoundErr = "unable to find closed channel" + if err != nil { + // If the channel was never created in the first + // place, it might just not exist. Therefore we + // ignore the "not found" error but fail on any + // other error. + if !strings.Contains(err.Error(), notFoundErr) { + log.Errorf("Unexpected error when "+ + "trying to clean up pending "+ + "channels: %v", err) + return err + } + + log.Debugf("Cleaning up incomplete/replaced "+ + "pending channel in lnd was "+ + "unsuccessful for order=%v "+ + "(channel_point=%v:%d), assuming "+ + "timeout when funding: %v", txHash, idx, + ourOrderNonce, err) + } + } + } + + return nil +} diff --git a/order/batch.go b/order/batch.go index bc99d39..170801f 100644 --- a/order/batch.go +++ b/order/batch.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "net" - "strings" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" @@ -16,7 +15,6 @@ import ( "github.com/lightninglabs/pool/poolscript" "github.com/lightninglabs/pool/terms" "github.com/lightningnetwork/lnd/input" - "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -193,137 +191,6 @@ type Batch struct { // order based on its nonce. type Fetcher func(Nonce) (Order, error) -// CancelPendingFundingShims cancels all funding shims we registered when -// preparing for the orders in a batch. This should be called if for any reason -// we need to reject the batch, so we're able to process any subsequent modified -// batches. -func CancelPendingFundingShims(matchedOrders map[Nonce][]*MatchedOrder, - lndClient lnrpc.LightningClient, fetchOrder Fetcher) error { - - // Since we support partial matches, a given bid of ours could've been - // matched with multiple asks, so we'll iterate through all those to - // ensure we unregister all the created shims. - ctxb := context.Background() - for ourOrderNonce, matchedOrders := range matchedOrders { - ourOrder, err := fetchOrder(ourOrderNonce) - if err != nil { - return err - } - - orderIsAsk := ourOrder.Type() == TypeAsk - - // If the order as an ask, then we don't need to do anything, - // as we only register funding shims for incoming channels (so - // buys). - if orderIsAsk { - continue - } - - // For each ask order that was matched with this bid, we'll - // re-derive the pending chan ID key used, then attempt to - // unregister it. - for _, matchedOrder := range matchedOrders { - bidNonce := ourOrder.Nonce() - askNonce := matchedOrder.Order.Nonce() - pendingChanID := PendingChanKey( - askNonce, bidNonce, - ) - - cancelShimMsg := &lnrpc.FundingTransitionMsg_ShimCancel{ - ShimCancel: &lnrpc.FundingShimCancel{ - PendingChanId: pendingChanID[:], - }, - } - - _, err = lndClient.FundingStateStep( - ctxb, &lnrpc.FundingTransitionMsg{ - Trigger: cancelShimMsg, - }, - ) - if err != nil { - log.Warnf("Unable to unregister funding shim "+ - "(pendingChanID=%x) for order=%v", - pendingChanID[:], bidNonce) - } - } - } - - return nil -} - -// AbandonCanceledChannels removes all channels from lnd's channel database that -// were created for an iteration of a batch that never made it to chain in the -// provided configuration. This should be called whenever a batch is replaced -// with an updated version because some traders were offline or rejected the -// batch. If a non-nil error is returned, something with reading the local order -// or extracting the channel outpoint went wrong and we should fail hard. If the -// channel cannot be abandoned for some reason, the error is just logged but not -// returned. -func AbandonCanceledChannels(matchedOrders map[Nonce][]*MatchedOrder, - batchTx *wire.MsgTx, wallet lndclient.WalletKitClient, - lndClient lnrpc.LightningClient, fetchOrder Fetcher) error { - - // Since we support partial matches, a given bid of ours could've been - // matched with multiple asks, so we'll iterate through all those to - // ensure we remove all channels that never made it to chain. - ctxb := context.Background() - txHash := batchTx.TxHash() - for ourOrderNonce, matchedOrders := range matchedOrders { - ourOrder, err := fetchOrder(ourOrderNonce) - if err != nil { - return err - } - - // For each ask order that was matched with this bid, we'll - // locate the channel outpoint then abandon it from lnd's - // channel database. - for _, matchedOrder := range matchedOrders { - _, idx, err := ChannelOutput( - batchTx, wallet, ourOrder, matchedOrder, - ) - if err != nil { - return fmt.Errorf("error locating channel "+ - "outpoint: %v", err) - } - - channelPoint := &lnrpc.ChannelPoint{ - OutputIndex: idx, - FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ - FundingTxidBytes: txHash[:], - }, - } - _, err = lndClient.AbandonChannel( - ctxb, &lnrpc.AbandonChannelRequest{ - ChannelPoint: channelPoint, - PendingFundingShimOnly: true, - }, - ) - const notFoundErr = "unable to find closed channel" - if err != nil { - // If the channel was never created in the first - // place, it might just not exist. Therefore we - // ignore the "not found" error but fail on any - // other error. - if !strings.Contains(err.Error(), notFoundErr) { - log.Errorf("Unexpected error when "+ - "trying to clean up pending "+ - "channels: %v", err) - return err - } - - log.Debugf("Cleaning up incomplete/replaced "+ - "pending channel in lnd was "+ - "unsuccessful for order=%v "+ - "(channel_point=%v:%d), assuming "+ - "timeout when funding: %v", txHash, idx, - ourOrderNonce, err) - } - } - } - - return nil -} - // ChannelOutput returns the transaction output and output index of the channel // created for an order of ours that was matched with another one in a batch. func ChannelOutput(batchTx *wire.MsgTx, wallet lndclient.WalletKitClient, diff --git a/rpcserver.go b/rpcserver.go index 0ff9e60..c69df6c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -378,7 +378,7 @@ func (s *rpcServer) handleServerMessage(rpcMsg *poolrpc.ServerAuctionMessage) er if s.orderManager.HasPendingBatch() { pendingBatch := s.orderManager.PendingBatch() orderFetcher := s.server.db.GetOrder - err := order.CancelPendingFundingShims( + err := funding.CancelPendingFundingShims( pendingBatch.MatchedOrders, s.lndClient, orderFetcher, ) @@ -393,7 +393,7 @@ func (s *rpcServer) handleServerMessage(rpcMsg *poolrpc.ServerAuctionMessage) er // Also abandon any channels that might still be pending // from a previous round of the same batch or a previous // batch that we didn't make it into the final round. - err = order.AbandonCanceledChannels( + err = funding.AbandonCanceledChannels( pendingBatch.MatchedOrders, pendingBatch.BatchTX, s.lndServices.WalletKit, s.lndClient, orderFetcher, @@ -1388,7 +1388,7 @@ func (s *rpcServer) sendRejectBatch(batch *order.Batch, failure error) error { // As we're rejecting this batch, we'll now cancel all funding shims // that we may have registered since we may be matched with a distinct // set of channels if this batch is repeated. - err := order.CancelPendingFundingShims( + err := funding.CancelPendingFundingShims( batch.MatchedOrders, s.lndClient, func(o order.Nonce) (order.Order, error) { return s.server.db.GetOrder(o)