From 8950d4ed35cae655d9f2b051e7688be34b72c1a2 Mon Sep 17 00:00:00 2001
From: Al Cutter <al@google.com>
Date: Thu, 7 Mar 2024 17:35:12 +0000
Subject: [PATCH] Fix nil ptr (#128)

---
 cmd/internal/distributor/distributor.go      |  6 ++-
 cmd/internal/distributor/distributor_test.go | 52 ++++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/cmd/internal/distributor/distributor.go b/cmd/internal/distributor/distributor.go
index fd757b2..bb19b41 100644
--- a/cmd/internal/distributor/distributor.go
+++ b/cmd/internal/distributor/distributor.go
@@ -251,7 +251,11 @@ func (d *Distributor) Distribute(ctx context.Context, logID, witID string, nextR
 			return status.Errorf(codes.Internal, "failed to scan rows: %v", err)
 		}
 		allCheckpoints = append(allCheckpoints, cp)
-		witnesses = append(witnesses, d.ws[witID])
+		// If there is no known witness ID, this is probably due to an old witness
+		// having been removed from the config.
+		if w, ok := d.ws[witID]; ok {
+			witnesses = append(witnesses, w)
+		}
 	}
 
 	if err := rows.Err(); err != nil {
diff --git a/cmd/internal/distributor/distributor_test.go b/cmd/internal/distributor/distributor_test.go
index 53205dd..c8059eb 100644
--- a/cmd/internal/distributor/distributor_test.go
+++ b/cmd/internal/distributor/distributor_test.go
@@ -554,6 +554,58 @@ func TestFiltersUnknownSignatures(t *testing.T) {
 	}
 }
 
+func TestDisappearingWitness(t *testing.T) {
+	ws := map[string]note.Verifier{
+		aardvarkVKey: witAardvark.verifier,
+		badgerVKey:   witBadger.verifier,
+	}
+	ls := map[string]config.LogInfo{
+		"FooLog": logFoo.LogInfo,
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	db, err := helper.create("TestDisappearingWitness")
+	if err != nil {
+		t.Fatalf("helper.create(): %v", err)
+	}
+
+	// Put a checkpoint in sigs from a known witness.
+	{
+		d, err := distributor.NewDistributor(ws, ls, db)
+		if err != nil {
+			t.Fatalf("NewDistributor(): %v", err)
+		}
+
+		writeCP := logFoo.checkpoint(16, "16", witAardvark.signer)
+		err = d.Distribute(ctx, "FooLog", "Aardvark", writeCP)
+		if err != nil {
+			t.Fatalf("Distribute(): %v", err)
+		}
+	}
+
+	// Now remove one of the witnesses; it was a bad aardvark.
+	ws = map[string]note.Verifier{
+		badgerVKey:    witBadger.verifier,
+		chameleonVKey: witChameleon.verifier,
+	}
+
+	// Now recreate the distributor instance to represent re-deploying after the config update, and
+	// try to update the checkpoint with a new signature from another known witness.
+	{
+		d, err := distributor.NewDistributor(ws, ls, db)
+		if err != nil {
+			t.Fatalf("NewDistributor(): %v", err)
+		}
+
+		writeCP := logFoo.checkpoint(16, "16", witBadger.signer)
+		err = d.Distribute(ctx, "FooLog", "Badger", writeCP)
+		if err != nil {
+			t.Fatalf("Distribute(): %v", err)
+		}
+	}
+}
+
 func TestGetCheckpointN(t *testing.T) {
 	// The base case for this test is that 2 checkpoints have already been written:
 	//  - aardvark, at tree size 16