Merge pull request #4560 from guggero/abandon-shim-chans
Shim funded channels: don't count towards max pending channels, allow to abandon them
This commit is contained in:
7 files changed
+954
-851
No files matched your search
+20
-2
@@ -1210,9 +1210,21 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
// sum of the active reservations and the channels pending open in the
|
// sum of the active reservations and the channels pending open in the
|
||||||
// database.
|
// database.
|
||||||
f.resMtx.RLock()
|
f.resMtx.RLock()
|
||||||
numPending := len(f.activeReservations[peerIDKey])
|
reservations := f.activeReservations[peerIDKey]
|
||||||
f.resMtx.RUnlock()
|
f.resMtx.RUnlock()
|
||||||
|
|
||||||
|
// We don't count reservations that were created from a canned funding
|
||||||
|
// shim. The user has registered the shim and therefore expects this
|
||||||
|
// channel to arrive.
|
||||||
|
numPending := 0
|
||||||
|
for _, res := range reservations {
|
||||||
|
if !res.reservation.IsCannedShim() {
|
||||||
|
numPending++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also count the channels that are already pending. There we don't know
|
||||||
|
// the underlying intent anymore, unfortunately.
|
||||||
channels, err := f.cfg.Wallet.Cfg.Database.FetchOpenChannels(peerPubKey)
|
channels, err := f.cfg.Wallet.Cfg.Database.FetchOpenChannels(peerPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.failFundingFlow(
|
f.failFundingFlow(
|
||||||
@@ -1222,7 +1234,13 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range channels {
|
for _, c := range channels {
|
||||||
if c.IsPending {
|
// Pending channels that have a non-zero thaw height were also
|
||||||
|
// created through a canned funding shim. Those also don't
|
||||||
|
// count towards the DoS protection limit.
|
||||||
|
//
|
||||||
|
// TODO(guggero): Properly store the funding type (wallet, shim,
|
||||||
|
// PSBT) on the channel so we don't need to use the thaw height.
|
||||||
|
if c.IsPending && c.ThawHeight == 0 {
|
||||||
numPending++
|
numPending++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+758
-745
File diff suppressed because it is too large.
Load diff
+6
-2
@@ -235,8 +235,10 @@ service Lightning {
|
|||||||
/* lncli: `abandonchannel`
|
/* lncli: `abandonchannel`
|
||||||
AbandonChannel removes all channel state from the database except for a
|
AbandonChannel removes all channel state from the database except for a
|
||||||
close summary. This method can be used to get rid of permanently unusable
|
close summary. This method can be used to get rid of permanently unusable
|
||||||
channels due to bugs fixed in newer versions of lnd. Only available
|
channels due to bugs fixed in newer versions of lnd. This method can also be
|
||||||
when in debug builds of lnd.
|
used to remove externally funded channels where the funding transaction was
|
||||||
|
never broadcast. Only available for non-externally funded channels in dev
|
||||||
|
build.
|
||||||
*/
|
*/
|
||||||
rpc AbandonChannel (AbandonChannelRequest) returns (AbandonChannelResponse);
|
rpc AbandonChannel (AbandonChannelRequest) returns (AbandonChannelResponse);
|
||||||
|
|
||||||
@@ -3058,6 +3060,8 @@ message DeleteAllPaymentsResponse {
|
|||||||
|
|
||||||
message AbandonChannelRequest {
|
message AbandonChannelRequest {
|
||||||
ChannelPoint channel_point = 1;
|
ChannelPoint channel_point = 1;
|
||||||
|
|
||||||
|
bool pending_funding_shim_only = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AbandonChannelResponse {
|
message AbandonChannelResponse {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@
|
|||||||
},
|
},
|
||||||
"/v1/channels/abandon/{channel_point.funding_txid_str}/{channel_point.output_index}": {
|
"/v1/channels/abandon/{channel_point.funding_txid_str}/{channel_point.output_index}": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"summary": "lncli: `abandonchannel`\nAbandonChannel removes all channel state from the database except for a\nclose summary. This method can be used to get rid of permanently unusable\nchannels due to bugs fixed in newer versions of lnd. Only available\nwhen in debug builds of lnd.",
|
"summary": "lncli: `abandonchannel`\nAbandonChannel removes all channel state from the database except for a\nclose summary. This method can be used to get rid of permanently unusable\nchannels due to bugs fixed in newer versions of lnd. This method can also be\nused to remove externally funded channels where the funding transaction was\nnever broadcast. Only available for non-externally funded channels in dev\nbuild.",
|
||||||
"operationId": "AbandonChannel",
|
"operationId": "AbandonChannel",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
@@ -190,6 +190,13 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "byte"
|
"format": "byte"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pending_funding_shim_only",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "boolean",
|
||||||
|
"format": "boolean"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
+137
-98
@@ -13795,77 +13795,178 @@ func testExternalFundingChanPoint(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
// First, we'll create two new nodes that we'll use to open channel
|
// First, we'll create two new nodes that we'll use to open channel
|
||||||
// between for this test.
|
// between for this test.
|
||||||
carol, err := net.NewNode("carol", nil)
|
carol, err := net.NewNode("carol", nil)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to start new node: %v", err)
|
|
||||||
}
|
|
||||||
defer shutdownAndAssert(net, t, carol)
|
defer shutdownAndAssert(net, t, carol)
|
||||||
|
|
||||||
dave, err := net.NewNode("dave", nil)
|
dave, err := net.NewNode("dave", nil)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to start new node: %v", err)
|
|
||||||
}
|
|
||||||
defer shutdownAndAssert(net, t, dave)
|
defer shutdownAndAssert(net, t, dave)
|
||||||
|
|
||||||
// Carol will be funding the channel, so we'll send some coins over to
|
// Carol will be funding the channel, so we'll send some coins over to
|
||||||
// her and ensure they have enough confirmations before we proceed.
|
// her and ensure they have enough confirmations before we proceed.
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol)
|
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to send coins to carol: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we start the test, we'll ensure both sides are connected to
|
// Before we start the test, we'll ensure both sides are connected to
|
||||||
// the funding flow can properly be executed.
|
// the funding flow can properly be executed.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
err = net.EnsureConnected(ctxt, carol, dave)
|
err = net.EnsureConnected(ctxt, carol, dave)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to connect peers: %v", err)
|
|
||||||
|
// At this point, we're ready to simulate our external channel funding
|
||||||
|
// flow. To start with, we'll create a pending channel with a shim for
|
||||||
|
// a transaction that will never be published.
|
||||||
|
const thawHeight uint32 = 10
|
||||||
|
const chanSize = lnd.MaxBtcFundingAmount
|
||||||
|
fundingShim1, chanPoint1, _ := deriveFundingShim(
|
||||||
|
net, t, carol, dave, chanSize, thawHeight, 1, false,
|
||||||
|
)
|
||||||
|
_ = openChannelStream(
|
||||||
|
ctxb, t, net, carol, dave, lntest.OpenChannelParams{
|
||||||
|
Amt: chanSize,
|
||||||
|
FundingShim: fundingShim1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
assertNumOpenChannelsPending(ctxt, t, carol, dave, 1)
|
||||||
|
|
||||||
|
// That channel is now pending forever and normally would saturate the
|
||||||
|
// max pending channel limit for both nodes. But because the channel is
|
||||||
|
// externally funded, we should still be able to open another one. Let's
|
||||||
|
// do exactly that now. For this one we publish the transaction so we
|
||||||
|
// can mine it later.
|
||||||
|
fundingShim2, chanPoint2, _ := deriveFundingShim(
|
||||||
|
net, t, carol, dave, chanSize, thawHeight, 2, true,
|
||||||
|
)
|
||||||
|
|
||||||
|
// At this point, we'll now carry out the normal basic channel funding
|
||||||
|
// test as everything should now proceed as normal (a regular channel
|
||||||
|
// funding flow).
|
||||||
|
carolChan, daveChan, _, err := basicChannelFundingTest(
|
||||||
|
t, net, carol, dave, fundingShim2,
|
||||||
|
)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Both channels should be marked as frozen with the proper thaw
|
||||||
|
// height.
|
||||||
|
if carolChan.ThawHeight != thawHeight {
|
||||||
|
t.Fatalf("expected thaw height of %v, got %v",
|
||||||
|
carolChan.ThawHeight, thawHeight)
|
||||||
|
}
|
||||||
|
if daveChan.ThawHeight != thawHeight {
|
||||||
|
t.Fatalf("expected thaw height of %v, got %v",
|
||||||
|
daveChan.ThawHeight, thawHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we're ready to simulate our external channle funding
|
// Next, to make sure the channel functions as normal, we'll make some
|
||||||
// flow. To start with, we'll get to new keys from both sides which
|
// payments within the channel.
|
||||||
// will be used to create the multi-sig output for the external funding
|
payAmt := btcutil.Amount(100000)
|
||||||
// transaction.
|
invoice := &lnrpc.Invoice{
|
||||||
|
Memo: "new chans",
|
||||||
|
Value: int64(payAmt),
|
||||||
|
}
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
resp, err := dave.AddInvoice(ctxt, invoice)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
err = completePaymentRequests(
|
||||||
|
ctxt, carol, carol.RouterClient, []string{resp.PaymentRequest},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// Now that the channels are open, and we've confirmed that they're
|
||||||
|
// operational, we'll now ensure that the channels are frozen as
|
||||||
|
// intended (if requested).
|
||||||
|
//
|
||||||
|
// First, we'll try to close the channel as Carol, the initiator. This
|
||||||
|
// should fail as a frozen channel only allows the responder to
|
||||||
|
// initiate a channel close.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||||
|
_, _, err = net.CloseChannel(ctxt, carol, chanPoint2, false)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("carol wasn't denied a co-op close attempt for a " +
|
||||||
|
"frozen channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we'll try but this time with Dave (the responder) as the
|
||||||
|
// initiator. This time the channel should be closed as normal.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||||
|
closeChannelAndAssert(ctxt, t, net, dave, chanPoint2, false)
|
||||||
|
|
||||||
|
// As a last step, we check if we still have the pending channel hanging
|
||||||
|
// around because we never published the funding TX.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
assertNumOpenChannelsPending(ctxt, t, carol, dave, 1)
|
||||||
|
|
||||||
|
// Let's make sure we can abandon it.
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
_, err = carol.AbandonChannel(ctxt, &lnrpc.AbandonChannelRequest{
|
||||||
|
ChannelPoint: chanPoint1,
|
||||||
|
PendingFundingShimOnly: true,
|
||||||
|
})
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
_, err = dave.AbandonChannel(ctxt, &lnrpc.AbandonChannelRequest{
|
||||||
|
ChannelPoint: chanPoint1,
|
||||||
|
PendingFundingShimOnly: true,
|
||||||
|
})
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
// It should now not appear in the pending channels anymore.
|
||||||
|
assertNumOpenChannelsPending(ctxt, t, carol, dave, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deriveFundingShim creates a channel funding shim by deriving the necessary
|
||||||
|
// keys on both sides.
|
||||||
|
func deriveFundingShim(net *lntest.NetworkHarness, t *harnessTest,
|
||||||
|
carol, dave *lntest.HarnessNode, chanSize btcutil.Amount,
|
||||||
|
thawHeight uint32, keyIndex int32, publish bool) (*lnrpc.FundingShim,
|
||||||
|
*lnrpc.ChannelPoint, *chainhash.Hash) {
|
||||||
|
|
||||||
|
ctxb := context.Background()
|
||||||
keyLoc := &signrpc.KeyLocator{
|
keyLoc := &signrpc.KeyLocator{
|
||||||
KeyFamily: 9999,
|
KeyFamily: 9999,
|
||||||
KeyIndex: 1,
|
KeyIndex: keyIndex,
|
||||||
}
|
}
|
||||||
carolFundingKey, err := carol.WalletKitClient.DeriveKey(ctxb, keyLoc)
|
carolFundingKey, err := carol.WalletKitClient.DeriveKey(ctxb, keyLoc)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to get carol funding key: %v", err)
|
|
||||||
}
|
|
||||||
daveFundingKey, err := dave.WalletKitClient.DeriveKey(ctxb, keyLoc)
|
daveFundingKey, err := dave.WalletKitClient.DeriveKey(ctxb, keyLoc)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to get dave funding key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we have the multi-sig keys for each party, we can manually
|
// Now that we have the multi-sig keys for each party, we can manually
|
||||||
// construct the funding transaction. We'll instruct the backend to
|
// construct the funding transaction. We'll instruct the backend to
|
||||||
// immediately create and broadcast a transaction paying out an exact
|
// immediately create and broadcast a transaction paying out an exact
|
||||||
// amount. Normally this would reside in the mempool, but we just
|
// amount. Normally this would reside in the mempool, but we just
|
||||||
// confirm it now for simplicity.
|
// confirm it now for simplicity.
|
||||||
const chanSize = lnd.MaxBtcFundingAmount
|
|
||||||
_, fundingOutput, err := input.GenFundingPkScript(
|
_, fundingOutput, err := input.GenFundingPkScript(
|
||||||
carolFundingKey.RawKeyBytes, daveFundingKey.RawKeyBytes,
|
carolFundingKey.RawKeyBytes, daveFundingKey.RawKeyBytes,
|
||||||
int64(chanSize),
|
int64(chanSize),
|
||||||
)
|
)
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to create funding script: %v", err)
|
|
||||||
}
|
var txid *chainhash.Hash
|
||||||
txid, err := net.Miner.SendOutputsWithoutChange(
|
targetOutputs := []*wire.TxOut{fundingOutput}
|
||||||
[]*wire.TxOut{fundingOutput}, 5,
|
if publish {
|
||||||
)
|
txid, err = net.Miner.SendOutputsWithoutChange(
|
||||||
if err != nil {
|
targetOutputs, 5,
|
||||||
t.Fatalf("unable to create funding output: %v", err)
|
)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
} else {
|
||||||
|
tx, err := net.Miner.CreateTransaction(targetOutputs, 5, false)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
txHash := tx.TxHash()
|
||||||
|
txid = &txHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we can being our external channel funding workflow.
|
// At this point, we can being our external channel funding workflow.
|
||||||
// We'll start by generating a pending channel ID externally that will
|
// We'll start by generating a pending channel ID externally that will
|
||||||
// be used to track this new funding type.
|
// be used to track this new funding type.
|
||||||
var pendingChanID [32]byte
|
var pendingChanID [32]byte
|
||||||
if _, err := rand.Read(pendingChanID[:]); err != nil {
|
_, err = rand.Read(pendingChanID[:])
|
||||||
t.Fatalf("unable to gen pending chan ID: %v", err)
|
require.NoError(t.t, err)
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we have the pending channel ID, Dave (our responder) will
|
// Now that we have the pending channel ID, Dave (our responder) will
|
||||||
// register the intent to receive a new channel funding workflow using
|
// register the intent to receive a new channel funding workflow using
|
||||||
@@ -13875,7 +13976,6 @@ func testExternalFundingChanPoint(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
FundingTxidBytes: txid[:],
|
FundingTxidBytes: txid[:],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
thawHeight := uint32(10)
|
|
||||||
chanPointShim := &lnrpc.ChanPointShim{
|
chanPointShim := &lnrpc.ChanPointShim{
|
||||||
Amt: int64(chanSize),
|
Amt: int64(chanSize),
|
||||||
ChanPoint: chanPoint,
|
ChanPoint: chanPoint,
|
||||||
@@ -13900,9 +14000,7 @@ func testExternalFundingChanPoint(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
ShimRegister: fundingShim,
|
ShimRegister: fundingShim,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
require.NoError(t.t, err)
|
||||||
t.Fatalf("unable to walk funding state forward: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we attempt to register the same shim (has the same pending chan
|
// If we attempt to register the same shim (has the same pending chan
|
||||||
// ID), then we should get an error.
|
// ID), then we should get an error.
|
||||||
@@ -13928,66 +14026,7 @@ func testExternalFundingChanPoint(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
fundingShim.GetChanPointShim().RemoteKey = daveFundingKey.RawKeyBytes
|
fundingShim.GetChanPointShim().RemoteKey = daveFundingKey.RawKeyBytes
|
||||||
|
|
||||||
// At this point, we'll now carry out the normal basic channel funding
|
return fundingShim, chanPoint, txid
|
||||||
// test as everything should now proceed as normal (a regular channel
|
|
||||||
// funding flow).
|
|
||||||
carolChan, daveChan, _, err := basicChannelFundingTest(
|
|
||||||
t, net, carol, dave, fundingShim,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to open channels: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both channels should be marked as frozen with the proper thaw
|
|
||||||
// height.
|
|
||||||
if carolChan.ThawHeight != thawHeight {
|
|
||||||
t.Fatalf("expected thaw height of %v, got %v",
|
|
||||||
carolChan.ThawHeight, thawHeight)
|
|
||||||
}
|
|
||||||
if daveChan.ThawHeight != thawHeight {
|
|
||||||
t.Fatalf("expected thaw height of %v, got %v",
|
|
||||||
daveChan.ThawHeight, thawHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, to make sure the channel functions as normal, we'll make some
|
|
||||||
// payments within the channel.
|
|
||||||
payAmt := btcutil.Amount(100000)
|
|
||||||
invoice := &lnrpc.Invoice{
|
|
||||||
Memo: "new chans",
|
|
||||||
Value: int64(payAmt),
|
|
||||||
}
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
resp, err := dave.AddInvoice(ctxt, invoice)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to add invoice: %v", err)
|
|
||||||
}
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
err = completePaymentRequests(
|
|
||||||
ctxt, carol, carol.RouterClient, []string{resp.PaymentRequest},
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unable to make payments between Carol and Dave")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that the channels are open, and we've confirmed that they're
|
|
||||||
// operational, we'll now ensure that the channels are frozen as
|
|
||||||
// intended (if requested).
|
|
||||||
//
|
|
||||||
// First, we'll try to close the channel as Carol, the initiator. This
|
|
||||||
// should fail as a frozen channel only allows the responder to
|
|
||||||
// initiate a channel close.
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
|
||||||
_, _, err = net.CloseChannel(ctxt, carol, chanPoint, false)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("carol wasn't denied a co-op close attempt for a " +
|
|
||||||
"frozen channel")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next we'll try but this time with Dave (the responder) as the
|
|
||||||
// initiator. This time the channel should be closed as normal.
|
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
|
||||||
closeChannelAndAssert(ctxt, t, net, dave, chanPoint, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendAndAssertSuccess sends the given payment requests and asserts that the
|
// sendAndAssertSuccess sends the given payment requests and asserts that the
|
||||||
|
|||||||
@@ -492,6 +492,13 @@ func (r *ChannelReservation) IsPsbt() bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCannedShim returns true if there is a canned shim funding intent mapped to
|
||||||
|
// this reservation.
|
||||||
|
func (r *ChannelReservation) IsCannedShim() bool {
|
||||||
|
_, ok := r.fundingIntent.(*chanfunding.ShimIntent)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessPsbt continues a previously paused funding flow that involves PSBT to
|
// ProcessPsbt continues a previously paused funding flow that involves PSBT to
|
||||||
// construct the funding transaction. This method can be called once the PSBT is
|
// construct the funding transaction. This method can be called once the PSBT is
|
||||||
// finalized and the signed transaction is available.
|
// finalized and the signed transaction is available.
|
||||||
|
|||||||
+18
-3
@@ -2358,13 +2358,14 @@ func abandonChanFromGraph(chanGraph *channeldb.ChannelGraph,
|
|||||||
// AbandonChannel removes all channel state from the database except for a
|
// AbandonChannel removes all channel state from the database except for a
|
||||||
// close summary. This method can be used to get rid of permanently unusable
|
// close summary. This method can be used to get rid of permanently unusable
|
||||||
// channels due to bugs fixed in newer versions of lnd.
|
// channels due to bugs fixed in newer versions of lnd.
|
||||||
func (r *rpcServer) AbandonChannel(ctx context.Context,
|
func (r *rpcServer) AbandonChannel(_ context.Context,
|
||||||
in *lnrpc.AbandonChannelRequest) (*lnrpc.AbandonChannelResponse, error) {
|
in *lnrpc.AbandonChannelRequest) (*lnrpc.AbandonChannelResponse, error) {
|
||||||
|
|
||||||
// If this isn't the dev build, then we won't allow the RPC to be
|
// If this isn't the dev build, then we won't allow the RPC to be
|
||||||
// executed, as it's an advanced feature and won't be activated in
|
// executed, as it's an advanced feature and won't be activated in
|
||||||
// regular production/release builds.
|
// regular production/release builds except for the explicit case of
|
||||||
if !build.IsDevBuild() {
|
// externally funded channels that are still pending.
|
||||||
|
if !in.PendingFundingShimOnly && !build.IsDevBuild() {
|
||||||
return nil, fmt.Errorf("AbandonChannel RPC call only " +
|
return nil, fmt.Errorf("AbandonChannel RPC call only " +
|
||||||
"available in dev builds")
|
"available in dev builds")
|
||||||
}
|
}
|
||||||
@@ -2396,6 +2397,20 @@ func (r *rpcServer) AbandonChannel(ctx context.Context,
|
|||||||
// on-disk state, we'll remove the channel from the switch and peer
|
// on-disk state, we'll remove the channel from the switch and peer
|
||||||
// state if it's been loaded in.
|
// state if it's been loaded in.
|
||||||
case err == nil:
|
case err == nil:
|
||||||
|
// If the user requested the more safe version that only allows
|
||||||
|
// the removal of externally (shim) funded channels that are
|
||||||
|
// still pending, we enforce this option now that we know the
|
||||||
|
// state of the channel.
|
||||||
|
//
|
||||||
|
// TODO(guggero): Properly store the funding type (wallet, shim,
|
||||||
|
// PSBT) on the channel so we don't need to use the thaw height.
|
||||||
|
isShimFunded := dbChan.ThawHeight > 0
|
||||||
|
isPendingShimFunded := isShimFunded && dbChan.IsPending
|
||||||
|
if in.PendingFundingShimOnly && !isPendingShimFunded {
|
||||||
|
return nil, fmt.Errorf("channel %v is not externally "+
|
||||||
|
"funded or not pending", chanPoint)
|
||||||
|
}
|
||||||
|
|
||||||
// We'll mark the channel as borked before we remove the state
|
// We'll mark the channel as borked before we remove the state
|
||||||
// from the switch/peer so it won't be loaded back in if the
|
// from the switch/peer so it won't be loaded back in if the
|
||||||
// peer reconnects.
|
// peer reconnects.
|
||||||
|
|||||||
Reference in new issue
Block a user