Skip to content

Commit

Permalink
multi: Use user provided close address for cooperative closes
Browse files Browse the repository at this point in the history
This commit is adapted from @Bluetegu's original
pull request #1462.

This commit reads an optional address to pay funds out to
from a user iniitiated close channel address. If the channel
already has a shutdown script set, the request will fail if
an address is provided. Otherwise, the cooperative close will
pay out to the address provided.
  • Loading branch information
carlaKC committed Dec 10, 2019
1 parent ef4d933 commit 94d3eb6
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 41 deletions.
8 changes: 4 additions & 4 deletions chancloser.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ var (
// a message while it is in an unknown state.
ErrInvalidState = fmt.Errorf("invalid state")

// errUpfrontShutdownScriptMismatch is returned when our peer sends us a
// shutdown message with a script that does not match the upfront shutdown
// script previously set.
errUpfrontShutdownScriptMismatch = fmt.Errorf("peer's shutdown " +
// errUpfrontShutdownScriptMismatch is returned when a peer or end user
// provides a script to cooperatively close out to which does not match
// the upfront shutdown script previously set for that party.
errUpfrontShutdownScriptMismatch = fmt.Errorf("shutdown " +
"script does not match upfront shutdown script")
)

Expand Down
17 changes: 11 additions & 6 deletions htlcswitch/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ type ChanClose struct {
// process for the cooperative closure transaction kicks off.
TargetFeePerKw chainfee.SatPerKWeight

// DeliveryScript is an optional delivery script to pay funds out to.
DeliveryScript lnwire.DeliveryAddress

// Updates is used by request creator to receive the notifications about
// execution of the close channel request.
Updates chan interface{}
Expand Down Expand Up @@ -1365,12 +1368,13 @@ func (s *Switch) teardownCircuit(pkt *htlcPacket) error {
}

// CloseLink creates and sends the close channel command to the target link
// directing the specified closure type. If the closure type if CloseRegular,
// then the last parameter should be the ideal fee-per-kw that will be used as
// a starting point for close negotiation.
func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType,
targetFeePerKw chainfee.SatPerKWeight) (chan interface{},
chan error) {
// directing the specified closure type. If the closure type is CloseRegular,
// targetFeePerKw parameter should be the ideal fee-per-kw that will be used as
// a starting point for close negotiation. The deliveryScript parameter is an
// optional parameter which sets a user specified script to close out to.
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
closeType ChannelCloseType, targetFeePerKw chainfee.SatPerKWeight,
deliveryScript lnwire.DeliveryAddress) (chan interface{}, chan error) {

// TODO(roasbeef) abstract out the close updates.
updateChan := make(chan interface{}, 2)
Expand All @@ -1381,6 +1385,7 @@ func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType,
ChanPoint: chanPoint,
Updates: updateChan,
TargetFeePerKw: targetFeePerKw,
DeliveryScript: deliveryScript,
Err: errChan,
}

Expand Down
56 changes: 50 additions & 6 deletions peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,37 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
return chanCloser, nil
}

// chooseDeliveryScript takes two optionally set shutdown scripts and returns
// a suitable script to close out to. This may be nil if neither script is
// set. If both scripts are set, this function will error if they do not match.
func chooseDeliveryScript(upfront,
requested lnwire.DeliveryAddress) (lnwire.DeliveryAddress, error) {

// If no upfront upfront shutdown script was provided, return the user
// requested address (which may be nil).
if len(upfront) == 0 {
return requested, nil
}

// If an upfront shutdown script was provided, and the user did not request
// a custom shutdown script, return the upfront address.
if len(requested) == 0 {
return upfront, nil
}

// If both an upfront shutdown script and a custom close script were
// provided, error if the user provided shutdown script does not match
// the upfront shutdown script (because closing out to a different script
// would violate upfront shutdown).
if !bytes.Equal(upfront, requested) {
return nil, errUpfrontShutdownScriptMismatch
}

// The user requested script matches the upfront shutdown script, so we
// can return it without error.
return upfront, nil
}

// handleLocalCloseReq kicks-off the workflow to execute a cooperative or
// forced unilateral closure of the channel initiated by a local subsystem.
func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) {
Expand All @@ -2144,13 +2175,26 @@ func (p *peer) handleLocalCloseReq(req *htlcswitch.ChanClose) {
// out this channel on-chain, so we execute the cooperative channel
// closure workflow.
case htlcswitch.CloseRegular:
// First, we'll fetch a delivery script that we'll use to send the
// funds to in the case of a successful negotiation. If an upfront
// shutdown script was set, we will use it. Otherwise, we get a fresh
// delivery script.
deliveryScript := channel.LocalUpfrontShutdownScript()
// First, we'll choose a delivery address that we'll use to send the
// funds to in the case of a successful negotiation.

// An upfront shutdown and user provided script are both optional,
// but must be equal if both set (because we cannot serve a request
// to close out to a script which violates upfront shutdown). Get the
// appropriate address to close out to (which may be nil if neither
// are set) and error if they are both set and do not match.
deliveryScript, err := chooseDeliveryScript(
channel.LocalUpfrontShutdownScript(), req.DeliveryScript,
)
if err != nil {
peerLog.Errorf("cannot close channel %v: %v", req.ChanPoint, err)
req.Err <- err
return
}

// If neither an upfront address or a user set address was
// provided, generate a fresh script.
if len(deliveryScript) == 0 {
var err error
deliveryScript, err = p.genDeliveryScript()
if err != nil {
peerLog.Errorf(err.Error())
Expand Down
Loading

0 comments on commit 94d3eb6

Please sign in to comment.