mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
149 lines
4.6 KiB
Go
149 lines
4.6 KiB
Go
package funding
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/davecgh/go-spew/spew"
|
|
"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,
|
|
baseClient BaseClient, 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 = baseClient.FundingStateStep(
|
|
ctxb, &lnrpc.FundingTransitionMsg{
|
|
Trigger: cancelShimMsg,
|
|
},
|
|
)
|
|
if err != nil {
|
|
log.Warnf("Unable to unregister funding shim "+
|
|
"(pendingChanID=%x) for order=%v: %v",
|
|
pendingChanID[:], bidNonce, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
baseClient BaseClient, 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 {
|
|
log.Warnf("Could not find channel output for "+
|
|
"our_order=%v, matched_order=%v in "+
|
|
"latest batch TX=%v",
|
|
ourOrderNonce.String(),
|
|
matchedOrder.Order.Nonce(),
|
|
spew.Sdump(batchTx))
|
|
continue
|
|
}
|
|
|
|
channelPoint := &lnrpc.ChannelPoint{
|
|
OutputIndex: idx,
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
FundingTxidBytes: txHash[:],
|
|
},
|
|
}
|
|
_, err = baseClient.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",
|
|
ourOrderNonce.String(), txHash, idx,
|
|
err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|