From 517ad4e4f563c3dbf280b1ec4e3c1e076f14b2ab Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 24 Sep 2019 14:33:59 +0200 Subject: [PATCH] lnwallet: log empty commit sig event To facilitate the logging, this commit adds a new OweCommitment method. For the logging, we only need to consider the remote perspective. In a later commit, we'll also start using the local perspective to support the decision to send another signature. --- lnwallet/channel.go | 85 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index ddd8cd41..5a87aca8 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3233,6 +3233,14 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch lc.Lock() defer lc.Unlock() + // Check for empty commit sig. This should never happen, but we don't + // dare to fail hard here. We assume peers can deal with the empty sig + // and continue channel operation. We log an error so that the bug + // causing this can be tracked down. + if !lc.oweCommitment(true) { + lc.log.Errorf("sending empty commit sig") + } + var ( sig lnwire.Sig htlcSigs []lnwire.Sig @@ -3987,6 +3995,18 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, lc.Lock() defer lc.Unlock() + // Check for empty commit sig. Because of a previously existing bug, it + // is possible that we receive an empty commit sig from nodes running an + // older version. This is a relaxation of the spec, but it is still + // possible to handle it. To not break any channels with those older + // nodes, we just log the event. This check is also not totally + // reliable, because it could be that we've sent out a new sig, but the + // remote hasn't received it yet. We could then falsely assume that they + // should add our updates to their remote commitment tx. + if !lc.oweCommitment(false) { + lc.log.Warnf("empty commit sig message received") + } + // Determine the last update on the local log that has been locked in. localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex localHtlcIndex := lc.remoteCommitChain.tail().ourHtlcIndex @@ -4140,6 +4160,71 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, return nil } +// OweCommitment returns a boolean value reflecting whether we need to send +// out a commitment signature because there are outstanding local updates and/or +// updates in the local commit tx that aren't reflected in the remote commit tx +// yet. +func (lc *LightningChannel) OweCommitment(local bool) bool { + lc.RLock() + defer lc.RUnlock() + + return lc.oweCommitment(local) +} + +// oweCommitment is the internal version of OweCommitment. This function expects +// to be executed with a lock held. +func (lc *LightningChannel) oweCommitment(local bool) bool { + var ( + remoteUpdatesPending, localUpdatesPending bool + + lastLocalCommit = lc.localCommitChain.tip() + lastRemoteCommit = lc.remoteCommitChain.tip() + + perspective string + ) + + if local { + perspective = "local" + + // There are local updates pending if our local update log is + // not in sync with our remote commitment tx. + localUpdatesPending = lc.localUpdateLog.logIndex != + lastRemoteCommit.ourMessageIndex + + // There are remote updates pending if their remote commitment + // tx (our local commitment tx) contains updates that we don't + // have added to our remote commitment tx yet. + remoteUpdatesPending = lastLocalCommit.theirMessageIndex != + lastRemoteCommit.theirMessageIndex + + } else { + perspective = "remote" + + // There are local updates pending (local updates from the + // perspective of the remote party) if the remote party has + // updates to their remote tx pending for which they haven't + // signed yet. + localUpdatesPending = lc.remoteUpdateLog.logIndex != + lastLocalCommit.theirMessageIndex + + // There are remote updates pending (remote updates from the + // perspective of the remote party) if we have updates on our + // remote commitment tx that they haven't added to theirs yet. + remoteUpdatesPending = lastRemoteCommit.ourMessageIndex != + lastLocalCommit.ourMessageIndex + } + + // If any of the conditions above is true, we owe a commitment + // signature. + oweCommitment := localUpdatesPending || remoteUpdatesPending + + lc.log.Tracef("%v owes commit: %v (local updates: %v, "+ + "remote updates %v)", perspective, oweCommitment, + localUpdatesPending, remoteUpdatesPending) + + return oweCommitment +} + // FullySynced returns a boolean value reflecting if both commitment chains // (remote+local) are fully in sync. Both commitment chains are fully in sync // if the tip of each chain includes the latest committed changes from both