Merge pull request #4365 from Crypt-iQ/chancloser_pkg_0609
lnwallet/chancloser: cooperative channel closure pkg
This commit is contained in:
commit
5eb798a23e
@ -1,4 +1,4 @@
|
|||||||
package lnd
|
package chancloser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -14,24 +14,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrChanAlreadyClosing is returned when a channel shutdown is
|
// ErrChanAlreadyClosing is returned when a channel shutdown is attempted
|
||||||
// attempted more than once.
|
// more than once.
|
||||||
ErrChanAlreadyClosing = fmt.Errorf("channel shutdown already initiated")
|
ErrChanAlreadyClosing = fmt.Errorf("channel shutdown already initiated")
|
||||||
|
|
||||||
// ErrChanCloseNotFinished is returned when a caller attempts to access
|
// ErrChanCloseNotFinished is returned when a caller attempts to access
|
||||||
// a field or function that is contingent on the channel closure
|
// a field or function that is contingent on the channel closure negotiation
|
||||||
// negotiation already being completed.
|
// already being completed.
|
||||||
ErrChanCloseNotFinished = fmt.Errorf("close negotiation not finished")
|
ErrChanCloseNotFinished = fmt.Errorf("close negotiation not finished")
|
||||||
|
|
||||||
// ErrInvalidState is returned when the closing state machine receives
|
// ErrInvalidState is returned when the closing state machine receives a
|
||||||
// a message while it is in an unknown state.
|
// message while it is in an unknown state.
|
||||||
ErrInvalidState = fmt.Errorf("invalid state")
|
ErrInvalidState = fmt.Errorf("invalid state")
|
||||||
|
|
||||||
// errUpfrontShutdownScriptMismatch is returned when a peer or end user
|
// ErrUpfrontShutdownScriptMismatch is returned when a peer or end user
|
||||||
// provides a script to cooperatively close out to which does not match
|
// provides a cooperative close script which does not match the upfront
|
||||||
// the upfront shutdown script previously set for that party.
|
// shutdown script previously set for that party.
|
||||||
errUpfrontShutdownScriptMismatch = fmt.Errorf("shutdown " +
|
ErrUpfrontShutdownScriptMismatch = fmt.Errorf("shutdown script does not " +
|
||||||
"script does not match upfront shutdown script")
|
"match upfront shutdown script")
|
||||||
)
|
)
|
||||||
|
|
||||||
// closeState represents all the possible states the channel closer state
|
// closeState represents all the possible states the channel closer state
|
||||||
@ -43,70 +43,69 @@ type closeState uint8
|
|||||||
const (
|
const (
|
||||||
// closeIdle is the initial starting state. In this state, the state
|
// closeIdle is the initial starting state. In this state, the state
|
||||||
// machine has been instantiated, but no state transitions have been
|
// machine has been instantiated, but no state transitions have been
|
||||||
// attempted. If a state machine receives a message while in this
|
// attempted. If a state machine receives a message while in this state,
|
||||||
// state, then it is the responder to an initiated cooperative channel
|
// then it is the responder to an initiated cooperative channel closure.
|
||||||
// closure.
|
|
||||||
closeIdle closeState = iota
|
closeIdle closeState = iota
|
||||||
|
|
||||||
// closeShutdownInitiated is the state that's transitioned to once the
|
// closeShutdownInitiated is the state that's transitioned to once the
|
||||||
// initiator of a closing workflow sends the shutdown message. At this
|
// initiator of a closing workflow sends the shutdown message. At this
|
||||||
// point, they're waiting for the remote party to respond with their
|
// point, they're waiting for the remote party to respond with their own
|
||||||
// own shutdown message. After which, they'll both enter the fee
|
// shutdown message. After which, they'll both enter the fee negotiation
|
||||||
// negotiation phase.
|
// phase.
|
||||||
closeShutdownInitiated
|
closeShutdownInitiated
|
||||||
|
|
||||||
// closeFeeNegotiation is the third, and most persistent state. Both
|
// closeFeeNegotiation is the third, and most persistent state. Both
|
||||||
// parties enter this state after they've sent and received a shutdown
|
// parties enter this state after they've sent and received a shutdown
|
||||||
// message. During this phase, both sides will send monotonically
|
// message. During this phase, both sides will send monotonically
|
||||||
// increasing fee requests until one side accepts the last fee rate
|
// increasing fee requests until one side accepts the last fee rate offered
|
||||||
// offered by the other party. In this case, the party will broadcast
|
// by the other party. In this case, the party will broadcast the closing
|
||||||
// the closing transaction, and send the accepted fee to the remote
|
// transaction, and send the accepted fee to the remote party. This then
|
||||||
// party. This then causes a shift into the closeFinished state.
|
// causes a shift into the closeFinished state.
|
||||||
closeFeeNegotiation
|
closeFeeNegotiation
|
||||||
|
|
||||||
// closeFinished is the final state of the state machine. In this,
|
// closeFinished is the final state of the state machine. In this state, a
|
||||||
// state a side has accepted a fee offer and has broadcast the valid
|
// side has accepted a fee offer and has broadcast the valid closing
|
||||||
// closing transaction to the network. During this phase, the closing
|
// transaction to the network. During this phase, the closing transaction
|
||||||
// transaction becomes available for examination.
|
// becomes available for examination.
|
||||||
closeFinished
|
closeFinished
|
||||||
)
|
)
|
||||||
|
|
||||||
// chanCloseCfg holds all the items that a channelCloser requires to carry out
|
// ChanCloseCfg holds all the items that a ChanCloser requires to carry out its
|
||||||
// its duties.
|
// duties.
|
||||||
type chanCloseCfg struct {
|
type ChanCloseCfg struct {
|
||||||
// channel is the channel that should be closed.
|
// Channel is the channel that should be closed.
|
||||||
channel *lnwallet.LightningChannel
|
Channel *lnwallet.LightningChannel
|
||||||
|
|
||||||
// unregisterChannel is a function closure that allows the
|
// UnregisterChannel is a function closure that allows the ChanCloser to
|
||||||
// channelCloser to re-register a channel. Once this has been done, no
|
// unregister a channel. Once this has been done, no further HTLC's should
|
||||||
// further HTLC's should be routed through the channel.
|
// be routed through the channel.
|
||||||
unregisterChannel func(lnwire.ChannelID)
|
UnregisterChannel func(lnwire.ChannelID)
|
||||||
|
|
||||||
// broadcastTx broadcasts the passed transaction to the network.
|
// BroadcastTx broadcasts the passed transaction to the network.
|
||||||
broadcastTx func(*wire.MsgTx, string) error
|
BroadcastTx func(*wire.MsgTx, string) error
|
||||||
|
|
||||||
// disableChannel disables a channel, resulting in it not being able to
|
// DisableChannel disables a channel, resulting in it not being able to
|
||||||
// forward payments.
|
// forward payments.
|
||||||
disableChannel func(wire.OutPoint) error
|
DisableChannel func(wire.OutPoint) error
|
||||||
|
|
||||||
// disconnect will disconnect from the remote peer in this close.
|
// Disconnect will disconnect from the remote peer in this close.
|
||||||
disconnect func() error
|
Disconnect func() error
|
||||||
|
|
||||||
// quit is a channel that should be sent upon in the occasion the state
|
// Quit is a channel that should be sent upon in the occasion the state
|
||||||
// machine should cease all progress and shutdown.
|
// machine should cease all progress and shutdown.
|
||||||
quit chan struct{}
|
Quit chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// channelCloser is a state machine that handles the cooperative channel
|
// ChanCloser is a state machine that handles the cooperative channel closure
|
||||||
// closure procedure. This includes shutting down a channel, marking it
|
// procedure. This includes shutting down a channel, marking it ineligible for
|
||||||
// ineligible for routing HTLC's, negotiating fees with the remote party, and
|
// routing HTLC's, negotiating fees with the remote party, and finally
|
||||||
// finally broadcasting the fully signed closure transaction to the network.
|
// broadcasting the fully signed closure transaction to the network.
|
||||||
type channelCloser struct {
|
type ChanCloser struct {
|
||||||
// state is the current state of the state machine.
|
// state is the current state of the state machine.
|
||||||
state closeState
|
state closeState
|
||||||
|
|
||||||
// cfg holds the configuration for this channelCloser instance.
|
// cfg holds the configuration for this ChanCloser instance.
|
||||||
cfg chanCloseCfg
|
cfg ChanCloseCfg
|
||||||
|
|
||||||
// chanPoint is the full channel point of the target channel.
|
// chanPoint is the full channel point of the target channel.
|
||||||
chanPoint wire.OutPoint
|
chanPoint wire.OutPoint
|
||||||
@ -117,83 +116,81 @@ type channelCloser struct {
|
|||||||
// negotiationHeight is the height that the fee negotiation begun at.
|
// negotiationHeight is the height that the fee negotiation begun at.
|
||||||
negotiationHeight uint32
|
negotiationHeight uint32
|
||||||
|
|
||||||
// closingTx is the final, fully signed closing transaction. This will
|
// closingTx is the final, fully signed closing transaction. This will only
|
||||||
// only be populated once the state machine shifts to the closeFinished
|
// be populated once the state machine shifts to the closeFinished state.
|
||||||
// state.
|
|
||||||
closingTx *wire.MsgTx
|
closingTx *wire.MsgTx
|
||||||
|
|
||||||
// idealFeeSat is the ideal fee that the state machine should initially
|
// idealFeeSat is the ideal fee that the state machine should initially
|
||||||
// offer when starting negotiation. This will be used as a baseline.
|
// offer when starting negotiation. This will be used as a baseline.
|
||||||
idealFeeSat btcutil.Amount
|
idealFeeSat btcutil.Amount
|
||||||
|
|
||||||
// lastFeeProposal is the last fee that we proposed to the remote
|
// lastFeeProposal is the last fee that we proposed to the remote party.
|
||||||
// party. We'll use this as a pivot point to rachet our next offer up,
|
// We'll use this as a pivot point to ratchet our next offer up, down, or
|
||||||
// or down, or simply accept the remote party's prior offer.
|
// simply accept the remote party's prior offer.
|
||||||
lastFeeProposal btcutil.Amount
|
lastFeeProposal btcutil.Amount
|
||||||
|
|
||||||
// priorFeeOffers is a map that keeps track of all the proposed fees
|
// priorFeeOffers is a map that keeps track of all the proposed fees that
|
||||||
// that we've offered during the fee negotiation. We use this map to
|
// we've offered during the fee negotiation. We use this map to cut the
|
||||||
// cut the negotiation early if the remote party ever sends an offer
|
// negotiation early if the remote party ever sends an offer that we've
|
||||||
// that we've sent in the past. Once negotiation terminates, we can
|
// sent in the past. Once negotiation terminates, we can extract the prior
|
||||||
// extract the prior signature of our accepted offer from this map.
|
// signature of our accepted offer from this map.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): need to ensure if they broadcast w/ any of our prior
|
// TODO(roasbeef): need to ensure if they broadcast w/ any of our prior
|
||||||
// sigs, we are aware of
|
// sigs, we are aware of
|
||||||
priorFeeOffers map[btcutil.Amount]*lnwire.ClosingSigned
|
priorFeeOffers map[btcutil.Amount]*lnwire.ClosingSigned
|
||||||
|
|
||||||
// closeReq is the initial closing request. This will only be populated
|
// closeReq is the initial closing request. This will only be populated if
|
||||||
// if we're the initiator of this closing negotiation.
|
// we're the initiator of this closing negotiation.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): abstract away
|
// TODO(roasbeef): abstract away
|
||||||
closeReq *htlcswitch.ChanClose
|
closeReq *htlcswitch.ChanClose
|
||||||
|
|
||||||
// localDeliveryScript is the script that we'll send our settled
|
// localDeliveryScript is the script that we'll send our settled channel
|
||||||
// channel funds to.
|
// funds to.
|
||||||
localDeliveryScript []byte
|
localDeliveryScript []byte
|
||||||
|
|
||||||
// remoteDeliveryScript is the script that we'll send the remote
|
// remoteDeliveryScript is the script that we'll send the remote party's
|
||||||
// party's settled channel funds to.
|
// settled channel funds to.
|
||||||
remoteDeliveryScript []byte
|
remoteDeliveryScript []byte
|
||||||
|
|
||||||
// locallyInitiated is true if we initiated the channel close.
|
// locallyInitiated is true if we initiated the channel close.
|
||||||
locallyInitiated bool
|
locallyInitiated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newChannelCloser creates a new instance of the channel closure given the
|
// NewChanCloser creates a new instance of the channel closure given the passed
|
||||||
// passed configuration, and delivery+fee preference. The final argument should
|
// configuration, and delivery+fee preference. The final argument should only
|
||||||
// only be populated iff, we're the initiator of this closing request.
|
// be populated iff, we're the initiator of this closing request.
|
||||||
func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte,
|
func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte,
|
||||||
idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32,
|
idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32,
|
||||||
closeReq *htlcswitch.ChanClose, locallyInitiated bool) *channelCloser {
|
closeReq *htlcswitch.ChanClose, locallyInitiated bool) *ChanCloser {
|
||||||
|
|
||||||
// Given the target fee-per-kw, we'll compute what our ideal _total_
|
// Given the target fee-per-kw, we'll compute what our ideal _total_ fee
|
||||||
// fee will be starting at for this fee negotiation.
|
// will be starting at for this fee negotiation.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): should factor in minimal commit
|
// TODO(roasbeef): should factor in minimal commit
|
||||||
idealFeeSat := cfg.channel.CalcFee(idealFeePerKw)
|
idealFeeSat := cfg.Channel.CalcFee(idealFeePerKw)
|
||||||
|
|
||||||
// If this fee is greater than the fee currently present within the
|
// If this fee is greater than the fee currently present within the
|
||||||
// commitment transaction, then we'll clamp it down to be within the
|
// commitment transaction, then we'll clamp it down to be within the proper
|
||||||
// proper range.
|
// range.
|
||||||
//
|
//
|
||||||
// TODO(roasbeef): clamp fee func?
|
// TODO(roasbeef): clamp fee func?
|
||||||
channelCommitFee := cfg.channel.StateSnapshot().CommitFee
|
channelCommitFee := cfg.Channel.StateSnapshot().CommitFee
|
||||||
if idealFeeSat > channelCommitFee {
|
if idealFeeSat > channelCommitFee {
|
||||||
peerLog.Infof("Ideal starting fee of %v is greater than "+
|
chancloserLog.Infof("Ideal starting fee of %v is greater than commit "+
|
||||||
"commit fee of %v, clamping", int64(idealFeeSat),
|
"fee of %v, clamping", int64(idealFeeSat), int64(channelCommitFee))
|
||||||
int64(channelCommitFee))
|
|
||||||
|
|
||||||
idealFeeSat = channelCommitFee
|
idealFeeSat = channelCommitFee
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("Ideal fee for closure of ChannelPoint(%v) is: %v sat",
|
chancloserLog.Infof("Ideal fee for closure of ChannelPoint(%v) is: %v sat",
|
||||||
cfg.channel.ChannelPoint(), int64(idealFeeSat))
|
cfg.Channel.ChannelPoint(), int64(idealFeeSat))
|
||||||
|
|
||||||
cid := lnwire.NewChanIDFromOutPoint(cfg.channel.ChannelPoint())
|
cid := lnwire.NewChanIDFromOutPoint(cfg.Channel.ChannelPoint())
|
||||||
return &channelCloser{
|
return &ChanCloser{
|
||||||
closeReq: closeReq,
|
closeReq: closeReq,
|
||||||
state: closeIdle,
|
state: closeIdle,
|
||||||
chanPoint: *cfg.channel.ChannelPoint(),
|
chanPoint: *cfg.Channel.ChannelPoint(),
|
||||||
cid: cid,
|
cid: cid,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
negotiationHeight: negotiationHeight,
|
negotiationHeight: negotiationHeight,
|
||||||
@ -206,37 +203,38 @@ func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte,
|
|||||||
|
|
||||||
// initChanShutdown begins the shutdown process by un-registering the channel,
|
// initChanShutdown begins the shutdown process by un-registering the channel,
|
||||||
// and creating a valid shutdown message to our target delivery address.
|
// and creating a valid shutdown message to our target delivery address.
|
||||||
func (c *channelCloser) initChanShutdown() (*lnwire.Shutdown, error) {
|
func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) {
|
||||||
// With both items constructed we'll now send the shutdown message for
|
// With both items constructed we'll now send the shutdown message for this
|
||||||
// this particular channel, advertising a shutdown request to our
|
// particular channel, advertising a shutdown request to our desired
|
||||||
// desired closing script.
|
// closing script.
|
||||||
shutdown := lnwire.NewShutdown(c.cid, c.localDeliveryScript)
|
shutdown := lnwire.NewShutdown(c.cid, c.localDeliveryScript)
|
||||||
|
|
||||||
// TODO(roasbeef): err if channel has htlc's?
|
// TODO(roasbeef): err if channel has htlc's?
|
||||||
|
|
||||||
// Before closing, we'll attempt to send a disable update for the
|
// Before closing, we'll attempt to send a disable update for the channel.
|
||||||
// channel. We do so before closing the channel as otherwise the current
|
// We do so before closing the channel as otherwise the current edge policy
|
||||||
// edge policy won't be retrievable from the graph.
|
// won't be retrievable from the graph.
|
||||||
if err := c.cfg.disableChannel(c.chanPoint); err != nil {
|
if err := c.cfg.DisableChannel(c.chanPoint); err != nil {
|
||||||
peerLog.Warnf("Unable to disable channel %v on "+
|
chancloserLog.Warnf("Unable to disable channel %v on close: %v",
|
||||||
"close: %v", c.chanPoint, err)
|
c.chanPoint, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before returning the shutdown message, we'll unregister the channel
|
// Before returning the shutdown message, we'll unregister the channel to
|
||||||
// to ensure that it isn't seen as usable within the system.
|
// ensure that it isn't seen as usable within the system.
|
||||||
c.cfg.unregisterChannel(c.cid)
|
c.cfg.UnregisterChannel(c.cid)
|
||||||
|
|
||||||
// Before continuing, mark the channel as cooperatively closed with a
|
// Before continuing, mark the channel as cooperatively closed with a nil
|
||||||
// nil txn. Even though we haven't negotiated the final txn, this
|
// txn. Even though we haven't negotiated the final txn, this guarantees
|
||||||
// guarantees that our listchannels rpc will be externally consistent,
|
// that our listchannels rpc will be externally consistent, and reflect
|
||||||
// and reflect that the channel is being shutdown by the time the
|
// that the channel is being shutdown by the time the closing request
|
||||||
// closing request returns.
|
// returns.
|
||||||
err := c.cfg.channel.MarkCoopBroadcasted(nil, c.locallyInitiated)
|
err := c.cfg.Channel.MarkCoopBroadcasted(nil, c.locallyInitiated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): sending shutdown message", c.chanPoint)
|
chancloserLog.Infof("ChannelPoint(%v): sending shutdown message",
|
||||||
|
c.chanPoint)
|
||||||
|
|
||||||
return shutdown, nil
|
return shutdown, nil
|
||||||
}
|
}
|
||||||
@ -245,14 +243,14 @@ func (c *channelCloser) initChanShutdown() (*lnwire.Shutdown, error) {
|
|||||||
// cooperative channel closure. This message returns the shutdown message to
|
// cooperative channel closure. This message returns the shutdown message to
|
||||||
// send to the remote party. Upon completion, we enter the
|
// send to the remote party. Upon completion, we enter the
|
||||||
// closeShutdownInitiated phase as we await a response.
|
// closeShutdownInitiated phase as we await a response.
|
||||||
func (c *channelCloser) ShutdownChan() (*lnwire.Shutdown, error) {
|
func (c *ChanCloser) ShutdownChan() (*lnwire.Shutdown, error) {
|
||||||
// If we attempt to shutdown the channel for the first time, and we're
|
// If we attempt to shutdown the channel for the first time, and we're not
|
||||||
// not in the closeIdle state, then the caller made an error.
|
// in the closeIdle state, then the caller made an error.
|
||||||
if c.state != closeIdle {
|
if c.state != closeIdle {
|
||||||
return nil, ErrChanAlreadyClosing
|
return nil, ErrChanAlreadyClosing
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): initiating shutdown of", c.chanPoint)
|
chancloserLog.Infof("ChannelPoint(%v): initiating shutdown", c.chanPoint)
|
||||||
|
|
||||||
shutdownMsg, err := c.initChanShutdown()
|
shutdownMsg, err := c.initChanShutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -260,12 +258,12 @@ func (c *channelCloser) ShutdownChan() (*lnwire.Shutdown, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// With the opening steps complete, we'll transition into the
|
// With the opening steps complete, we'll transition into the
|
||||||
// closeShutdownInitiated state. In this state, we'll wait until the
|
// closeShutdownInitiated state. In this state, we'll wait until the other
|
||||||
// other party sends their version of the shutdown message.
|
// party sends their version of the shutdown message.
|
||||||
c.state = closeShutdownInitiated
|
c.state = closeShutdownInitiated
|
||||||
|
|
||||||
// Finally, we'll return the shutdown message to the caller so it can
|
// Finally, we'll return the shutdown message to the caller so it can send
|
||||||
// send it to the remote peer.
|
// it to the remote peer.
|
||||||
return shutdownMsg, nil
|
return shutdownMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,8 +271,8 @@ func (c *channelCloser) ShutdownChan() (*lnwire.Shutdown, error) {
|
|||||||
//
|
//
|
||||||
// NOTE: This transaction is only available if the state machine is in the
|
// NOTE: This transaction is only available if the state machine is in the
|
||||||
// closeFinished state.
|
// closeFinished state.
|
||||||
func (c *channelCloser) ClosingTx() (*wire.MsgTx, error) {
|
func (c *ChanCloser) ClosingTx() (*wire.MsgTx, error) {
|
||||||
// If the state machine hasn't finished closing the channel then we'll
|
// If the state machine hasn't finished closing the channel, then we'll
|
||||||
// return an error as we haven't yet computed the closing tx.
|
// return an error as we haven't yet computed the closing tx.
|
||||||
if c.state != closeFinished {
|
if c.state != closeFinished {
|
||||||
return nil, ErrChanCloseNotFinished
|
return nil, ErrChanCloseNotFinished
|
||||||
@ -288,19 +286,28 @@ func (c *channelCloser) ClosingTx() (*wire.MsgTx, error) {
|
|||||||
//
|
//
|
||||||
// NOTE: This will only return a non-nil pointer if we were the initiator of
|
// NOTE: This will only return a non-nil pointer if we were the initiator of
|
||||||
// the cooperative closure workflow.
|
// the cooperative closure workflow.
|
||||||
func (c *channelCloser) CloseRequest() *htlcswitch.ChanClose {
|
func (c *ChanCloser) CloseRequest() *htlcswitch.ChanClose {
|
||||||
return c.closeReq
|
return c.closeReq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Channel returns the channel stored in the config.
|
||||||
|
func (c *ChanCloser) Channel() *lnwallet.LightningChannel {
|
||||||
|
return c.cfg.Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
// NegotiationHeight returns the negotiation height.
|
||||||
|
func (c *ChanCloser) NegotiationHeight() uint32 {
|
||||||
|
return c.negotiationHeight
|
||||||
|
}
|
||||||
|
|
||||||
// maybeMatchScript attempts to match the script provided in our peer's
|
// maybeMatchScript attempts to match the script provided in our peer's
|
||||||
// shutdown message with the upfront shutdown script we have on record.
|
// shutdown message with the upfront shutdown script we have on record. If no
|
||||||
// If no upfront shutdown script was set, we do not need to enforce option
|
// upfront shutdown script was set, we do not need to enforce option upfront
|
||||||
// upfront shutdown, so the function returns early. If an upfront script is
|
// shutdown, so the function returns early. If an upfront script is set, we
|
||||||
// set, we check whether it matches the script provided by our peer. If they
|
// check whether it matches the script provided by our peer. If they do not
|
||||||
// do not match, we use the disconnect function provided to disconnect from
|
// match, we use the disconnect function provided to disconnect from the peer.
|
||||||
// the peer.
|
func maybeMatchScript(disconnect func() error, upfrontScript,
|
||||||
func maybeMatchScript(disconnect func() error,
|
peerScript lnwire.DeliveryAddress) error {
|
||||||
upfrontScript, peerScript lnwire.DeliveryAddress) error {
|
|
||||||
|
|
||||||
// If no upfront shutdown script was set, return early because we do not
|
// If no upfront shutdown script was set, return early because we do not
|
||||||
// need to enforce closure to a specific script.
|
// need to enforce closure to a specific script.
|
||||||
@ -311,7 +318,7 @@ func maybeMatchScript(disconnect func() error,
|
|||||||
// If an upfront shutdown script was provided, disconnect from the peer, as
|
// If an upfront shutdown script was provided, disconnect from the peer, as
|
||||||
// per BOLT 2, and return an error.
|
// per BOLT 2, and return an error.
|
||||||
if !bytes.Equal(upfrontScript, peerScript) {
|
if !bytes.Equal(upfrontScript, peerScript) {
|
||||||
peerLog.Warnf("peer's script: %x does not match upfront "+
|
chancloserLog.Warnf("peer's script: %x does not match upfront "+
|
||||||
"shutdown script: %x", peerScript, upfrontScript)
|
"shutdown script: %x", peerScript, upfrontScript)
|
||||||
|
|
||||||
// Disconnect from the peer because they have violated option upfront
|
// Disconnect from the peer because they have violated option upfront
|
||||||
@ -320,7 +327,7 @@ func maybeMatchScript(disconnect func() error,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return errUpfrontShutdownScriptMismatch
|
return ErrUpfrontShutdownScriptMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -330,42 +337,44 @@ func maybeMatchScript(disconnect func() error,
|
|||||||
// This method will update the state accordingly and return two primary values:
|
// This method will update the state accordingly and return two primary values:
|
||||||
// the next set of messages to be sent, and a bool indicating if the fee
|
// the next set of messages to be sent, and a bool indicating if the fee
|
||||||
// negotiation process has completed. If the second value is true, then this
|
// negotiation process has completed. If the second value is true, then this
|
||||||
// means the channelCloser can be garbage collected.
|
// means the ChanCloser can be garbage collected.
|
||||||
func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, bool, error) {
|
func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
|
||||||
|
bool, error) {
|
||||||
|
|
||||||
switch c.state {
|
switch c.state {
|
||||||
|
|
||||||
// If we're in the close idle state, and we're receiving a channel
|
// If we're in the close idle state, and we're receiving a channel closure
|
||||||
// closure related message, then this indicates that we're on the
|
// related message, then this indicates that we're on the receiving side of
|
||||||
// receiving side of an initiated channel closure.
|
// an initiated channel closure.
|
||||||
case closeIdle:
|
case closeIdle:
|
||||||
// First, we'll assert that we have a channel shutdown message,
|
// First, we'll assert that we have a channel shutdown message,
|
||||||
// otherwise, this is an attempted invalid state transition.
|
// as otherwise, this is an attempted invalid state transition.
|
||||||
shutDownMsg, ok := msg.(*lnwire.Shutdown)
|
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, fmt.Errorf("expected lnwire.Shutdown, "+
|
return nil, false, fmt.Errorf("expected lnwire.Shutdown, instead "+
|
||||||
"instead have %v", spew.Sdump(msg))
|
"have %v", spew.Sdump(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// As we're the responder to this shutdown (the other party
|
// As we're the responder to this shutdown (the other party wants to
|
||||||
// wants to close), we'll check if this is a frozen channel or
|
// close), we'll check if this is a frozen channel or not. If the
|
||||||
// not. If the channel is frozen as we were also the initiator
|
// channel is frozen and we were not also the initiator of the channel
|
||||||
// of the channel opening, then we'll deny their close attempt.
|
// opening, then we'll deny their close attempt.
|
||||||
chanInitiator := c.cfg.channel.IsInitiator()
|
chanInitiator := c.cfg.Channel.IsInitiator()
|
||||||
if !chanInitiator && c.cfg.channel.State().ChanType.IsFrozen() &&
|
chanState := c.cfg.Channel.State()
|
||||||
c.negotiationHeight < c.cfg.channel.State().ThawHeight {
|
if !chanInitiator && chanState.ChanType.IsFrozen() &&
|
||||||
|
c.negotiationHeight < chanState.ThawHeight {
|
||||||
|
|
||||||
return nil, false, fmt.Errorf("initiator attempting "+
|
return nil, false, fmt.Errorf("initiator attempting to co-op "+
|
||||||
"to co-op close frozen ChannelPoint(%v) "+
|
"close frozen ChannelPoint(%v) (current_height=%v, "+
|
||||||
"(current_height=%v, thaw_height=%v)",
|
"thaw_height=%v)", c.chanPoint, c.negotiationHeight,
|
||||||
c.chanPoint, c.negotiationHeight,
|
chanState.ThawHeight)
|
||||||
c.cfg.channel.State().ThawHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the remote node opened the channel with option upfront shutdown
|
// If the remote node opened the channel with option upfront shutdown
|
||||||
// script, check that the script they provided matches.
|
// script, check that the script they provided matches.
|
||||||
if err := maybeMatchScript(
|
if err := maybeMatchScript(
|
||||||
c.cfg.disconnect, c.cfg.channel.RemoteUpfrontShutdownScript(),
|
c.cfg.Disconnect, c.cfg.Channel.RemoteUpfrontShutdownScript(),
|
||||||
shutDownMsg.Address,
|
shutdownMsg.Address,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@ -373,29 +382,29 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
|
|||||||
// Once we have checked that the other party has not violated option
|
// Once we have checked that the other party has not violated option
|
||||||
// upfront shutdown we set their preference for delivery address. We'll
|
// upfront shutdown we set their preference for delivery address. We'll
|
||||||
// use this when we craft the closure transaction.
|
// use this when we craft the closure transaction.
|
||||||
c.remoteDeliveryScript = shutDownMsg.Address
|
c.remoteDeliveryScript = shutdownMsg.Address
|
||||||
|
|
||||||
// We'll generate a shutdown message of our own to send across
|
// We'll generate a shutdown message of our own to send across the
|
||||||
// the wire.
|
// wire.
|
||||||
localShutdown, err := c.initChanShutdown()
|
localShutdown, err := c.initChanShutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): Responding to shutdown",
|
chancloserLog.Infof("ChannelPoint(%v): responding to shutdown",
|
||||||
c.chanPoint)
|
c.chanPoint)
|
||||||
|
|
||||||
msgsToSend := make([]lnwire.Message, 0, 2)
|
msgsToSend := make([]lnwire.Message, 0, 2)
|
||||||
msgsToSend = append(msgsToSend, localShutdown)
|
msgsToSend = append(msgsToSend, localShutdown)
|
||||||
|
|
||||||
// After the other party receives this message, we'll actually
|
// After the other party receives this message, we'll actually start
|
||||||
// start the final stage of the closure process: fee
|
// the final stage of the closure process: fee negotiation. So we'll
|
||||||
// negotiation. So we'll update our internal state to reflect
|
// update our internal state to reflect this, so we can handle the next
|
||||||
// this, so we can handle the next message sent.
|
// message sent.
|
||||||
c.state = closeFeeNegotiation
|
c.state = closeFeeNegotiation
|
||||||
|
|
||||||
// We'll also craft our initial close proposal in order to keep
|
// We'll also craft our initial close proposal in order to keep the
|
||||||
// the negotiation moving, but only if we're the negotiator.
|
// negotiation moving, but only if we're the negotiator.
|
||||||
if chanInitiator {
|
if chanInitiator {
|
||||||
closeSigned, err := c.proposeCloseSigned(c.idealFeeSat)
|
closeSigned, err := c.proposeCloseSigned(c.idealFeeSat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -404,48 +413,46 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
|
|||||||
msgsToSend = append(msgsToSend, closeSigned)
|
msgsToSend = append(msgsToSend, closeSigned)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll return both sets of messages to send to the remote
|
// We'll return both sets of messages to send to the remote party to
|
||||||
// party to kick off the fee negotiation process.
|
// kick off the fee negotiation process.
|
||||||
return msgsToSend, false, nil
|
return msgsToSend, false, nil
|
||||||
|
|
||||||
// If we just initiated a channel shutdown, and we receive a new
|
// If we just initiated a channel shutdown, and we receive a new message,
|
||||||
// message, then this indicates the other party is ready to shutdown as
|
// then this indicates the other party is ready to shutdown as well. In
|
||||||
// well. In this state we'll send our first signature.
|
// this state we'll send our first signature.
|
||||||
case closeShutdownInitiated:
|
case closeShutdownInitiated:
|
||||||
// First, we'll assert that we have a channel shutdown message,
|
// First, we'll assert that we have a channel shutdown message.
|
||||||
// otherwise, this is an attempted invalid state transition.
|
// Otherwise, this is an attempted invalid state transition.
|
||||||
shutDownMsg, ok := msg.(*lnwire.Shutdown)
|
shutdownMsg, ok := msg.(*lnwire.Shutdown)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, fmt.Errorf("expected lnwire.Shutdown, "+
|
return nil, false, fmt.Errorf("expected lnwire.Shutdown, instead "+
|
||||||
"instead have %v", spew.Sdump(msg))
|
"have %v", spew.Sdump(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the remote node opened the channel with option upfront shutdown
|
// If the remote node opened the channel with option upfront shutdown
|
||||||
// script, check that the script they provided matches.
|
// script, check that the script they provided matches.
|
||||||
if err := maybeMatchScript(
|
if err := maybeMatchScript(c.cfg.Disconnect,
|
||||||
c.cfg.disconnect, c.cfg.channel.RemoteUpfrontShutdownScript(),
|
c.cfg.Channel.RemoteUpfrontShutdownScript(), shutdownMsg.Address,
|
||||||
shutDownMsg.Address,
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we know this is a valid shutdown message and address, we'll
|
// Now that we know this is a valid shutdown message and address, we'll
|
||||||
// record their preferred delivery closing script.
|
// record their preferred delivery closing script.
|
||||||
c.remoteDeliveryScript = shutDownMsg.Address
|
c.remoteDeliveryScript = shutdownMsg.Address
|
||||||
|
|
||||||
// At this point, we can now start the fee negotiation state,
|
// At this point, we can now start the fee negotiation state, by
|
||||||
// by constructing and sending our initial signature for what
|
// constructing and sending our initial signature for what we think the
|
||||||
// we think the closing transaction should look like.
|
// closing transaction should look like.
|
||||||
c.state = closeFeeNegotiation
|
c.state = closeFeeNegotiation
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): shutdown response received, "+
|
chancloserLog.Infof("ChannelPoint(%v): shutdown response received, "+
|
||||||
"entering fee negotiation", c.chanPoint)
|
"entering fee negotiation", c.chanPoint)
|
||||||
|
|
||||||
// Starting with our ideal fee rate, we'll create an initial
|
// Starting with our ideal fee rate, we'll create an initial closing
|
||||||
// closing proposal, but only if we're the initiator, as
|
// proposal, but only if we're the initiator, as otherwise, the other
|
||||||
// otherwise, the other party will send their first proposal
|
// party will send their initial proposal first.
|
||||||
// first.
|
if c.cfg.Channel.IsInitiator() {
|
||||||
if c.cfg.channel.IsInitiator() {
|
|
||||||
closeSigned, err := c.proposeCloseSigned(c.idealFeeSat)
|
closeSigned, err := c.proposeCloseSigned(c.idealFeeSat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
@ -456,58 +463,54 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
|
|||||||
|
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
|
|
||||||
// If we're receiving a message while we're in the fee negotiation
|
// If we're receiving a message while we're in the fee negotiation phase,
|
||||||
// phase, then this indicates the remote party is responding to a closed
|
// then this indicates the remote party is responding to a close signed
|
||||||
// signed message we sent, or kicking off the process with their own.
|
// message we sent, or kicking off the process with their own.
|
||||||
case closeFeeNegotiation:
|
case closeFeeNegotiation:
|
||||||
// First, we'll assert that we're actually getting a
|
// First, we'll assert that we're actually getting a ClosingSigned
|
||||||
// CloseSigned message, otherwise an invalid state transition
|
// message, otherwise an invalid state transition was attempted.
|
||||||
// was attempted.
|
|
||||||
closeSignedMsg, ok := msg.(*lnwire.ClosingSigned)
|
closeSignedMsg, ok := msg.(*lnwire.ClosingSigned)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+
|
return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+
|
||||||
"instead have %v", spew.Sdump(msg))
|
"instead have %v", spew.Sdump(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll compare the proposed total fee, to what we've proposed
|
// We'll compare the proposed total fee, to what we've proposed during
|
||||||
// during the negotiations, if it doesn't match any of our
|
// the negotiations. If it doesn't match any of our prior offers, then
|
||||||
// prior offers, then we'll attempt to rachet the fee closer to
|
// we'll attempt to ratchet the fee closer to
|
||||||
remoteProposedFee := closeSignedMsg.FeeSatoshis
|
remoteProposedFee := closeSignedMsg.FeeSatoshis
|
||||||
if _, ok := c.priorFeeOffers[remoteProposedFee]; !ok {
|
if _, ok := c.priorFeeOffers[remoteProposedFee]; !ok {
|
||||||
// We'll now attempt to rachet towards a fee deemed
|
// We'll now attempt to ratchet towards a fee deemed acceptable by
|
||||||
// acceptable by both parties, factoring in our ideal
|
// both parties, factoring in our ideal fee rate, and the last
|
||||||
// fee rate, and the last proposed fee by both sides.
|
// proposed fee by both sides.
|
||||||
feeProposal := calcCompromiseFee(c.chanPoint,
|
feeProposal := calcCompromiseFee(c.chanPoint, c.idealFeeSat,
|
||||||
c.idealFeeSat, c.lastFeeProposal,
|
c.lastFeeProposal, remoteProposedFee,
|
||||||
remoteProposedFee,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// With our new fee proposal calculated, we'll craft a
|
// With our new fee proposal calculated, we'll craft a new close
|
||||||
// new close signed signature to send to the other
|
// signed signature to send to the other party so we can continue
|
||||||
// party so we can continue the fee negotiation
|
// the fee negotiation process.
|
||||||
// process.
|
|
||||||
closeSigned, err := c.proposeCloseSigned(feeProposal)
|
closeSigned, err := c.proposeCloseSigned(feeProposal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the compromise fee doesn't match what the peer
|
// If the compromise fee doesn't match what the peer proposed, then
|
||||||
// proposed, then we'll return this latest close signed
|
// we'll return this latest close signed message so we can continue
|
||||||
// message so we continue negotiation.
|
// negotiation.
|
||||||
if feeProposal != remoteProposedFee {
|
if feeProposal != remoteProposedFee {
|
||||||
peerLog.Debugf("ChannelPoint(%v): close tx "+
|
chancloserLog.Debugf("ChannelPoint(%v): close tx fee "+
|
||||||
"fee disagreement, continuing negotiation",
|
"disagreement, continuing negotiation", c.chanPoint)
|
||||||
c.chanPoint)
|
|
||||||
return []lnwire.Message{closeSigned}, false, nil
|
return []lnwire.Message{closeSigned}, false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v) fee of %v accepted, ending "+
|
chancloserLog.Infof("ChannelPoint(%v) fee of %v accepted, ending "+
|
||||||
"negotiation", c.chanPoint, remoteProposedFee)
|
"negotiation", c.chanPoint, remoteProposedFee)
|
||||||
|
|
||||||
// Otherwise, we've agreed on a fee for the closing
|
// Otherwise, we've agreed on a fee for the closing transaction! We'll
|
||||||
// transaction! We'll craft the final closing transaction so
|
// craft the final closing transaction so we can broadcast it to the
|
||||||
// we can broadcast it to the network.
|
// network.
|
||||||
matchingSig := c.priorFeeOffers[remoteProposedFee].Signature
|
matchingSig := c.priorFeeOffers[remoteProposedFee].Signature
|
||||||
localSig, err := matchingSig.ToSignature()
|
localSig, err := matchingSig.ToSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -519,57 +522,52 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
closeTx, _, err := c.cfg.channel.CompleteCooperativeClose(
|
closeTx, _, err := c.cfg.Channel.CompleteCooperativeClose(
|
||||||
localSig, remoteSig, c.localDeliveryScript,
|
localSig, remoteSig, c.localDeliveryScript, c.remoteDeliveryScript,
|
||||||
c.remoteDeliveryScript, remoteProposedFee,
|
remoteProposedFee,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
c.closingTx = closeTx
|
c.closingTx = closeTx
|
||||||
|
|
||||||
// Before publishing the closing tx, we persist it to the
|
// Before publishing the closing tx, we persist it to the database,
|
||||||
// database, such that it can be republished if something goes
|
// such that it can be republished if something goes wrong.
|
||||||
// wrong.
|
err = c.cfg.Channel.MarkCoopBroadcasted(closeTx, c.locallyInitiated)
|
||||||
err = c.cfg.channel.MarkCoopBroadcasted(
|
|
||||||
closeTx, c.locallyInitiated,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the closing transaction crafted, we'll now broadcast it
|
// With the closing transaction crafted, we'll now broadcast it to the
|
||||||
// to the network.
|
// network.
|
||||||
peerLog.Infof("Broadcasting cooperative close tx: %v",
|
chancloserLog.Infof("Broadcasting cooperative close tx: %v",
|
||||||
newLogClosure(func() string {
|
newLogClosure(func() string {
|
||||||
return spew.Sdump(closeTx)
|
return spew.Sdump(closeTx)
|
||||||
}))
|
}),
|
||||||
err = c.cfg.broadcastTx(closeTx, "")
|
)
|
||||||
if err != nil {
|
if err := c.cfg.BroadcastTx(closeTx, ""); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we'll transition to the closeFinished state, and
|
// Finally, we'll transition to the closeFinished state, and also
|
||||||
// also return the final close signed message we sent.
|
// return the final close signed message we sent. Additionally, we
|
||||||
// Additionally, we return true for the second argument to
|
// return true for the second argument to indicate we're finished with
|
||||||
// indicate we're finished with the channel closing
|
// the channel closing negotiation.
|
||||||
// negotiation.
|
|
||||||
c.state = closeFinished
|
c.state = closeFinished
|
||||||
matchingOffer := c.priorFeeOffers[remoteProposedFee]
|
matchingOffer := c.priorFeeOffers[remoteProposedFee]
|
||||||
return []lnwire.Message{matchingOffer}, true, nil
|
return []lnwire.Message{matchingOffer}, true, nil
|
||||||
|
|
||||||
// If we receive a message while in the closeFinished state, then this
|
// If we received a message while in the closeFinished state, then this
|
||||||
// should only be the remote party echoing the last ClosingSigned
|
// should only be the remote party echoing the last ClosingSigned message
|
||||||
// message that we agreed on.
|
// that we agreed on.
|
||||||
case closeFinished:
|
case closeFinished:
|
||||||
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
|
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
|
||||||
return nil, false, fmt.Errorf("expected "+
|
return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+
|
||||||
"lnwire.ClosingSigned, instead have %v",
|
"instead have %v", spew.Sdump(msg))
|
||||||
spew.Sdump(msg))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's no more to do as both sides should have already
|
// There's no more to do as both sides should have already broadcast
|
||||||
// broadcast the closing transaction at this state.
|
// the closing transaction at this state.
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
|
|
||||||
// Otherwise, we're in an unknown state, and can't proceed.
|
// Otherwise, we're in an unknown state, and can't proceed.
|
||||||
@ -579,31 +577,30 @@ func (c *channelCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// proposeCloseSigned attempts to propose a new signature for the closing
|
// proposeCloseSigned attempts to propose a new signature for the closing
|
||||||
// transaction for a channel based on the prior fee negotiations and our
|
// transaction for a channel based on the prior fee negotiations and our current
|
||||||
// current compromise fee.
|
// compromise fee.
|
||||||
func (c *channelCloser) proposeCloseSigned(fee btcutil.Amount) (*lnwire.ClosingSigned, error) {
|
func (c *ChanCloser) proposeCloseSigned(fee btcutil.Amount) (*lnwire.ClosingSigned, error) {
|
||||||
rawSig, _, _, err := c.cfg.channel.CreateCloseProposal(
|
rawSig, _, _, err := c.cfg.Channel.CreateCloseProposal(
|
||||||
fee, c.localDeliveryScript, c.remoteDeliveryScript,
|
fee, c.localDeliveryScript, c.remoteDeliveryScript,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll note our last signature and proposed fee so when the remote
|
// We'll note our last signature and proposed fee so when the remote party
|
||||||
// party responds we'll be able to decide if we've agreed on fees or
|
// responds we'll be able to decide if we've agreed on fees or not.
|
||||||
// not.
|
|
||||||
c.lastFeeProposal = fee
|
c.lastFeeProposal = fee
|
||||||
parsedSig, err := lnwire.NewSigFromSignature(rawSig)
|
parsedSig, err := lnwire.NewSigFromSignature(rawSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): proposing fee of %v sat to close "+
|
chancloserLog.Infof("ChannelPoint(%v): proposing fee of %v sat to close "+
|
||||||
"chan", c.chanPoint, int64(fee))
|
"chan", c.chanPoint, int64(fee))
|
||||||
|
|
||||||
// We'll assemble a ClosingSigned message using this information and
|
// We'll assemble a ClosingSigned message using this information and return
|
||||||
// return it to the caller so we can kick off the final stage of the
|
// it to the caller so we can kick off the final stage of the channel
|
||||||
// channel closure project.
|
// closure process.
|
||||||
closeSignedMsg := lnwire.NewClosingSigned(c.cid, fee, parsedSig)
|
closeSignedMsg := lnwire.NewClosingSigned(c.cid, fee, parsedSig)
|
||||||
|
|
||||||
// We'll also save this close signed, in the case that the remote party
|
// We'll also save this close signed, in the case that the remote party
|
||||||
@ -618,26 +615,25 @@ func (c *channelCloser) proposeCloseSigned(fee btcutil.Amount) (*lnwire.ClosingS
|
|||||||
// compromise and to ensure that the fee negotiation has a stopping point. We
|
// compromise and to ensure that the fee negotiation has a stopping point. We
|
||||||
// consider their fee acceptable if it's within 30% of our fee.
|
// consider their fee acceptable if it's within 30% of our fee.
|
||||||
func feeInAcceptableRange(localFee, remoteFee btcutil.Amount) bool {
|
func feeInAcceptableRange(localFee, remoteFee btcutil.Amount) bool {
|
||||||
// If our offer is lower than theirs, then we'll accept their
|
// If our offer is lower than theirs, then we'll accept their offer if it's
|
||||||
// offer if it's no more than 30% *greater* than our current
|
// no more than 30% *greater* than our current offer.
|
||||||
// offer.
|
|
||||||
if localFee < remoteFee {
|
if localFee < remoteFee {
|
||||||
acceptableRange := localFee + ((localFee * 3) / 10)
|
acceptableRange := localFee + ((localFee * 3) / 10)
|
||||||
return remoteFee <= acceptableRange
|
return remoteFee <= acceptableRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// If our offer is greater than theirs, then we'll accept their offer
|
// If our offer is greater than theirs, then we'll accept their offer if
|
||||||
// if it's no more than 30% *less* than our current offer.
|
// it's no more than 30% *less* than our current offer.
|
||||||
acceptableRange := localFee - ((localFee * 3) / 10)
|
acceptableRange := localFee - ((localFee * 3) / 10)
|
||||||
return remoteFee >= acceptableRange
|
return remoteFee >= acceptableRange
|
||||||
}
|
}
|
||||||
|
|
||||||
// rachetFee is our step function used to inch our fee closer to something that
|
// ratchetFee is our step function used to inch our fee closer to something
|
||||||
// both sides can agree on. If up is true, then we'll attempt to increase our
|
// that both sides can agree on. If up is true, then we'll attempt to increase
|
||||||
// offered fee. Otherwise, if up is false, then we'll attempt to decrease our
|
// our offered fee. Otherwise, if up is false, then we'll attempt to decrease
|
||||||
// offered fee.
|
// our offered fee.
|
||||||
func rachetFee(fee btcutil.Amount, up bool) btcutil.Amount {
|
func ratchetFee(fee btcutil.Amount, up bool) btcutil.Amount {
|
||||||
// If we need to rachet up, then we'll increase our fee by 10%.
|
// If we need to ratchet up, then we'll increase our fee by 10%.
|
||||||
if up {
|
if up {
|
||||||
return fee + ((fee * 1) / 10)
|
return fee + ((fee * 1) / 10)
|
||||||
}
|
}
|
||||||
@ -649,62 +645,58 @@ func rachetFee(fee btcutil.Amount, up bool) btcutil.Amount {
|
|||||||
// calcCompromiseFee performs the current fee negotiation algorithm, taking
|
// calcCompromiseFee performs the current fee negotiation algorithm, taking
|
||||||
// into consideration our ideal fee based on current fee environment, the fee
|
// into consideration our ideal fee based on current fee environment, the fee
|
||||||
// we last proposed (if any), and the fee proposed by the peer.
|
// we last proposed (if any), and the fee proposed by the peer.
|
||||||
func calcCompromiseFee(chanPoint wire.OutPoint,
|
func calcCompromiseFee(chanPoint wire.OutPoint, ourIdealFee, lastSentFee,
|
||||||
ourIdealFee, lastSentFee, remoteFee btcutil.Amount) btcutil.Amount {
|
remoteFee btcutil.Amount) btcutil.Amount {
|
||||||
|
|
||||||
// TODO(roasbeef): take in number of rounds as well?
|
// TODO(roasbeef): take in number of rounds as well?
|
||||||
|
|
||||||
peerLog.Infof("ChannelPoint(%v): computing fee compromise, ideal=%v, "+
|
chancloserLog.Infof("ChannelPoint(%v): computing fee compromise, ideal="+
|
||||||
"last_sent=%v, remote_offer=%v", chanPoint, int64(ourIdealFee),
|
"%v, last_sent=%v, remote_offer=%v", chanPoint, int64(ourIdealFee),
|
||||||
int64(lastSentFee), int64(remoteFee))
|
int64(lastSentFee), int64(remoteFee))
|
||||||
|
|
||||||
// Otherwise, we'll need to attempt to make a fee compromise if this is
|
// Otherwise, we'll need to attempt to make a fee compromise if this is the
|
||||||
// the second round, and neither side has agreed on fees.
|
// second round, and neither side has agreed on fees.
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
// If their proposed fee is identical to our ideal fee, then we'll go
|
// If their proposed fee is identical to our ideal fee, then we'll go with
|
||||||
// with that as we can short circuit the fee negotiation. Similarly, if
|
// that as we can short circuit the fee negotiation. Similarly, if we
|
||||||
// we haven't sent an offer yet, we'll default to our ideal fee.
|
// haven't sent an offer yet, we'll default to our ideal fee.
|
||||||
case ourIdealFee == remoteFee || lastSentFee == 0:
|
case ourIdealFee == remoteFee || lastSentFee == 0:
|
||||||
return ourIdealFee
|
return ourIdealFee
|
||||||
|
|
||||||
// If the last fee we sent, is equal to the fee the remote party is
|
// If the last fee we sent, is equal to the fee the remote party is
|
||||||
// offering, then we can simply return this fee as the negotiation is
|
// offering, then we can simply return this fee as the negotiation is over.
|
||||||
// over.
|
|
||||||
case remoteFee == lastSentFee:
|
case remoteFee == lastSentFee:
|
||||||
return lastSentFee
|
return lastSentFee
|
||||||
|
|
||||||
// If the fee the remote party is offering is less than the last one we
|
// If the fee the remote party is offering is less than the last one we
|
||||||
// sent, then we'll need to rachet down in order to move our offer
|
// sent, then we'll need to ratchet down in order to move our offer closer
|
||||||
// closer to theirs.
|
// to theirs.
|
||||||
case remoteFee < lastSentFee:
|
case remoteFee < lastSentFee:
|
||||||
// If the fee is lower, but still acceptable, then we'll just
|
// If the fee is lower, but still acceptable, then we'll just return
|
||||||
// return this fee and end the negotiation.
|
// this fee and end the negotiation.
|
||||||
if feeInAcceptableRange(lastSentFee, remoteFee) {
|
if feeInAcceptableRange(lastSentFee, remoteFee) {
|
||||||
peerLog.Infof("ChannelPoint(%v): proposed remote fee "+
|
chancloserLog.Infof("ChannelPoint(%v): proposed remote fee is "+
|
||||||
"is close enough, capitulating", chanPoint)
|
"close enough, capitulating", chanPoint)
|
||||||
return remoteFee
|
return remoteFee
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we'll rachet the fee *down* using our current
|
// Otherwise, we'll ratchet the fee *down* using our current algorithm.
|
||||||
// algorithm.
|
return ratchetFee(lastSentFee, false)
|
||||||
return rachetFee(lastSentFee, false)
|
|
||||||
|
|
||||||
// If the fee the remote party is offering is greater than the last one
|
// If the fee the remote party is offering is greater than the last one we
|
||||||
// we sent, then we'll rachet up in order to ensure we terminate
|
// sent, then we'll ratchet up in order to ensure we terminate eventually.
|
||||||
// eventually.
|
|
||||||
case remoteFee > lastSentFee:
|
case remoteFee > lastSentFee:
|
||||||
// If the fee is greater, but still acceptable, then we'll just
|
// If the fee is greater, but still acceptable, then we'll just return
|
||||||
// return this fee in order to put an end to the negotiation.
|
// this fee in order to put an end to the negotiation.
|
||||||
if feeInAcceptableRange(lastSentFee, remoteFee) {
|
if feeInAcceptableRange(lastSentFee, remoteFee) {
|
||||||
peerLog.Infof("ChannelPoint(%v): proposed remote fee "+
|
chancloserLog.Infof("ChannelPoint(%v): proposed remote fee is "+
|
||||||
"is close enough, capitulating", chanPoint)
|
"close enough, capitulating", chanPoint)
|
||||||
return remoteFee
|
return remoteFee
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we'll rachet the fee up using our current
|
// Otherwise, we'll ratchet the fee up using our current algorithm.
|
||||||
// algorithm.
|
return ratchetFee(lastSentFee, true)
|
||||||
return rachetFee(lastSentFee, true)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO(roasbeef): fail if their fee isn't in expected range
|
// TODO(roasbeef): fail if their fee isn't in expected range
|
@ -1,4 +1,4 @@
|
|||||||
package lnd
|
package chancloser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -49,7 +49,7 @@ func TestMaybeMatchScript(t *testing.T) {
|
|||||||
name: "upfront shutdown set, script not ok",
|
name: "upfront shutdown set, script not ok",
|
||||||
shutdownScript: addr1,
|
shutdownScript: addr1,
|
||||||
upfrontScript: addr2,
|
upfrontScript: addr2,
|
||||||
expectedErr: errUpfrontShutdownScriptMismatch,
|
expectedErr: ErrUpfrontShutdownScriptMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nil shutdown and empty upfront",
|
name: "nil shutdown and empty upfront",
|
41
lnwallet/chancloser/log.go
Normal file
41
lnwallet/chancloser/log.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package chancloser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/btcsuite/btclog"
|
||||||
|
"github.com/lightningnetwork/lnd/build"
|
||||||
|
)
|
||||||
|
|
||||||
|
// chancloserLog is a logger that is initialized with the btclog.Disabled
|
||||||
|
// logger.
|
||||||
|
var chancloserLog btclog.Logger
|
||||||
|
|
||||||
|
// The default amount of logging is none.
|
||||||
|
func init() {
|
||||||
|
UseLogger(build.NewSubLogger("CHCL", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableLog disables all logging output.
|
||||||
|
func DisableLog() {
|
||||||
|
UseLogger(btclog.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseLogger uses a specified Logger to output package logging info.
|
||||||
|
func UseLogger(logger btclog.Logger) {
|
||||||
|
chancloserLog = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// logClosure is used to provide a closure over expensive logging operations
|
||||||
|
// so they aren't performed when the logging level doesn't warrant it.
|
||||||
|
type logClosure func() string
|
||||||
|
|
||||||
|
// String invokes the underlying function and returns the result.
|
||||||
|
func (c logClosure) String() string {
|
||||||
|
return c()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLogClosure returns a new closure over a function that returns a string
|
||||||
|
// which itself provides a Stringer interface so that it can be used with the
|
||||||
|
// logging system.
|
||||||
|
func newLogClosure(c func() string) logClosure {
|
||||||
|
return logClosure(c)
|
||||||
|
}
|
2
log.go
2
log.go
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnrpc/verrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/verrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||||
"github.com/lightningnetwork/lnd/monitoring"
|
"github.com/lightningnetwork/lnd/monitoring"
|
||||||
"github.com/lightningnetwork/lnd/netann"
|
"github.com/lightningnetwork/lnd/netann"
|
||||||
@ -121,6 +122,7 @@ func SetupLoggers(root *build.RotatingLogWriter) {
|
|||||||
AddSubLogger(root, "WTCL", wtclient.UseLogger)
|
AddSubLogger(root, "WTCL", wtclient.UseLogger)
|
||||||
AddSubLogger(root, "PRNF", peernotifier.UseLogger)
|
AddSubLogger(root, "PRNF", peernotifier.UseLogger)
|
||||||
AddSubLogger(root, "CHFD", chanfunding.UseLogger)
|
AddSubLogger(root, "CHFD", chanfunding.UseLogger)
|
||||||
|
AddSubLogger(root, "CHCL", chancloser.UseLogger)
|
||||||
|
|
||||||
AddSubLogger(root, routing.Subsystem, routing.UseLogger, localchans.UseLogger)
|
AddSubLogger(root, routing.Subsystem, routing.UseLogger, localchans.UseLogger)
|
||||||
AddSubLogger(root, routerrpc.Subsystem, routerrpc.UseLogger)
|
AddSubLogger(root, routerrpc.Subsystem, routerrpc.UseLogger)
|
||||||
|
51
peer.go
51
peer.go
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/lnpeer"
|
"github.com/lightningnetwork/lnd/lnpeer"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/pool"
|
"github.com/lightningnetwork/lnd/pool"
|
||||||
"github.com/lightningnetwork/lnd/queue"
|
"github.com/lightningnetwork/lnd/queue"
|
||||||
@ -190,7 +191,7 @@ type peer struct {
|
|||||||
// messages are directed to one of these active state machines. Once
|
// messages are directed to one of these active state machines. Once
|
||||||
// the channel has been closed, the state machine will be delete from
|
// the channel has been closed, the state machine will be delete from
|
||||||
// the map.
|
// the map.
|
||||||
activeChanCloses map[lnwire.ChannelID]*channelCloser
|
activeChanCloses map[lnwire.ChannelID]*chancloser.ChanCloser
|
||||||
|
|
||||||
// localCloseChanReqs is a channel in which any local requests to close
|
// localCloseChanReqs is a channel in which any local requests to close
|
||||||
// a particular channel are sent over.
|
// a particular channel are sent over.
|
||||||
@ -302,7 +303,7 @@ func newPeer(cfg *Config, conn net.Conn, connReq *connmgr.ConnReq, server *serve
|
|||||||
|
|
||||||
activeMsgStreams: make(map[lnwire.ChannelID]*msgStream),
|
activeMsgStreams: make(map[lnwire.ChannelID]*msgStream),
|
||||||
|
|
||||||
activeChanCloses: make(map[lnwire.ChannelID]*channelCloser),
|
activeChanCloses: make(map[lnwire.ChannelID]*chancloser.ChanCloser),
|
||||||
localCloseChanReqs: make(chan *htlcswitch.ChanClose),
|
localCloseChanReqs: make(chan *htlcswitch.ChanClose),
|
||||||
linkFailures: make(chan linkFailureReport),
|
linkFailures: make(chan linkFailureReport),
|
||||||
chanCloseMsgs: make(chan *closeMsg),
|
chanCloseMsgs: make(chan *closeMsg),
|
||||||
@ -2114,7 +2115,7 @@ out:
|
|||||||
// As the negotiations failed, we'll reset the
|
// As the negotiations failed, we'll reset the
|
||||||
// channel state to ensure we act to on-chain
|
// channel state to ensure we act to on-chain
|
||||||
// events as normal.
|
// events as normal.
|
||||||
chanCloser.cfg.channel.ResetState()
|
chanCloser.Channel().ResetState()
|
||||||
|
|
||||||
if chanCloser.CloseRequest() != nil {
|
if chanCloser.CloseRequest() != nil {
|
||||||
chanCloser.CloseRequest().Err <- err
|
chanCloser.CloseRequest().Err <- err
|
||||||
@ -2232,7 +2233,9 @@ func (p *peer) reenableActiveChannels() {
|
|||||||
// for the target channel ID. If the channel isn't active an error is returned.
|
// for the target channel ID. If the channel isn't active an error is returned.
|
||||||
// Otherwise, either an existing state machine will be returned, or a new one
|
// Otherwise, either an existing state machine will be returned, or a new one
|
||||||
// will be created.
|
// will be created.
|
||||||
func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, error) {
|
func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (
|
||||||
|
*chancloser.ChanCloser, error) {
|
||||||
|
|
||||||
// First, we'll ensure that we actually know of the target channel. If
|
// First, we'll ensure that we actually know of the target channel. If
|
||||||
// not, we'll ignore this message.
|
// not, we'll ignore this message.
|
||||||
p.activeChanMtx.RLock()
|
p.activeChanMtx.RLock()
|
||||||
@ -2288,16 +2291,16 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
|
|||||||
return nil, fmt.Errorf("cannot obtain best block")
|
return nil, fmt.Errorf("cannot obtain best block")
|
||||||
}
|
}
|
||||||
|
|
||||||
chanCloser = newChannelCloser(
|
chanCloser = chancloser.NewChanCloser(
|
||||||
chanCloseCfg{
|
chancloser.ChanCloseCfg{
|
||||||
channel: channel,
|
Channel: channel,
|
||||||
unregisterChannel: p.server.htlcSwitch.RemoveLink,
|
UnregisterChannel: p.server.htlcSwitch.RemoveLink,
|
||||||
broadcastTx: p.server.cc.wallet.PublishTransaction,
|
BroadcastTx: p.server.cc.wallet.PublishTransaction,
|
||||||
disableChannel: p.server.chanStatusMgr.RequestDisable,
|
DisableChannel: p.server.chanStatusMgr.RequestDisable,
|
||||||
disconnect: func() error {
|
Disconnect: func() error {
|
||||||
return p.server.DisconnectPeer(p.IdentityKey())
|
return p.server.DisconnectPeer(p.IdentityKey())
|
||||||
},
|
},
|
||||||
quit: p.quit,
|
Quit: p.quit,
|
||||||
},
|
},
|
||||||
deliveryScript,
|
deliveryScript,
|
||||||
feePerKw,
|
feePerKw,
|
||||||
@ -2334,7 +2337,7 @@ func chooseDeliveryScript(upfront,
|
|||||||
// the upfront shutdown script (because closing out to a different script
|
// the upfront shutdown script (because closing out to a different script
|
||||||
// would violate upfront shutdown).
|
// would violate upfront shutdown).
|
||||||
if !bytes.Equal(upfront, requested) {
|
if !bytes.Equal(upfront, requested) {
|
||||||
return nil, errUpfrontShutdownScriptMismatch
|
return nil, chancloser.ErrUpfrontShutdownScriptMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// The user requested script matches the upfront shutdown script, so we
|
// The user requested script matches the upfront shutdown script, so we
|
||||||
@ -2404,16 +2407,16 @@ func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chanCloser := newChannelCloser(
|
chanCloser := chancloser.NewChanCloser(
|
||||||
chanCloseCfg{
|
chancloser.ChanCloseCfg{
|
||||||
channel: channel,
|
Channel: channel,
|
||||||
unregisterChannel: p.server.htlcSwitch.RemoveLink,
|
UnregisterChannel: p.server.htlcSwitch.RemoveLink,
|
||||||
broadcastTx: p.server.cc.wallet.PublishTransaction,
|
BroadcastTx: p.server.cc.wallet.PublishTransaction,
|
||||||
disableChannel: p.server.chanStatusMgr.RequestDisable,
|
DisableChannel: p.server.chanStatusMgr.RequestDisable,
|
||||||
disconnect: func() error {
|
Disconnect: func() error {
|
||||||
return p.server.DisconnectPeer(p.IdentityKey())
|
return p.server.DisconnectPeer(p.IdentityKey())
|
||||||
},
|
},
|
||||||
quit: p.quit,
|
Quit: p.quit,
|
||||||
},
|
},
|
||||||
deliveryScript,
|
deliveryScript,
|
||||||
req.TargetFeePerKw,
|
req.TargetFeePerKw,
|
||||||
@ -2520,11 +2523,11 @@ func (p *peer) handleLinkFailure(failure linkFailureReport) {
|
|||||||
// machine should be passed in. Once the transaction has been sufficiently
|
// machine should be passed in. Once the transaction has been sufficiently
|
||||||
// confirmed, the channel will be marked as fully closed within the database,
|
// confirmed, the channel will be marked as fully closed within the database,
|
||||||
// and any clients will be notified of updates to the closing state.
|
// and any clients will be notified of updates to the closing state.
|
||||||
func (p *peer) finalizeChanClosure(chanCloser *channelCloser) {
|
func (p *peer) finalizeChanClosure(chanCloser *chancloser.ChanCloser) {
|
||||||
closeReq := chanCloser.CloseRequest()
|
closeReq := chanCloser.CloseRequest()
|
||||||
|
|
||||||
// First, we'll clear all indexes related to the channel in question.
|
// First, we'll clear all indexes related to the channel in question.
|
||||||
chanPoint := chanCloser.cfg.channel.ChannelPoint()
|
chanPoint := chanCloser.Channel().ChannelPoint()
|
||||||
p.WipeChannel(chanPoint)
|
p.WipeChannel(chanPoint)
|
||||||
|
|
||||||
// Next, we'll launch a goroutine which will request to be notified by
|
// Next, we'll launch a goroutine which will request to be notified by
|
||||||
@ -2558,7 +2561,7 @@ func (p *peer) finalizeChanClosure(chanCloser *channelCloser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go waitForChanToClose(chanCloser.negotiationHeight, notifier, errChan,
|
go waitForChanToClose(chanCloser.NegotiationHeight(), notifier, errChan,
|
||||||
chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() {
|
chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() {
|
||||||
|
|
||||||
// Respond to the local subsystem which requested the
|
// Respond to the local subsystem which requested the
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -642,7 +643,7 @@ func TestChooseDeliveryScript(t *testing.T) {
|
|||||||
userScript: script1,
|
userScript: script1,
|
||||||
shutdownScript: script2,
|
shutdownScript: script2,
|
||||||
expectedScript: nil,
|
expectedScript: nil,
|
||||||
expectedError: errUpfrontShutdownScriptMismatch,
|
expectedError: chancloser.ErrUpfrontShutdownScriptMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Only upfront script",
|
name: "Only upfront script",
|
||||||
@ -733,7 +734,7 @@ func TestCustomShutdownScript(t *testing.T) {
|
|||||||
name: "Shutdown set, user script different",
|
name: "Shutdown set, user script different",
|
||||||
update: setShutdown,
|
update: setShutdown,
|
||||||
userCloseScript: []byte("different addr"),
|
userCloseScript: []byte("different addr"),
|
||||||
expectedError: errUpfrontShutdownScriptMismatch,
|
expectedError: chancloser.ErrUpfrontShutdownScriptMismatch,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/netann"
|
"github.com/lightningnetwork/lnd/netann"
|
||||||
"github.com/lightningnetwork/lnd/shachain"
|
"github.com/lightningnetwork/lnd/shachain"
|
||||||
@ -441,7 +442,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, publTx chan *wire.MsgTx,
|
|||||||
activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel),
|
activeChannels: make(map[lnwire.ChannelID]*lnwallet.LightningChannel),
|
||||||
newChannels: make(chan *newChannelMsg, 1),
|
newChannels: make(chan *newChannelMsg, 1),
|
||||||
|
|
||||||
activeChanCloses: make(map[lnwire.ChannelID]*channelCloser),
|
activeChanCloses: make(map[lnwire.ChannelID]*chancloser.ChanCloser),
|
||||||
localCloseChanReqs: make(chan *htlcswitch.ChanClose),
|
localCloseChanReqs: make(chan *htlcswitch.ChanClose),
|
||||||
chanCloseMsgs: make(chan *closeMsg),
|
chanCloseMsgs: make(chan *closeMsg),
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user