From 33ad645f8c65388736c10b267c2ec2ec4a0c62c1 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 8 Mar 2019 17:55:42 -0800 Subject: [PATCH] lnwallet: update TestChanSyncFailure to pass with new borked update restriction In this commit, we update the `TestChanSyncFailure` method to pass given the new behavior around updating borked channel states. In order to do this, we add a new method to allow the test to clear an existing channel state. This method may be of independent use in other areas in the codebase in the future as well. --- channeldb/channel.go | 41 +++++++++++++++++++++++++++++++++++++++- lnwallet/channel_test.go | 31 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index aba7fe25f..486b66fd9 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -110,7 +110,7 @@ var ( // ErrChanBorked is returned when a caller attempts to mutate a borked // channel. - ErrChanBorked = fmt.Errorf("channel mutate borked channel") + ErrChanBorked = fmt.Errorf("cannot mutate borked channel") ) // ChannelType is an enum-like type that describes one of several possible @@ -549,6 +549,16 @@ func (c *OpenChannel) ApplyChanStatus(status ChannelStatus) error { return c.putChanStatus(status) } +// ClearChanStatus allows the caller to clear a particular channel status from +// the primary channel status bit field. After this method returns, a call to +// HasChanStatus(status) should return false. +func (c *OpenChannel) ClearChanStatus(status ChannelStatus) error { + c.Lock() + defer c.Unlock() + + return c.clearChanStatus(status) +} + // HasChanStatus returns true if the internal bitfield channel status of the // target channel has the specified status bit set. func (c *OpenChannel) HasChanStatus(status ChannelStatus) bool { @@ -864,6 +874,35 @@ func (c *OpenChannel) putChanStatus(status ChannelStatus) error { return nil } +func (c *OpenChannel) clearChanStatus(status ChannelStatus) error { + if err := c.Db.Update(func(tx *bbolt.Tx) error { + chanBucket, err := fetchChanBucket( + tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash, + ) + if err != nil { + return err + } + + channel, err := fetchOpenChannel(chanBucket, &c.FundingOutpoint) + if err != nil { + return err + } + + // Unset this bit in the bitvector on disk. + status = channel.chanStatus & ^status + channel.chanStatus = status + + return putOpenChannel(chanBucket, channel) + }); err != nil { + return err + } + + // Update the in-memory representation to keep it in sync with the DB. + c.chanStatus = status + + return nil +} + // putChannel serializes, and stores the current state of the channel in its // entirety. func putOpenChannel(chanBucket *bbolt.Bucket, channel *OpenChannel) error { diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 02fa2c442..5616b0fc7 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -3641,6 +3641,8 @@ func TestChanSyncFailure(t *testing.T) { // advanceState is a helper method to fully advance the channel state // by one. advanceState := func() { + t.Helper() + // We'll kick off the test by having Bob send Alice an HTLC, // then lock it in with a state transition. var bobPreimage [32]byte @@ -3672,6 +3674,8 @@ func TestChanSyncFailure(t *testing.T) { // halfAdvance is a helper method that sends a new commitment signature // from Alice to Bob, but doesn't make Bob revoke his current state. halfAdvance := func() { + t.Helper() + // We'll kick off the test by having Bob send Alice an HTLC, // then lock it in with a state transition. var bobPreimage [32]byte @@ -3707,6 +3711,8 @@ func TestChanSyncFailure(t *testing.T) { // assertLocalDataLoss checks that aliceOld and bobChannel detects that // Alice has lost state during sync. assertLocalDataLoss := func(aliceOld *LightningChannel) { + t.Helper() + aliceSyncMsg, err := ChanSyncMsg(aliceOld.channelState) if err != nil { t.Fatalf("unable to produce chan sync msg: %v", err) @@ -3733,6 +3739,25 @@ func TestChanSyncFailure(t *testing.T) { } } + // clearBorkedState is a method that allows us to clear the borked + // state that will arise after the first chan message sync. We need to + // do this in order to be able to continue to update the commitment + // state for our test scenarios. + clearBorkedState := func() { + err = aliceChannel.channelState.ClearChanStatus( + channeldb.ChanStatusLocalDataLoss | channeldb.ChanStatusBorked, + ) + if err != nil { + t.Fatalf("unable to update channel state: %v", err) + } + err = bobChannel.channelState.ClearChanStatus( + channeldb.ChanStatusLocalDataLoss | channeldb.ChanStatusBorked, + ) + if err != nil { + t.Fatalf("unable to update channel state: %v", err) + } + } + // Start by advancing the state. advanceState() @@ -3755,6 +3780,9 @@ func TestChanSyncFailure(t *testing.T) { // Make sure the up-to-date channels still are in sync. assertNoChanSyncNeeded(t, aliceChannel, bobChannel) + // Clear the borked state before we attempt to advance. + clearBorkedState() + // Advance the state again, and do the same check. advanceState() assertNoChanSyncNeeded(t, aliceChannel, bobChannel) @@ -3825,6 +3853,9 @@ func TestChanSyncFailure(t *testing.T) { // Make sure the up-to-date channels still are good. assertNoChanSyncNeeded(t, aliceChannel, bobChannel) + // Clear the borked state before we attempt to advance. + clearBorkedState() + // Finally check that Alice is also able to detect a wrong commit point // when there's a pending remote commit. halfAdvance()