mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-10 22:27:22 +01:00
peer/lnwallet: persist shutdown info on send
In this commit, we start persisting shutdown info when we send the Shutdown message. When starting up a link, we also first check if we have previously persisted Shutdown info and if we have, we start the link in shutdown mode meaning that it will not accept any new outgoing HTLC additions and it will queue the shutdown message after any pending CommitSig has been sent.
This commit is contained in:
@@ -186,7 +186,7 @@ func coopCloseWithHTLCsWithRestart(ht *lntest.HarnessTest) {
|
|||||||
DeliveryAddress: newAddr.Address,
|
DeliveryAddress: newAddr.Address,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Assert that both nodes now see this channel as inactive.
|
// Assert that both nodes see the channel as waiting for close.
|
||||||
ht.AssertChannelInactive(bob, chanPoint)
|
ht.AssertChannelInactive(bob, chanPoint)
|
||||||
ht.AssertChannelInactive(alice, chanPoint)
|
ht.AssertChannelInactive(alice, chanPoint)
|
||||||
|
|
||||||
@@ -196,42 +196,35 @@ func coopCloseWithHTLCsWithRestart(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
ht.AssertConnected(alice, bob)
|
ht.AssertConnected(alice, bob)
|
||||||
|
|
||||||
// Show that the channel is seen as active again by Alice and Bob.
|
// Show that both nodes still see the channel as waiting for close after
|
||||||
//
|
// the restart.
|
||||||
// NOTE: This is a bug and will be fixed in an upcoming commit.
|
ht.AssertChannelInactive(bob, chanPoint)
|
||||||
ht.AssertChannelActive(alice, chanPoint)
|
ht.AssertChannelInactive(alice, chanPoint)
|
||||||
ht.AssertChannelActive(bob, chanPoint)
|
|
||||||
|
|
||||||
// Let's settle the invoice.
|
// Settle the invoice.
|
||||||
alice.RPC.SettleInvoice(preimage[:])
|
alice.RPC.SettleInvoice(preimage[:])
|
||||||
|
|
||||||
// Wait for the channel to appear in the waiting closed list.
|
// Wait for the channel to appear in the waiting closed list.
|
||||||
//
|
|
||||||
// NOTE: this will time out at the moment since there is a bug that
|
|
||||||
// results in shutdown not properly being re-started after a reconnect.
|
|
||||||
err := wait.Predicate(func() bool {
|
err := wait.Predicate(func() bool {
|
||||||
pendingChansResp := alice.RPC.PendingChannels()
|
pendingChansResp := alice.RPC.PendingChannels()
|
||||||
waitingClosed := pendingChansResp.WaitingCloseChannels
|
waitingClosed := pendingChansResp.WaitingCloseChannels
|
||||||
|
|
||||||
return len(waitingClosed) == 1
|
return len(waitingClosed) == 1
|
||||||
}, defaultTimeout)
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err)
|
||||||
|
|
||||||
// We assert here that there is a timeout error. This will be fixed in
|
// Wait for the close tx to be in the Mempool and then mine 6 blocks
|
||||||
// an upcoming commit.
|
// to confirm the close.
|
||||||
require.Error(ht, err)
|
closingTx := ht.AssertClosingTxInMempool(
|
||||||
|
chanPoint, lnrpc.CommitmentType_LEGACY,
|
||||||
|
)
|
||||||
|
ht.MineBlocksAndAssertNumTxes(6, 1)
|
||||||
|
|
||||||
// Since the channel closure did not continue, we need to re-init the
|
// Finally, we inspect the closing transaction here to show that the
|
||||||
// close.
|
// delivery address that Alice specified in her original close request
|
||||||
closingTXID := ht.CloseChannel(alice, chanPoint)
|
// is the one that ended up being used in the final closing transaction.
|
||||||
|
|
||||||
// To further demonstrate the extent of the bug, we inspect the closing
|
|
||||||
// transaction here to show that the delivery address that Alice
|
|
||||||
// specified in her original close request is not the one that ended up
|
|
||||||
// being used.
|
|
||||||
//
|
|
||||||
// NOTE: this is a bug that will be fixed in an upcoming commit.
|
|
||||||
tx := alice.RPC.GetTransaction(&walletrpc.GetTransactionRequest{
|
tx := alice.RPC.GetTransaction(&walletrpc.GetTransactionRequest{
|
||||||
Txid: closingTXID.String(),
|
Txid: closingTx.TxHash().String(),
|
||||||
})
|
})
|
||||||
require.Len(ht, tx.OutputDetails, 2)
|
require.Len(ht, tx.OutputDetails, 2)
|
||||||
|
|
||||||
@@ -245,6 +238,6 @@ func coopCloseWithHTLCsWithRestart(ht *lntest.HarnessTest) {
|
|||||||
}
|
}
|
||||||
require.NotNil(ht, outputDetail)
|
require.NotNil(ht, outputDetail)
|
||||||
|
|
||||||
// Show that the address used is not the one she requested.
|
// Show that the address used is the one she requested.
|
||||||
require.NotEqual(ht, outputDetail.Address, newAddr.Address)
|
require.Equal(ht, outputDetail.Address, newAddr.Address)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,6 +356,17 @@ func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) {
|
|||||||
chancloserLog.Infof("ChannelPoint(%v): sending shutdown message",
|
chancloserLog.Infof("ChannelPoint(%v): sending shutdown message",
|
||||||
c.chanPoint)
|
c.chanPoint)
|
||||||
|
|
||||||
|
// At this point, we persist any relevant info regarding the Shutdown
|
||||||
|
// message we are about to send in order to ensure that if a
|
||||||
|
// re-establish occurs then we will re-send the same Shutdown message.
|
||||||
|
shutdownInfo := channeldb.NewShutdownInfo(
|
||||||
|
c.localDeliveryScript, c.locallyInitiated,
|
||||||
|
)
|
||||||
|
err := c.cfg.Channel.MarkShutdownSent(shutdownInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return shutdown, nil
|
return shutdown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/discovery"
|
"github.com/lightningnetwork/lnd/discovery"
|
||||||
"github.com/lightningnetwork/lnd/feature"
|
"github.com/lightningnetwork/lnd/feature"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
||||||
@@ -986,6 +987,59 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shutdownInfo, err := lnChan.State().ShutdownInfo()
|
||||||
|
if err != nil && !errors.Is(err, channeldb.ErrNoShutdownInfo) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
shutdownMsg fn.Option[lnwire.Shutdown]
|
||||||
|
shutdownInfoErr error
|
||||||
|
)
|
||||||
|
shutdownInfo.WhenSome(func(info channeldb.ShutdownInfo) {
|
||||||
|
// Compute an ideal fee.
|
||||||
|
feePerKw, err := p.cfg.FeeEstimator.EstimateFeePerKW(
|
||||||
|
p.cfg.CoopCloseTargetConfs,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
shutdownInfoErr = fmt.Errorf("unable to "+
|
||||||
|
"estimate fee: %w", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chanCloser, err := p.createChanCloser(
|
||||||
|
lnChan, info.DeliveryScript.Val, feePerKw, nil,
|
||||||
|
info.LocalInitiator.Val,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
shutdownInfoErr = fmt.Errorf("unable to "+
|
||||||
|
"create chan closer: %w", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chanID := lnwire.NewChanIDFromOutPoint(
|
||||||
|
&lnChan.State().FundingOutpoint,
|
||||||
|
)
|
||||||
|
|
||||||
|
p.activeChanCloses[chanID] = chanCloser
|
||||||
|
|
||||||
|
// Create the Shutdown message.
|
||||||
|
shutdown, err := chanCloser.ShutdownChan()
|
||||||
|
if err != nil {
|
||||||
|
delete(p.activeChanCloses, chanID)
|
||||||
|
shutdownInfoErr = err
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownMsg = fn.Some[lnwire.Shutdown](*shutdown)
|
||||||
|
})
|
||||||
|
if shutdownInfoErr != nil {
|
||||||
|
return nil, shutdownInfoErr
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe to the set of on-chain events for this channel.
|
// Subscribe to the set of on-chain events for this channel.
|
||||||
chainEvents, err := p.cfg.ChainArb.SubscribeChannelEvents(
|
chainEvents, err := p.cfg.ChainArb.SubscribeChannelEvents(
|
||||||
*chanPoint,
|
*chanPoint,
|
||||||
@@ -996,7 +1050,7 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
|
|||||||
|
|
||||||
err = p.addLink(
|
err = p.addLink(
|
||||||
chanPoint, lnChan, forwardingPolicy, chainEvents,
|
chanPoint, lnChan, forwardingPolicy, chainEvents,
|
||||||
true,
|
true, shutdownMsg,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to add link %v to "+
|
return nil, fmt.Errorf("unable to add link %v to "+
|
||||||
@@ -1014,7 +1068,7 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
|
|||||||
lnChan *lnwallet.LightningChannel,
|
lnChan *lnwallet.LightningChannel,
|
||||||
forwardingPolicy *models.ForwardingPolicy,
|
forwardingPolicy *models.ForwardingPolicy,
|
||||||
chainEvents *contractcourt.ChainEventSubscription,
|
chainEvents *contractcourt.ChainEventSubscription,
|
||||||
syncStates bool) error {
|
syncStates bool, shutdownMsg fn.Option[lnwire.Shutdown]) error {
|
||||||
|
|
||||||
// onChannelFailure will be called by the link in case the channel
|
// onChannelFailure will be called by the link in case the channel
|
||||||
// fails for some reason.
|
// fails for some reason.
|
||||||
@@ -1083,6 +1137,7 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
|
|||||||
NotifyInactiveLinkEvent: p.cfg.ChannelNotifier.NotifyInactiveLinkEvent,
|
NotifyInactiveLinkEvent: p.cfg.ChannelNotifier.NotifyInactiveLinkEvent,
|
||||||
HtlcNotifier: p.cfg.HtlcNotifier,
|
HtlcNotifier: p.cfg.HtlcNotifier,
|
||||||
GetAliases: p.cfg.GetAliases,
|
GetAliases: p.cfg.GetAliases,
|
||||||
|
PreviouslySentShutdown: shutdownMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before adding our new link, purge the switch of any pending or live
|
// Before adding our new link, purge the switch of any pending or live
|
||||||
@@ -2802,15 +2857,32 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) (
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// As mentioned above, we don't re-create the delivery script.
|
var deliveryScript []byte
|
||||||
deliveryScript := c.LocalShutdownScript
|
|
||||||
if len(deliveryScript) == 0 {
|
shutdownInfo, err := c.ShutdownInfo()
|
||||||
var err error
|
switch {
|
||||||
deliveryScript, err = p.genDeliveryScript()
|
// We have previously stored the delivery script that we need to use
|
||||||
if err != nil {
|
// in the shutdown message. Re-use this script.
|
||||||
p.log.Errorf("unable to gen delivery script: %v",
|
case err == nil:
|
||||||
err)
|
shutdownInfo.WhenSome(func(info channeldb.ShutdownInfo) {
|
||||||
return nil, fmt.Errorf("close addr unavailable")
|
deliveryScript = info.DeliveryScript.Val
|
||||||
|
})
|
||||||
|
|
||||||
|
// An error other than ErrNoShutdownInfo was returned
|
||||||
|
case err != nil && !errors.Is(err, channeldb.ErrNoShutdownInfo):
|
||||||
|
return nil, err
|
||||||
|
|
||||||
|
case errors.Is(err, channeldb.ErrNoShutdownInfo):
|
||||||
|
deliveryScript = c.LocalShutdownScript
|
||||||
|
if len(deliveryScript) == 0 {
|
||||||
|
var err error
|
||||||
|
deliveryScript, err = p.genDeliveryScript()
|
||||||
|
if err != nil {
|
||||||
|
p.log.Errorf("unable to gen delivery script: "+
|
||||||
|
"%v", err)
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("close addr unavailable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3905,7 +3977,7 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error {
|
|||||||
// Create the link and add it to the switch.
|
// Create the link and add it to the switch.
|
||||||
err = p.addLink(
|
err = p.addLink(
|
||||||
chanPoint, lnChan, initialPolicy, chainEvents,
|
chanPoint, lnChan, initialPolicy, chainEvents,
|
||||||
shouldReestablish,
|
shouldReestablish, fn.None[lnwire.Shutdown](),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't register new channel link(%v) with "+
|
return fmt.Errorf("can't register new channel link(%v) with "+
|
||||||
|
|||||||
Reference in New Issue
Block a user