chancloser: extract ProcessCloseMsg ClosingSigned handling logic into dedicated method

This commit is contained in:
Keagan McClelland committed 2024-01-22 12:19:58 -08:00
1 parent 6fef9ea2d7
commit e77d3adef2
1 file changed
+70 -63
+70 -63
View File
@@ -616,33 +616,24 @@ func (c *ChanCloser) ReceiveShutdown(
} }
} }
// ProcessCloseMsg attempts to process the next message in the closing series. // ReceiveClosingSigned is a method that should be called whenever we receive a
// This method will update the state accordingly and return two primary values: // ClosingSigned message from the wire. It may or may not return a ClosingSigned
// the next set of messages to be sent, and a bool indicating if the fee // of our own to send back to the remote.
// negotiation process has completed. If the second value is true, then this func (c *ChanCloser) ReceiveClosingSigned(
// means the ChanCloser can be garbage collected. msg lnwire.ClosingSigned,
func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message, ) (fn.Option[lnwire.ClosingSigned], error) {
bool, error) {
noClosing := fn.None[lnwire.ClosingSigned]()
switch c.state { switch c.state {
// If we're receiving a message while we're in the fee negotiation phase,
// then this indicates the remote party is responding to a close signed
// 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 ClosingSigned
// message, otherwise an invalid state transition was attempted.
closeSignedMsg, ok := msg.(*lnwire.ClosingSigned)
if !ok {
return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+
"instead have %v", spew.Sdump(msg))
}
// If this is a taproot channel, then it MUST have a partial // If this is a taproot channel, then it MUST have a partial
// signature set at this point. // signature set at this point.
isTaproot := c.cfg.Channel.ChanType().IsTaproot() isTaproot := c.cfg.Channel.ChanType().IsTaproot()
if isTaproot && closeSignedMsg.PartialSig == nil { if isTaproot && msg.PartialSig == nil {
return nil, false, fmt.Errorf("partial sig not set " + return noClosing,
"for taproot chan") fmt.Errorf("partial sig not set " +
"for taproot chan")
} }
isInitiator := c.cfg.Channel.IsInitiator() isInitiator := c.cfg.Channel.IsInitiator()
@@ -651,7 +642,7 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
// during the negotiations. If it doesn't match any of our // during the negotiations. If it doesn't match any of our
// prior offers, then we'll attempt to ratchet the fee closer // prior offers, then we'll attempt to ratchet the fee closer
// to our ideal fee. // to our ideal fee.
remoteProposedFee := closeSignedMsg.FeeSatoshis remoteProposedFee := msg.FeeSatoshis
_, feeMatchesOffer := c.priorFeeOffers[remoteProposedFee] _, feeMatchesOffer := c.priorFeeOffers[remoteProposedFee]
switch { switch {
@@ -671,7 +662,7 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
// to Alice the final signature. // to Alice the final signature.
_, err := c.proposeCloseSigned(remoteProposedFee) _, err := c.proposeCloseSigned(remoteProposedFee)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("unable to sign "+ return noClosing, fmt.Errorf("unable to sign "+
"new co op close offer: %w", err) "new co op close offer: %w", err)
} }
@@ -679,10 +670,11 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
// signature for a taproot channel, then we'll ensure that the // signature for a taproot channel, then we'll ensure that the
// fee rate matches up exactly. // fee rate matches up exactly.
case isTaproot && isInitiator && !feeMatchesOffer: case isTaproot && isInitiator && !feeMatchesOffer:
return nil, false, fmt.Errorf("fee rate for "+ return noClosing,
"taproot channels was not accepted: "+ fmt.Errorf("fee rate for "+
"sent %v, got %v", "taproot channels was not accepted: "+
c.idealFeeSat, remoteProposedFee) "sent %v, got %v",
c.idealFeeSat, remoteProposedFee)
// If we're the initiator of the taproot channel, and we had // If we're the initiator of the taproot channel, and we had
// our fee echo'd back, then it's all good, and we can proceed // our fee echo'd back, then it's all good, and we can proceed
@@ -697,37 +689,41 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
// We'll now attempt to ratchet towards a fee deemed // We'll now attempt to ratchet towards a fee deemed
// acceptable by both parties, factoring in our ideal // acceptable by both parties, factoring in our ideal
// fee rate, and the last proposed fee by both sides. // fee rate, and the last proposed fee by both sides.
feeProposal := calcCompromiseFee(c.chanPoint, c.idealFeeSat, proposal := calcCompromiseFee(
c.lastFeeProposal, remoteProposedFee, c.chanPoint, c.idealFeeSat, c.lastFeeProposal,
remoteProposedFee,
) )
if c.cfg.Channel.IsInitiator() && feeProposal > c.maxFee { if c.cfg.Channel.IsInitiator() && proposal > c.maxFee {
return nil, false, fmt.Errorf("%w: %v > %v", return noClosing, fmt.Errorf(
ErrProposalExceedsMaxFee, feeProposal, "%w: %v > %v",
c.maxFee) ErrProposalExceedsMaxFee,
proposal, c.maxFee)
} }
// With our new fee proposal calculated, we'll craft a // With our new fee proposal calculated, we'll craft a
// new close signed signature to send to the other // new close signed signature to send to the other
// party so we can continue the fee negotiation // party so we can continue the fee negotiation
// process. // process.
closeSigned, err := c.proposeCloseSigned(feeProposal) closeSigned, err := c.proposeCloseSigned(proposal)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("unable to sign "+ return noClosing, fmt.Errorf("unable to sign "+
"new co op close offer: %w", err) "new co op close offer: %w", err)
} }
// If the compromise fee doesn't match what the peer // If the compromise fee doesn't match what the peer
// proposed, then we'll return this latest close signed // proposed, then we'll return this latest close signed
// message so we can continue negotiation. // message so we can continue negotiation.
if feeProposal != remoteProposedFee { if proposal != remoteProposedFee {
chancloserLog.Debugf("ChannelPoint(%v): close tx fee "+ chancloserLog.Debugf("ChannelPoint(%v): close "+
"disagreement, continuing negotiation", c.chanPoint) "tx fee disagreement, continuing "+
return []lnwire.Message{closeSigned}, false, nil "negotiation", c.chanPoint)
return fn.Some(*closeSigned), nil
} }
} }
chancloserLog.Infof("ChannelPoint(%v) fee of %v accepted, ending "+ chancloserLog.Infof("ChannelPoint(%v) fee of %v accepted, "+
"negotiation", c.chanPoint, remoteProposedFee) "ending 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 craft the final closing transaction so we // transaction! We'll craft the final closing transaction so we
@@ -740,21 +736,22 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
matchingSig := c.priorFeeOffers[remoteProposedFee] matchingSig := c.priorFeeOffers[remoteProposedFee]
if c.cfg.Channel.ChanType().IsTaproot() { if c.cfg.Channel.ChanType().IsTaproot() {
muSession := c.cfg.MusigSession muSession := c.cfg.MusigSession
localSig, remoteSig, closeOpts, err = muSession.CombineClosingOpts( //nolint:lll localSig, remoteSig, closeOpts, err =
*matchingSig.PartialSig, muSession.CombineClosingOpts(
*closeSignedMsg.PartialSig, *matchingSig.PartialSig,
) *msg.PartialSig,
)
if err != nil { if err != nil {
return nil, false, err return noClosing, err
} }
} else { } else {
localSig, err = matchingSig.Signature.ToSignature() localSig, err = matchingSig.Signature.ToSignature()
if err != nil { if err != nil {
return nil, false, err return noClosing, err
} }
remoteSig, err = closeSignedMsg.Signature.ToSignature() remoteSig, err = msg.Signature.ToSignature()
if err != nil { if err != nil {
return nil, false, err return noClosing, err
} }
} }
@@ -763,7 +760,7 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
c.remoteDeliveryScript, remoteProposedFee, closeOpts..., c.remoteDeliveryScript, remoteProposedFee, closeOpts...,
) )
if err != nil { if err != nil {
return nil, false, err return noClosing, err
} }
c.closingTx = closeTx c.closingTx = closeTx
@@ -774,7 +771,7 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
closeTx, c.locallyInitiated, closeTx, c.locallyInitiated,
) )
if err != nil { if err != nil {
return nil, false, err return noClosing, err
} }
// With the closing transaction crafted, we'll now broadcast it // With the closing transaction crafted, we'll now broadcast it
@@ -792,7 +789,7 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
) )
if err := c.cfg.BroadcastTx(closeTx, closeLabel); err != nil { if err := c.cfg.BroadcastTx(closeTx, closeLabel); err != nil {
return nil, false, err return noClosing, err
} }
// Finally, we'll transition to the closeFinished state, and // Finally, we'll transition to the closeFinished state, and
@@ -802,22 +799,32 @@ func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
// negotiation. // negotiation.
c.state = closeFinished c.state = closeFinished
matchingOffer := c.priorFeeOffers[remoteProposedFee] matchingOffer := c.priorFeeOffers[remoteProposedFee]
return []lnwire.Message{matchingOffer}, true, nil
return fn.Some(*matchingOffer), nil
// If we received 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 message // should only be the remote party echoing the last ClosingSigned
// that we agreed on. // message that we agreed on.
case closeFinished: case closeFinished:
if _, ok := msg.(*lnwire.ClosingSigned); !ok {
return nil, false, fmt.Errorf("expected lnwire.ClosingSigned, "+
"instead have %v", spew.Sdump(msg))
}
// There's no more to do as both sides should have already broadcast // There's no more to do as both sides should have already
// the closing transaction at this state. // broadcast the closing transaction at this state.
return nil, true, nil return noClosing, nil
// Otherwise, we're in an unknown state, and can't proceed. default:
return noClosing, ErrInvalidState
}
}
// ProcessCloseMsg attempts to process the next message in the closing series.
// 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
// negotiation process has completed. If the second value is true, then this
// means the ChanCloser can be garbage collected.
func (c *ChanCloser) ProcessCloseMsg(msg lnwire.Message) ([]lnwire.Message,
bool, error) {
switch c.state {
default: default:
return nil, false, ErrInvalidState return nil, false, ErrInvalidState
} }