github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/helloworld/helloworld_test.go (about) 1 // Copyright 2021 Google LLC. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package helloworld 16 17 import ( 18 "context" 19 "crypto/rand" 20 "flag" 21 "fmt" 22 "testing" 23 "time" 24 25 p "github.com/google/trillian-examples/helloworld/personality" 26 "github.com/transparency-dev/formats/log" 27 "golang.org/x/mod/sumdb/note" 28 ) 29 30 const ( 31 // testPrivateKey is the personality's key for signing its checkpoints. 32 testPrivateKey = "PRIVATE+KEY+helloworld+b51acf1b+ASW28PXJDCV8klh7JeacIgfJR3/Q60dklasmgnv4c9I7" 33 // testPublicKey is used for verifying the signatures on the checkpoints from 34 // the personality. 35 testPublicKey = "helloworld+b51acf1b+AZ2ZM0ZQ69GwDUyO7/x0JyLo09y3geyufyN1mFFMeUH3" 36 ) 37 38 var ( 39 seed = flag.String("seed", time.Now().Format(time.UnixDate), "Seed for leaf randomness") 40 trillianAddr = flag.String("trillian", "localhost:50054", "Host:port of Trillian Log RPC server") 41 treeID = flag.Int64("tree_id", 0, "Tree ID") 42 ) 43 44 func mustGetSigner(t *testing.T) note.Signer { 45 t.Helper() 46 s, err := note.NewSigner(testPrivateKey) 47 if err != nil { 48 t.Fatalf("Failed to create signer: %q", err) 49 } 50 return s 51 } 52 53 func mustGetVerifier(t *testing.T) note.Verifier { 54 t.Helper() 55 v, err := note.NewVerifier(testPublicKey) 56 if err != nil { 57 t.Fatalf("Failed to create verifier: %q", err) 58 } 59 return v 60 } 61 62 func mustOpenCheckpoint(t *testing.T, cRaw []byte) *log.Checkpoint { 63 t.Helper() 64 cp, _, _, err := log.ParseCheckpoint(cRaw, "Hello World Log", mustGetVerifier(t)) 65 if err != nil { 66 t.Fatalf("Failed to open checkpoint: %q", err) 67 } 68 return cp 69 } 70 71 // TestAppend appends a random entry to the log and ensures that the 72 // checkpoint updates properly (locally on the personality's side). 73 func TestAppend(t *testing.T) { 74 if *treeID == 0 { 75 t.Skip("--tree_id flag unset, skipping test") 76 } 77 78 name := "testAppend" 79 t.Run(name, func(t *testing.T) { 80 ctx := context.Background() 81 personality, err := p.NewPersonality(*trillianAddr, *treeID, mustGetSigner(t)) 82 if err != nil { 83 t.Fatalf(err.Error()) 84 } 85 chkptOldRaw, err := personality.GetChkpt(ctx) 86 if err != nil { 87 t.Fatalf(err.Error()) 88 } 89 chkptOld := mustOpenCheckpoint(t, chkptOldRaw) 90 // Add a random entry so we can be sure it's new. 91 entry := make([]byte, 10) 92 if _, err := rand.Read(entry); err != nil { 93 t.Error(err) 94 } 95 chkptNewRaw, err := personality.Append(ctx, entry) 96 if err != nil { 97 t.Fatalf(err.Error()) 98 } 99 chkptNew := mustOpenCheckpoint(t, chkptNewRaw) 100 if chkptNew.Size <= chkptOld.Size { 101 t.Errorf("the log didn't grow properly in %v", name) 102 } 103 fmt.Printf("success in %v, new log size is %v\n", name, chkptNew.Size) 104 }) 105 } 106 107 // TestUpdate appends a random entry to the log and ensures that the 108 // checkpoint updates properly for both the personality and the verifier. 109 func TestUpdate(t *testing.T) { 110 if *treeID == 0 { 111 t.Skip("--tree_id flag unset, skipping test") 112 } 113 114 name := "testUpdate" 115 t.Run(name, func(t *testing.T) { 116 ctx := context.Background() 117 personality, err := p.NewPersonality(*trillianAddr, *treeID, mustGetSigner(t)) 118 if err != nil { 119 t.Fatalf(err.Error()) 120 } 121 client := NewClient(personality, mustGetVerifier(t)) 122 chkptRaw, err := personality.GetChkpt(ctx) 123 if err != nil { 124 t.Fatalf(err.Error()) 125 } 126 client.chkpt = mustOpenCheckpoint(t, chkptRaw) 127 entry := make([]byte, 10) 128 if _, err := rand.Read(entry); err != nil { 129 t.Error(err) 130 } 131 if _, err := personality.Append(ctx, entry); err != nil { 132 t.Error(err) 133 } 134 chkptNewRaw, pf, err := personality.UpdateChkpt(ctx, client.chkpt.Size) 135 if err != nil { 136 t.Fatalf(err.Error()) 137 } 138 got := client.UpdateChkpt(chkptNewRaw, pf) 139 if got != nil { 140 t.Errorf("verifier failed to update checkpoint: %q", err) 141 } 142 chkptNew := mustOpenCheckpoint(t, chkptNewRaw) 143 fmt.Printf("success in %v, new log size is %v\n", name, chkptNew.Size) 144 }) 145 } 146 147 // TestIncl tests inclusion proof checking for entries that both are and 148 // aren't in the log. 149 func TestIncl(t *testing.T) { 150 if *treeID == 0 { 151 t.Skip("--tree_id flag unset, skipping test") 152 } 153 154 tests := []struct { 155 name string 156 addEntries []string 157 checkEntries []string 158 wants []bool 159 }{ 160 { 161 name: "all there", 162 addEntries: []string{"a", "b", "c", "d"}, 163 checkEntries: []string{"a", "b", "c", "d"}, 164 wants: []bool{true, true, true, true}, 165 }, 166 { 167 name: "all missing", 168 addEntries: []string{"e", "f", "g", "h"}, 169 checkEntries: []string{"w", "x", "y", "z"}, 170 wants: []bool{false, false, false, false}, 171 }, 172 { 173 name: "mixed bag", 174 addEntries: []string{"i", "j", "k", "l"}, 175 checkEntries: []string{"i", "j", "y", "z"}, 176 wants: []bool{true, true, false, false}, 177 }, 178 } 179 180 for _, test := range tests { 181 test := test 182 t.Run(test.name, func(t *testing.T) { 183 ctx := context.Background() 184 personality, err := p.NewPersonality(*trillianAddr, *treeID, mustGetSigner(t)) 185 if err != nil { 186 t.Fatalf(err.Error()) 187 } 188 var chkptRaw []byte 189 // Append all the entries we plan to add, folding in 190 // the seed to avoid duplication of entries across tests. 191 for _, entry := range test.addEntries { 192 entry = entry + *seed 193 bs := []byte(entry) 194 chkptRaw, err = personality.Append(ctx, bs) 195 if err != nil { 196 // If the checkpoint didn't update that's a problem. 197 t.Fatalf(err.Error()) 198 } 199 } 200 client := NewClient(personality, mustGetVerifier(t)) 201 // For the purposes of the test let's skip having the 202 // verifier update the right way and just assign their checkpoint. 203 client.chkpt = mustOpenCheckpoint(t, chkptRaw) 204 // Then prove and check inclusion of the other entries. 205 for i := range test.checkEntries { 206 entry := test.checkEntries[i] + *seed 207 bs := []byte(entry) 208 // Ignore error here since it's okay if we 209 // don't have a valid inclusion proof (testing 210 // on entries that aren't there). 211 pf, err := personality.ProveIncl(ctx, client.chkpt.Size, bs) 212 got := false 213 if err == nil { 214 got = client.VerIncl(bs, pf) 215 } 216 if got != test.wants[i] { 217 t.Errorf("%v: got %v, want %v", test.name, got, test.wants[i]) 218 } 219 } 220 fmt.Printf("testIncl: all good for the %v test!\n", test.name) 221 }) 222 } 223 }