contractcourt: ensure the chainWatcher is able to play all remote commitments

This commit is contained in:
Olaoluwa Osuntokun 2018-04-29 15:42:01 -07:00
parent 88ff2af931
commit c8b15719f2
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21

@ -340,40 +340,71 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
) )
remoteStateNum := remoteCommit.CommitHeight remoteStateNum := remoteCommit.CommitHeight
remoteChainTip, err := c.cfg.chanState.RemoteCommitChainTip()
if err != nil && err != channeldb.ErrNoPendingCommit {
log.Errorf("unable to obtain chain tip for "+
"ChannelPoint(%v): %v",
c.cfg.chanState.FundingOutpoint, err)
return
}
switch { switch {
// If state number spending transaction matches the // If state number spending transaction matches the
// current latest state, then they've initiated a // current latest state, then they've initiated a
// unilateral close. So we'll trigger the unilateral // unilateral close. So we'll trigger the unilateral
// close signal so subscribers can clean up the state // close signal so subscribers can clean up the state
// as necessary. // as necessary.
// case broadcastStateNum == remoteStateNum:
err := c.dispatchRemoteForceClose(
commitSpend, *remoteCommit, false,
)
if err != nil {
log.Errorf("unable to handle remote "+
"close for chan_point=%v: %v",
c.cfg.chanState.FundingOutpoint, err)
}
// We'll also handle the case of the remote party // We'll also handle the case of the remote party
// broadcasting their commitment transaction which is // broadcasting their commitment transaction which is
// one height above ours. This case can arise when we // one height above ours. This case can arise when we
// initiate a state transition, but the remote party // initiate a state transition, but the remote party
// has a fail crash _after_ accepting the new state, // has a fail crash _after_ accepting the new state,
// but _before_ sending their signature to us. // but _before_ sending their signature to us.
case broadcastStateNum >= remoteStateNum: case broadcastStateNum == remoteStateNum+1 &&
if err := c.dispatchRemoteForceClose( remoteChainTip != nil:
commitSpend, *remoteCommit,
); err != nil { err := c.dispatchRemoteForceClose(
commitSpend, remoteChainTip.Commitment,
true,
)
if err != nil {
log.Errorf("unable to handle remote "+ log.Errorf("unable to handle remote "+
"close for chan_point=%v: %v", "close for chan_point=%v: %v",
c.cfg.chanState.FundingOutpoint, err) c.cfg.chanState.FundingOutpoint, err)
} }
// This is the case that somehow the commitment
// broadcast is actually greater than even one beyond
// our best known state number. This should NEVER
// happen, but we'll log it in any case.
case broadcastStateNum > remoteStateNum+1:
log.Errorf("Remote node broadcast state #%v, "+
"which is more than 1 beyond best known "+
"state #%v!!!", broadcastStateNum,
remoteStateNum)
// If the state number broadcast is lower than the // If the state number broadcast is lower than the
// remote node's current un-revoked height, then // remote node's current un-revoked height, then
// THEY'RE ATTEMPTING TO VIOLATE THE CONTRACT LAID OUT // THEY'RE ATTEMPTING TO VIOLATE THE CONTRACT LAID OUT
// WITHIN THE PAYMENT CHANNEL. Therefore we close the // WITHIN THE PAYMENT CHANNEL. Therefore we close the
// signal indicating a revoked broadcast to allow // signal indicating a revoked broadcast to allow
// subscribers to // subscribers to swiftly dispatch justice!!!
// swiftly dispatch justice!!!
case broadcastStateNum < remoteStateNum: case broadcastStateNum < remoteStateNum:
if err := c.dispatchContractBreach( err := c.dispatchContractBreach(
commitSpend, remoteCommit, commitSpend, remoteCommit,
broadcastStateNum, broadcastStateNum,
); err != nil { )
if err != nil {
log.Errorf("unable to handle channel "+ log.Errorf("unable to handle channel "+
"breach for chan_point=%v: %v", "breach for chan_point=%v: %v",
c.cfg.chanState.FundingOutpoint, err) c.cfg.chanState.FundingOutpoint, err)
@ -570,13 +601,16 @@ func (c *chainWatcher) dispatchLocalForceClose(
return nil return nil
} }
// dispatchRemoteForceClose processes a detected unilateral channel closure by the // dispatchRemoteForceClose processes a detected unilateral channel closure by
// remote party. This function will prepare a UnilateralCloseSummary which will // the remote party. This function will prepare a UnilateralCloseSummary which
// then be sent to any subscribers allowing them to resolve all our funds in // will then be sent to any subscribers allowing them to resolve all our funds
// the channel on chain. Once this close summary is prepared, all registered // in the channel on chain. Once this close summary is prepared, all registered
// subscribers will receive a notification of this event. // subscribers will receive a notification of this event. The
// isRemotePendingCommit argument should be set to true if the remote node
// broadcast their pending commitment (w/o revoking their current settled
// commitment).
func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail, func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDetail,
remoteCommit channeldb.ChannelCommitment) error { remoteCommit channeldb.ChannelCommitment, isRemotePendingCommit bool) error {
log.Infof("Unilateral close of ChannelPoint(%v) "+ log.Infof("Unilateral close of ChannelPoint(%v) "+
"detected", c.cfg.chanState.FundingOutpoint) "detected", c.cfg.chanState.FundingOutpoint)
@ -584,8 +618,9 @@ func (c *chainWatcher) dispatchRemoteForceClose(commitSpend *chainntnfs.SpendDet
// First, we'll create a closure summary that contains all the // First, we'll create a closure summary that contains all the
// materials required to let each subscriber sweep the funds in the // materials required to let each subscriber sweep the funds in the
// channel on-chain. // channel on-chain.
uniClose, err := lnwallet.NewUnilateralCloseSummary(c.cfg.chanState, uniClose, err := lnwallet.NewUnilateralCloseSummary(
c.cfg.signer, c.cfg.pCache, commitSpend, remoteCommit, c.cfg.chanState, c.cfg.signer, c.cfg.pCache, commitSpend,
remoteCommit, isRemotePendingCommit,
) )
if err != nil { if err != nil {
return err return err