diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 3d13c9229..0c522aa30 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -584,48 +584,32 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) { "state #%v!!! Attempting recovery...", broadcastStateNum, remoteStateNum) - // If we are lucky, the remote peer sent us the correct - // commitment point during channel sync, such that we - // can sweep our funds. If we cannot find the commit - // point, there's not much we can do other than wait - // for us to retrieve it. We will attempt to retrieve - // it from the peer each time we connect to it. - // - // TODO(halseth): actively initiate re-connection to - // the peer? - var commitPoint *btcec.PublicKey - backoff := minCommitPointPollTimeout - for { - commitPoint, err = c.cfg.chanState.DataLossCommitPoint() - if err == nil { - break - } + tweaklessCommit := (c.cfg.chanState.ChanType == + channeldb.SingleFunderTweakless) - log.Errorf("Unable to retrieve commitment "+ - "point for channel(%v) with lost "+ - "state: %v. Retrying in %v.", - c.cfg.chanState.FundingOutpoint, - err, backoff) - - select { - // Wait before retrying, with an exponential - // backoff. - case <-time.After(backoff): - backoff = 2 * backoff - if backoff > maxCommitPointPollTimeout { - backoff = maxCommitPointPollTimeout - } - - case <-c.quit: + // If this isn't a tweakless commitment, then we'll + // need to wait for the remote party's latest unrevoked + // commitment point to be presented to us as we need + // this to sweep. Otherwise, we can dispatch the remote + // close and sweep immediately using a fake commitPoint + // as it isn't actually needed for recovery anymore. + commitPoint := c.cfg.chanState.RemoteCurrentRevocation + if !tweaklessCommit { + commitPoint = c.waitForCommitmentPoint() + if commitPoint == nil { return } - } - log.Infof("Recovered commit point(%x) for "+ - "channel(%v)! Now attempting to use it to "+ - "sweep our funds...", - commitPoint.SerializeCompressed(), - c.cfg.chanState.FundingOutpoint) + log.Infof("Recovered commit point(%x) for "+ + "channel(%v)! Now attempting to use it to "+ + "sweep our funds...", + commitPoint.SerializeCompressed(), + c.cfg.chanState.FundingOutpoint) + + } else { + log.Infof("ChannelPoint(%v) is tweakless, " + + "moving to sweep directly on chain") + } // Since we don't have the commitment stored for this // state, we'll just pass an empty commitment within @@ -1009,3 +993,39 @@ func (c *chainWatcher) dispatchContractBreach(spendEvent *chainntnfs.SpendDetail return nil } + +// waitForCommitmentPoint waits for the commitment point to be inserted into +// the local database. We'll use this method in the DLP case, to wait for the +// remote party to send us their point, as we can't proceed until we have that. +func (c *chainWatcher) waitForCommitmentPoint() *btcec.PublicKey { + // If we are lucky, the remote peer sent us the correct commitment + // point during channel sync, such that we can sweep our funds. If we + // cannot find the commit point, there's not much we can do other than + // wait for us to retrieve it. We will attempt to retrieve it from the + // peer each time we connect to it. + // + // TODO(halseth): actively initiate re-connection to the peer? + backoff := minCommitPointPollTimeout + for { + commitPoint, err := c.cfg.chanState.DataLossCommitPoint() + if err == nil { + return commitPoint + } + + log.Errorf("Unable to retrieve commitment point for "+ + "channel(%v) with lost state: %v. Retrying in %v.", + c.cfg.chanState.FundingOutpoint, err, backoff) + + select { + // Wait before retrying, with an exponential backoff. + case <-time.After(backoff): + backoff = 2 * backoff + if backoff > maxCommitPointPollTimeout { + backoff = maxCommitPointPollTimeout + } + + case <-c.quit: + return nil + } + } +}