lnwallet: don't sign new commitment if next revocation point is unknown

In this commit, we extend the initial check within SignNextCommitment
to bail out early if we don’t yet know the commitment point of the
remote party. This prevents a class of nil pointer panics if we attempt
to create a new state without yet having received the FundingLocked
message.
This commit is contained in:
Olaoluwa Osuntokun 2017-12-10 16:10:44 -08:00
parent b40c84833e
commit 5b4aa82667
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 34 additions and 4 deletions

@ -2930,10 +2930,13 @@ func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Sig
lc.Lock()
defer lc.Unlock()
// If we're awaiting for an ACK to a commitment signature, then we're
// unable to create new states. Basically we don't want to advance the
// commitment chain by more than one at a time.
if lc.remoteCommitChain.hasUnackedCommitment() {
// If we're awaiting for an ACK to a commitment signature, or if we
// don't yet have the initial next revocation point of the remote
// party, then we're unable to create new states. Each time we create a
// new state, we consume a prior revocation point.
if lc.remoteCommitChain.hasUnackedCommitment() ||
lc.channelState.RemoteCurrentRevocation == nil {
return nil, nil, ErrNoWindow
}

@ -3832,4 +3832,31 @@ func TestChanAvailableBandwidth(t *testing.T) {
// TODO(roasbeef): additional tests from diff starting conditions
}
// TestSignCommitmentFailNotLockedIn tests that a channel will not attempt to
// create a new state if it doesn't yet know of the next revocation point for
// the remote party.
func TestSignCommitmentFailNotLockedIn(t *testing.T) {
t.Parallel()
// Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, _, cleanUp, err := createTestChannels(1)
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer cleanUp()
// Next, we'll modify Alice's internal state to omit knowledge of Bob's
// next revocation point.
aliceChannel.channelState.RemoteCurrentRevocation = nil
// If we now try to initiate a state update, then it should fail as
// Alice is unable to actually create a new state.
_, _, err = aliceChannel.SignNextCommitment()
if err != ErrNoWindow {
t.Fatalf("expected ErrNoWindow, instead have: %v", err)
}
}
// TODO(roasbeef): testing.Quick test case for retrans!!!