mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-31 17:51:33 +02:00
peer: add initial awareness of new rbf coop closer
In this commit, we use the interfaces we created in the prior commit to make a new method capable of spinning up the new rbf coop closer.
This commit is contained in:
@@ -9935,3 +9935,16 @@ func (lc *LightningChannel) FundingBlob() fn.Option[tlv.Blob] {
|
|||||||
return newBlob
|
return newBlob
|
||||||
})(lc.channelState.CustomBlob)
|
})(lc.channelState.CustomBlob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ZeroConfRealScid returns an optional real scid for the channel. If this
|
||||||
|
// returns None, then this isn't a zero conf channel. Otherwise, the real scid
|
||||||
|
// value will be returned.
|
||||||
|
//
|
||||||
|
//nolint:ll
|
||||||
|
func (lc *LightningChannel) ZeroConfRealScid() fn.Option[lnwire.ShortChannelID] {
|
||||||
|
if lc.channelState.IsZeroConf() {
|
||||||
|
return fn.Some(lc.channelState.ZeroConfRealScid())
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.None[lnwire.ShortChannelID]()
|
||||||
|
}
|
||||||
|
158
peer/brontide.go
158
peer/brontide.go
@@ -3,6 +3,7 @@ package peer
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
"container/list"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -46,6 +47,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/msgmux"
|
"github.com/lightningnetwork/lnd/msgmux"
|
||||||
"github.com/lightningnetwork/lnd/netann"
|
"github.com/lightningnetwork/lnd/netann"
|
||||||
"github.com/lightningnetwork/lnd/pool"
|
"github.com/lightningnetwork/lnd/pool"
|
||||||
|
"github.com/lightningnetwork/lnd/protofsm"
|
||||||
"github.com/lightningnetwork/lnd/queue"
|
"github.com/lightningnetwork/lnd/queue"
|
||||||
"github.com/lightningnetwork/lnd/subscribe"
|
"github.com/lightningnetwork/lnd/subscribe"
|
||||||
"github.com/lightningnetwork/lnd/ticker"
|
"github.com/lightningnetwork/lnd/ticker"
|
||||||
@@ -913,6 +915,16 @@ func (p *Brontide) taprootShutdownAllowed() bool {
|
|||||||
p.LocalFeatures().HasFeature(lnwire.ShutdownAnySegwitOptional)
|
p.LocalFeatures().HasFeature(lnwire.ShutdownAnySegwitOptional)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rbfCoopCloseAllowed returns true if both parties have negotiated the new RBF
|
||||||
|
// coop close feature.
|
||||||
|
func (p *Brontide) rbfCoopCloseAllowed() bool {
|
||||||
|
return p.RemoteFeatures().HasFeature(
|
||||||
|
lnwire.RbfCoopCloseOptionalStaging,
|
||||||
|
) && p.LocalFeatures().HasFeature(
|
||||||
|
lnwire.RbfCoopCloseOptionalStaging,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// QuitSignal is a method that should return a channel which will be sent upon
|
// QuitSignal is a method that should return a channel which will be sent upon
|
||||||
// or closed once the backing peer exits. This allows callers using the
|
// or closed once the backing peer exits. This allows callers using the
|
||||||
// interface to cancel any processing in the event the backing implementation
|
// interface to cancel any processing in the event the backing implementation
|
||||||
@@ -3314,7 +3326,8 @@ func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initNegotiateChanCloser initializes the channel closer for a channel that is
|
// initNegotiateChanCloser initializes the channel closer for a channel that is
|
||||||
// using the original "negotiation" based protocol.
|
// using the original "negotiation" based protocol. This path is used when
|
||||||
|
// we're the one initiating the channel close.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): can make a MsgEndpoint for existing handling logic to
|
// TODO(roasbeef): can make a MsgEndpoint for existing handling logic to
|
||||||
// further abstract.
|
// further abstract.
|
||||||
@@ -3392,6 +3405,149 @@ func (p *Brontide) initNegotiateChanCloser(req *htlcswitch.ChanClose,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func chooseAddr(addr lnwire.DeliveryAddress) fn.Option[lnwire.DeliveryAddress] {
|
||||||
|
if len(addr) == 0 {
|
||||||
|
return fn.None[lnwire.DeliveryAddress]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.Some(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initRbfChanCloser initializes the channel closer for a channel that
|
||||||
|
// is using the new RBF based co-op close protocol. This only creates the chan
|
||||||
|
// closer, but doesn't attempt to trigger any manual state transitions.
|
||||||
|
func (p *Brontide) initRbfChanCloser(
|
||||||
|
channel *lnwallet.LightningChannel) (*chancloser.RbfChanCloser, error) {
|
||||||
|
|
||||||
|
chanID := lnwire.NewChanIDFromOutPoint(channel.ChannelPoint())
|
||||||
|
|
||||||
|
link := p.fetchLinkFromKeyAndCid(chanID)
|
||||||
|
|
||||||
|
_, startingHeight, err := p.cfg.ChainIO.GetBestBlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot obtain best block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultFeePerKw, err := p.cfg.FeeEstimator.EstimateFeePerKW(
|
||||||
|
p.cfg.CoopCloseTargetConfs,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to estimate fee: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
thawHeight, err := channel.AbsoluteThawHeight()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to get thaw height: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerPub := *p.IdentityKey()
|
||||||
|
|
||||||
|
msgMapper := chancloser.NewRbfMsgMapper(uint32(startingHeight), chanID, peerPub)
|
||||||
|
|
||||||
|
initialState := chancloser.ChannelActive{}
|
||||||
|
|
||||||
|
scid := channel.ZeroConfRealScid().UnwrapOr(
|
||||||
|
channel.ShortChanID(),
|
||||||
|
)
|
||||||
|
|
||||||
|
env := chancloser.Environment{
|
||||||
|
ChainParams: p.cfg.Wallet.Cfg.NetParams,
|
||||||
|
ChanPeer: peerPub,
|
||||||
|
ChanPoint: channel.ChannelPoint(),
|
||||||
|
ChanID: chanID,
|
||||||
|
Scid: scid,
|
||||||
|
ChanType: channel.ChanType(),
|
||||||
|
DefaultFeeRate: defaultFeePerKw.FeePerVByte(),
|
||||||
|
ThawHeight: fn.Some(thawHeight),
|
||||||
|
RemoteUpfrontShutdown: chooseAddr(
|
||||||
|
channel.RemoteUpfrontShutdownScript(),
|
||||||
|
),
|
||||||
|
LocalUpfrontShutdown: chooseAddr(
|
||||||
|
channel.LocalUpfrontShutdownScript(),
|
||||||
|
),
|
||||||
|
NewDeliveryScript: func() (lnwire.DeliveryAddress, error) {
|
||||||
|
return p.genDeliveryScript()
|
||||||
|
},
|
||||||
|
FeeEstimator: &chancloser.SimpleCoopFeeEstimator{},
|
||||||
|
ChanObserver: newChanObserver(
|
||||||
|
channel, link, p.cfg.ChanStatusMgr,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
spendEvent := protofsm.RegisterSpend[chancloser.ProtocolEvent]{
|
||||||
|
OutPoint: channel.ChannelPoint(),
|
||||||
|
PkScript: channel.FundingTxOut().PkScript,
|
||||||
|
HeightHint: scid.BlockHeight,
|
||||||
|
PostSpendEvent: fn.Some[chancloser.RbfSpendMapper](
|
||||||
|
chancloser.SpendMapper,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): edge case here to re-enable a channel before both
|
||||||
|
// shutdown sent?
|
||||||
|
|
||||||
|
daemonAdapters := NewLndDaemonAdapters(LndAdapterCfg{
|
||||||
|
MsgSender: newPeerMsgSender(peerPub, p),
|
||||||
|
TxBroadcaster: p.cfg.Wallet,
|
||||||
|
ChainNotifier: p.cfg.ChainNotifier,
|
||||||
|
})
|
||||||
|
|
||||||
|
protoCfg := chancloser.RbfChanCloserCfg{
|
||||||
|
Daemon: daemonAdapters,
|
||||||
|
InitialState: &initialState,
|
||||||
|
Env: &env,
|
||||||
|
InitEvent: fn.Some[protofsm.DaemonEvent](&spendEvent),
|
||||||
|
MsgMapper: fn.Some[protofsm.MsgMapper[chancloser.ProtocolEvent]]( //nolint:ll
|
||||||
|
msgMapper,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
chanCloser := protofsm.NewStateMachine(protoCfg)
|
||||||
|
|
||||||
|
// Now that we've created the channel state machine, we'll register for
|
||||||
|
// a hook to be sent once the channel has been flushed.
|
||||||
|
link.OnFlushedOnce(func() {
|
||||||
|
commitState := channel.StateSnapshot()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
chanCloser.SendEvent(ctx, &chancloser.ChannelFlushed{
|
||||||
|
ShutdownBalances: chancloser.ShutdownBalances{
|
||||||
|
LocalBalance: commitState.LocalBalance,
|
||||||
|
RemoteBalance: commitState.RemoteBalance,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return &chanCloser, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initAndStartRbfChanCloser initializes the channel closer for a channel that
|
||||||
|
// is using the new RBF based co-op close protocol. This is called when we're
|
||||||
|
// the one that's initiating the cooperative channel close.
|
||||||
|
func (p *Brontide) initAndStartRbfChanCloser(req *htlcswitch.ChanClose,
|
||||||
|
channel *lnwallet.LightningChannel) error {
|
||||||
|
|
||||||
|
// TODO(roasbeef): either kick off sent shutdown or shutdown recv'd
|
||||||
|
// * can also send the NoDangling in as new event?
|
||||||
|
|
||||||
|
// First, we'll create the channel closer for this channel.
|
||||||
|
chanCloser, err := p.initRbfChanCloser(channel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the chan closer created, we'll now kick off the co-op close
|
||||||
|
// process by instructing it to send a shutdown message to the remote
|
||||||
|
// party.
|
||||||
|
ctx := context.Background()
|
||||||
|
chanCloser.SendEvent(ctx, &chancloser.SendShutdown{
|
||||||
|
IdealFeeRate: req.TargetFeePerKw.FeePerVByte(),
|
||||||
|
DeliveryAddr: chooseAddr(req.DeliveryScript),
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleLocalCloseReq kicks-off the workflow to execute a cooperative or
|
// handleLocalCloseReq kicks-off the workflow to execute a cooperative or
|
||||||
// forced unilateral closure of the channel initiated by a local subsystem.
|
// forced unilateral closure of the channel initiated by a local subsystem.
|
||||||
func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
|
func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
|
||||||
|
Reference in New Issue
Block a user