peer: reject remote closes with active HTLCs

In this commit, we follow up to the prior commit by ensuring we won't
accept a co-op close request for a chennel with active HTLCs. When
creating a chanCloser for the first time, we'll check the set of HTLC's
and reject a request (by sending a wire error) if the target channel
still as active HTLC's.
This commit is contained in:
Olaoluwa Osuntokun 2018-03-30 13:04:59 -07:00
parent ecbeca5f29
commit 447a031435
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 24 additions and 7 deletions

27
peer.go

@ -1438,9 +1438,14 @@ out:
// closure process. // closure process.
chanCloser, err := p.fetchActiveChanCloser(closeMsg.cid) chanCloser, err := p.fetchActiveChanCloser(closeMsg.cid)
if err != nil { if err != nil {
// TODO(roasbeef): send protocol error?
peerLog.Errorf("unable to respond to remote "+ peerLog.Errorf("unable to respond to remote "+
"close msg: %v", err) "close msg: %v", err)
errMsg := &lnwire.Error{
ChanID: closeMsg.cid,
Data: lnwire.ErrorData(err.Error()),
}
p.queueMsg(errMsg, nil)
continue continue
} }
@ -1520,11 +1525,21 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
// cooperative channel closure. // cooperative channel closure.
chanCloser, ok := p.activeChanCloses[chanID] chanCloser, ok := p.activeChanCloses[chanID]
if !ok { if !ok {
// If we need to create a chan closer for the first time, then
// we'll check to ensure that the channel is even in the proper
// state to allow a co-op channel closure.
if len(channel.ActiveHtlcs()) != 0 {
return nil, fmt.Errorf("cannot co-op close " +
"channel w/ active htlcs")
}
// We'll create a valid closing state machine in order to // We'll create a valid closing state machine in order to
// respond to the initiated cooperative channel closure. // respond to the initiated cooperative channel closure.
deliveryAddr, err := p.genDeliveryScript() deliveryAddr, err := p.genDeliveryScript()
if err != nil { if err != nil {
return nil, err peerLog.Errorf("unable to gen delivery script: %v", err)
return nil, fmt.Errorf("close addr unavailable")
} }
// In order to begin fee negotiations, we'll first compute our // In order to begin fee negotiations, we'll first compute our
@ -1532,8 +1547,9 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
// we weren't the ones that initiated the channel closure. // we weren't the ones that initiated the channel closure.
feePerVSize, err := p.server.cc.feeEstimator.EstimateFeePerVSize(6) feePerVSize, err := p.server.cc.feeEstimator.EstimateFeePerVSize(6)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to query fee "+ peerLog.Errorf("unable to query fee estimator: %v", err)
"estimator: %v", err)
return nil, fmt.Errorf("unable to estimate fee")
} }
// We'll then convert the sat per weight to sat per k/w as this // We'll then convert the sat per weight to sat per k/w as this
@ -1543,7 +1559,8 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
_, startingHeight, err := p.server.cc.chainIO.GetBestBlock() _, startingHeight, err := p.server.cc.chainIO.GetBestBlock()
if err != nil { if err != nil {
return nil, err peerLog.Errorf("unable to obtain best block: %v", err)
return nil, fmt.Errorf("cannot obtain best block")
} }
// Before we create the chan closer, we'll start a new // Before we create the chan closer, we'll start a new

@ -1094,8 +1094,8 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
// Before we attempt the cooperative channel closure, we'll // Before we attempt the cooperative channel closure, we'll
// examine the channel to ensure that it doesn't have a // examine the channel to ensure that it doesn't have a
// lingering HTLC. // lingering HTLC.
if len(channel.localCommit.Htlcs) != 0 { if len(channel.ActiveHtlcs()) != 0 {
return nil, fmt.Errorf("cannot co-op close channel " + return fmt.Errorf("cannot co-op close channel " +
"with active htlcs") "with active htlcs")
} }