channeldb: filter out unsigned acked updates in AdvanceCommitChainTail

This commit moves the deletion of all updates under the unsigned
acked updates key from AppendRemoteCommitChain to
AdvanceCommitChainTail. This is done because if we went down after
signing for these updates but before receiving a revocation, we would
incorrectly reject their commitment signature:

Alice                Bob
      -----add----->
      -----sig----->
      <----rev------
      <----sig------
      -----rev----->
      <----fail-----
      <----sig------
      -----rev----->
      -----sig----->
      *reconnect*
      <----rev------
      <----add------
      x----sig------

It is also important to note that filtering is required when we
receive a revocation to ensure that we aren't erroneously deleting
remote updates. Take the following state transitions:

 Alice                Bob
       -----add----->
       -----sig----->
       <----rev------
       <----sig------
       -----rev----->
       -----add----->
       -----sig----->
       <----fail-----
       <----sig------
       -----rev-----> (alice stores updates here)
       <----rev------

In the above case, if Alice deleted all updates rather than filtering
when receiving the final revocation from Bob, then Alice would have
to force close the channel due to missing updates. Since Alice hasn't
signed for any of the unsigned acked updates, she should not filter any
of them out.
This commit is contained in:
nsa committed 2020-07-27 15:41:48 -04:00
1 parent 36eb666cb7
commit 2149157d49
2 files changed
+42 -12

No files matched your search

+38 -8
View File
@@ -1980,14 +1980,6 @@ func (c *OpenChannel) AppendRemoteCommitChain(diff *CommitDiff) error {
return err
}
// Clear unsigned acked remote updates. We are signing now for
// all that we've got.
err = chanBucket.Delete(unsignedAckedUpdatesKey)
if err != nil {
return fmt.Errorf("unable to clear dangling remote "+
"updates: %v", err)
}
// TODO(roasbeef): use seqno to derive key for later LCP
// With the bucket retrieved, we'll now serialize the commit
@@ -2196,6 +2188,44 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg) error {
return err
}
// Persist the unsigned acked updates that are not included
// in their new commitment.
updateBytes := chanBucket.Get(unsignedAckedUpdatesKey)
if updateBytes == nil {
// If there are no updates to sign, we don't need to
// filter out any updates.
newRemoteCommit = &newCommit.Commitment
return nil
}
r := bytes.NewReader(updateBytes)
unsignedUpdates, err := deserializeLogUpdates(r)
if err != nil {
return err
}
var validUpdates []LogUpdate
for _, upd := range unsignedUpdates {
lIdx := upd.LogIndex
// Filter for updates that are not on the remote
// commitment.
if lIdx >= newCommit.Commitment.RemoteLogIndex {
validUpdates = append(validUpdates, upd)
}
}
var b bytes.Buffer
err = serializeLogUpdates(&b, validUpdates)
if err != nil {
return fmt.Errorf("unable to serialize log updates: %v", err)
}
err = chanBucket.Put(unsignedAckedUpdatesKey, b.Bytes())
if err != nil {
return fmt.Errorf("unable to store under unsignedAckedUpdatesKey: %v", err)
}
newRemoteCommit = &newCommit.Commitment
return nil