diff --git a/channeldb/channel.go b/channeldb/channel.go index a68f517c..42a53a7c 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -102,6 +102,11 @@ var ( // ErrNoCommitPoint is returned when no data loss commit point is found // in the database. ErrNoCommitPoint = fmt.Errorf("no commit point found") + + // ErrNoRestoredChannelMutation is returned when a caller attempts to + // mutate a channel that's been recovered. + ErrNoRestoredChannelMutation = fmt.Errorf("cannot mutate restored " + + "channel state") ) // ChannelType is an enum-like type that describes one of several possible @@ -958,6 +963,13 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment) error { c.Lock() defer c.Unlock() + // If this is a restored channel, then we want to avoid mutating the + // state as all, as it's impossible to do so in a protocol compliant + // manner. + if c.hasChanStatus(ChanStatusRestored) { + return ErrNoRestoredChannelMutation + } + err := c.Db.Update(func(tx *bbolt.Tx) error { chanBucket, err := fetchChanBucket( tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash, @@ -1379,6 +1391,13 @@ func (c *OpenChannel) AppendRemoteCommitChain(diff *CommitDiff) error { c.Lock() defer c.Unlock() + // If this is a restored channel, then we want to avoid mutating the + // state as all, as it's impossible to do so in a protocol compliant + // manner. + if c.hasChanStatus(ChanStatusRestored) { + return ErrNoRestoredChannelMutation + } + return c.Db.Update(func(tx *bbolt.Tx) error { // First, we'll grab the writable bucket where this channel's // data resides. @@ -1503,6 +1522,13 @@ func (c *OpenChannel) AdvanceCommitChainTail(fwdPkg *FwdPkg) error { c.Lock() defer c.Unlock() + // If this is a restored channel, then we want to avoid mutating the + // state as all, as it's impossible to do so in a protocol compliant + // manner. + if c.hasChanStatus(ChanStatusRestored) { + return ErrNoRestoredChannelMutation + } + var newRemoteCommit *ChannelCommitment err := c.Db.Update(func(tx *bbolt.Tx) error { diff --git a/channeldb/db_test.go b/channeldb/db_test.go index 261e0b36..43b62619 100644 --- a/channeldb/db_test.go +++ b/channeldb/db_test.go @@ -395,6 +395,22 @@ func TestRestoreChannelShells(t *testing.T) { "pubkey: %v", err) } + // Ensure that it isn't possible to modify the commitment state machine + // of this restored channel. + channel := nodeChans[0] + err = channel.UpdateCommitment(nil) + if err != ErrNoRestoredChannelMutation { + t.Fatalf("able to mutate restored channel") + } + err = channel.AppendRemoteCommitChain(nil) + if err != ErrNoRestoredChannelMutation { + t.Fatalf("able to mutate restored channel") + } + err = channel.AdvanceCommitChainTail(nil) + if err != ErrNoRestoredChannelMutation { + t.Fatalf("able to mutate restored channel") + } + // That single channel should have the proper channel point, and also // the expected set of flags to indicate that it was a restored // channel.