github.com/bartle-stripe/trillian@v1.2.1/storage/mysql/map_storage_test.go (about) 1 // Copyright 2016 Google Inc. 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 mysql 16 17 import ( 18 "bytes" 19 "context" 20 "crypto" 21 "database/sql" 22 "fmt" 23 "strings" 24 "testing" 25 26 "github.com/golang/protobuf/proto" 27 "github.com/google/trillian" 28 "github.com/google/trillian/examples/ct/ctmapper/ctmapperpb" 29 "github.com/google/trillian/storage" 30 "github.com/google/trillian/storage/testdb" 31 "github.com/google/trillian/testonly" 32 "github.com/google/trillian/types" 33 "github.com/kylelemons/godebug/pretty" 34 35 tcrypto "github.com/google/trillian/crypto" 36 storageto "github.com/google/trillian/storage/testonly" 37 ) 38 39 var fixedSigner = tcrypto.NewSigner(0, testonly.NewSignerWithFixedSig(nil, []byte("notempty")), crypto.SHA256) 40 41 func MustSignMapRoot(root *types.MapRootV1) *trillian.SignedMapRoot { 42 r, err := fixedSigner.SignMapRoot(root) 43 if err != nil { 44 panic(fmt.Sprintf("SignMapRoot(): %v", err)) 45 } 46 return r 47 } 48 49 func TestMySQLMapStorage_CheckDatabaseAccessible(t *testing.T) { 50 testdb.SkipIfNoMySQL(t) 51 52 cleanTestDB(DB) 53 s := NewMapStorage(DB) 54 if err := s.CheckDatabaseAccessible(context.Background()); err != nil { 55 t.Errorf("CheckDatabaseAccessible() = %v, want = nil", err) 56 } 57 } 58 59 func TestMapSnapshot(t *testing.T) { 60 testdb.SkipIfNoMySQL(t) 61 62 cleanTestDB(DB) 63 ctx := context.Background() 64 65 frozenMap := createInitializedMapForTests(ctx, t, DB) 66 updateTree(DB, frozenMap.TreeId, func(tree *trillian.Tree) { 67 tree.TreeState = trillian.TreeState_FROZEN 68 }) 69 70 activeMap := createInitializedMapForTests(ctx, t, DB) 71 logID := createTreeOrPanic(DB, storageto.LogTree).TreeId 72 73 tests := []struct { 74 desc string 75 tree *trillian.Tree 76 wantErr bool 77 }{ 78 { 79 desc: "unknownSnapshot", 80 tree: mapTree(-1), 81 wantErr: true, 82 }, 83 { 84 desc: "activeMapSnapshot", 85 tree: activeMap, 86 }, 87 { 88 desc: "frozenSnapshot", 89 tree: frozenMap, 90 }, 91 { 92 desc: "logSnapshot", 93 tree: mapTree(logID), 94 wantErr: true, 95 }, 96 } 97 98 s := NewMapStorage(DB) 99 for _, test := range tests { 100 t.Run(test.desc, func(t *testing.T) { 101 tx, err := s.SnapshotForTree(ctx, test.tree) 102 if err != nil { 103 t.Fatalf("SnapshotForTree()=_,%v; want _, nil", err) 104 } 105 defer tx.Close() 106 107 _, err = tx.LatestSignedMapRoot(ctx) 108 if gotErr := (err != nil); gotErr != test.wantErr { 109 t.Errorf("LatestSignedMapRoot()=_,%v; want _, err? %v", err, test.wantErr) 110 } 111 if err != nil { 112 return 113 } 114 if err := tx.Commit(); err != nil { 115 t.Errorf("Commit()=_,%v; want _,nil", err) 116 } 117 }) 118 } 119 } 120 121 func TestMapReadWriteTransaction(t *testing.T) { 122 testdb.SkipIfNoMySQL(t) 123 124 cleanTestDB(DB) 125 ctx := context.Background() 126 activeMap := createInitializedMapForTests(ctx, t, DB) 127 128 tests := []struct { 129 desc string 130 tree *trillian.Tree 131 wantRev int64 132 wantTXRev int64 133 wantErr bool 134 wantRootErr string 135 }{ 136 { 137 desc: "unknownBegin", 138 tree: mapTree(-1), 139 wantRev: 0, 140 wantTXRev: -1, 141 wantRootErr: "needs initialising", 142 }, 143 { 144 desc: "activeMapBegin", 145 tree: activeMap, 146 wantRev: 0, 147 wantTXRev: 1, 148 }, 149 } 150 151 s := NewMapStorage(DB) 152 for _, test := range tests { 153 t.Run(test.desc, func(t *testing.T) { 154 err := s.ReadWriteTransaction(ctx, test.tree, func(ctx context.Context, tx storage.MapTreeTX) error { 155 root, err := tx.LatestSignedMapRoot(ctx) 156 if err != nil { 157 if !strings.Contains(err.Error(), test.wantRootErr) { 158 t.Errorf("LatestSignedMapRoot() returned err = %v", err) 159 } 160 return nil 161 } 162 if len(test.wantRootErr) != 0 { 163 t.Fatalf("LatestSignedMapRoot() returned err = %v, want: nil", err) 164 } 165 var mapRoot types.MapRootV1 166 if err := mapRoot.UnmarshalBinary(root.MapRoot); err != nil { 167 t.Fatalf("UmarshalBinary(): %v", err) 168 } 169 if got, want := tx.WriteRevision(), test.wantTXRev; got != want { 170 t.Errorf("WriteRevision() = %v, want = %v", got, want) 171 } 172 if got, want := int64(mapRoot.Revision), test.wantRev; got != want { 173 t.Errorf("TreeRevision() = %v, want = %v", got, want) 174 } 175 return nil 176 }) 177 if hasErr := err != nil; hasErr != test.wantErr { 178 t.Fatalf("err = %q, wantErr = %v", err, test.wantErr) 179 } else if hasErr { 180 return 181 } 182 }) 183 } 184 } 185 186 func TestMapRootUpdate(t *testing.T) { 187 testdb.SkipIfNoMySQL(t) 188 189 cleanTestDB(DB) 190 ctx := context.Background() 191 tree := createInitializedMapForTests(ctx, t, DB) 192 s := NewMapStorage(DB) 193 194 populatedMetadata := testonly.MustMarshalAnyNoT(&ctmapperpb.MapperMetadata{HighestFullyCompletedSeq: 1}) 195 196 for _, tc := range []struct { 197 desc string 198 root *trillian.SignedMapRoot 199 wantMetadata []byte 200 }{ 201 { 202 desc: "Initial root", 203 root: MustSignMapRoot(&types.MapRootV1{ 204 TimestampNanos: 98765, 205 Revision: 5, 206 RootHash: []byte(dummyHash), 207 }), 208 }, 209 { 210 desc: "Root update", 211 root: MustSignMapRoot(&types.MapRootV1{ 212 TimestampNanos: 98766, 213 Revision: 6, 214 RootHash: []byte(dummyHash), 215 }), 216 }, 217 { 218 desc: "Root with default (empty) MapperMetadata", 219 root: MustSignMapRoot(&types.MapRootV1{ 220 TimestampNanos: 98768, 221 Revision: 7, 222 RootHash: []byte(dummyHash), 223 }), 224 }, 225 { 226 desc: "Root with non-default (populated) MapperMetadata", 227 root: MustSignMapRoot(&types.MapRootV1{ 228 TimestampNanos: 98769, 229 Revision: 8, 230 RootHash: []byte(dummyHash), 231 Metadata: populatedMetadata, 232 }), 233 wantMetadata: populatedMetadata, 234 }, 235 } { 236 func() { 237 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 238 if err := tx.StoreSignedMapRoot(ctx, *tc.root); err != nil { 239 t.Fatalf("%v: Failed to store signed map root: %v", tc.desc, err) 240 } 241 return nil 242 }) 243 }() 244 245 func() { 246 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 247 smr, err := tx.LatestSignedMapRoot(ctx) 248 if err != nil { 249 t.Fatalf("%v: Failed to read back new map root: %v", tc.desc, err) 250 } 251 252 var root types.MapRootV1 253 if err := root.UnmarshalBinary(smr.MapRoot); err != nil { 254 t.Fatalf("%v: UnmarshalBinary(): %v", tc.desc, err) 255 } 256 257 if got, want := root.Metadata, tc.wantMetadata; !bytes.Equal(got, want) { 258 t.Errorf("%v: LatestSignedMapRoot() diff(-got, +want) \n%v", tc.desc, pretty.Compare(got, want)) 259 } 260 return nil 261 }) 262 }() 263 } 264 } 265 266 var keyHash = []byte([]byte("A Key Hash")) 267 var mapLeaf = trillian.MapLeaf{ 268 Index: keyHash, 269 LeafHash: []byte("A Hash"), 270 LeafValue: []byte("A Value"), 271 ExtraData: []byte("Some Extra Data"), 272 } 273 274 func TestMapSetGetRoundTrip(t *testing.T) { 275 testdb.SkipIfNoMySQL(t) 276 277 cleanTestDB(DB) 278 ctx := context.Background() 279 tree := createInitializedMapForTests(ctx, t, DB) 280 s := NewMapStorage(DB) 281 282 readRev := int64(1) 283 { 284 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 285 if err := tx.Set(ctx, keyHash, mapLeaf); err != nil { 286 t.Fatalf("Failed to set %v to %v: %v", keyHash, mapLeaf, err) 287 } 288 return nil 289 }) 290 } 291 292 { 293 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 294 readValues, err := tx.Get(ctx, readRev, [][]byte{keyHash}) 295 if err != nil { 296 t.Fatalf("Failed to get %v: %v", keyHash, err) 297 } 298 if got, want := len(readValues), 1; got != want { 299 t.Fatalf("Got %d values, expected %d", got, want) 300 } 301 if got, want := &readValues[0], &mapLeaf; !proto.Equal(got, want) { 302 t.Fatalf("Read back %v, but expected %v", got, want) 303 } 304 return nil 305 }) 306 } 307 } 308 309 func TestMapSetSameKeyInSameRevisionFails(t *testing.T) { 310 testdb.SkipIfNoMySQL(t) 311 312 cleanTestDB(DB) 313 ctx := context.Background() 314 tree := createInitializedMapForTests(ctx, t, DB) 315 s := NewMapStorage(DB) 316 317 { 318 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 319 if err := tx.Set(ctx, keyHash, mapLeaf); err != nil { 320 t.Fatalf("Failed to set %v to %v: %v", keyHash, mapLeaf, err) 321 } 322 return nil 323 }) 324 } 325 326 { 327 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 328 if err := tx.Set(ctx, keyHash, mapLeaf); err == nil { 329 t.Fatalf("Unexpectedly succeeded in setting %v to %v", keyHash, mapLeaf) 330 } 331 return nil 332 }) 333 } 334 } 335 336 func TestMapGet0Results(t *testing.T) { 337 testdb.SkipIfNoMySQL(t) 338 339 cleanTestDB(DB) 340 ctx := context.Background() 341 tree := createInitializedMapForTests(ctx, t, DB) 342 s := NewMapStorage(DB) 343 344 for _, tc := range []struct { 345 index [][]byte 346 }{ 347 {index: nil}, //empty list. 348 {index: [][]byte{[]byte("This doesn't exist.")}}, 349 } { 350 t.Run(fmt.Sprintf("tx.Get(%s)", tc.index), func(t *testing.T) { 351 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 352 readValues, err := tx.Get(ctx, 1, tc.index) 353 if err != nil { 354 t.Fatal(err) 355 } 356 if got, want := len(readValues), 0; got != want { 357 t.Fatalf("len = %d, want %d", got, want) 358 } 359 return nil 360 }) 361 }) 362 } 363 } 364 365 func TestMapSetGetMultipleRevisions(t *testing.T) { 366 testdb.SkipIfNoMySQL(t) 367 368 // Write two roots for a map and make sure the one with the newest timestamp supersedes 369 cleanTestDB(DB) 370 ctx := context.Background() 371 tree := createInitializedMapForTests(ctx, t, DB) 372 s := NewMapStorage(DB) 373 374 tests := []struct { 375 rev int64 376 leaf trillian.MapLeaf 377 }{ 378 {0, trillian.MapLeaf{Index: keyHash, LeafHash: []byte{0}, LeafValue: []byte{0}, ExtraData: []byte{0}}}, 379 {1, trillian.MapLeaf{Index: keyHash, LeafHash: []byte{1}, LeafValue: []byte{1}, ExtraData: []byte{1}}}, 380 {2, trillian.MapLeaf{Index: keyHash, LeafHash: []byte{2}, LeafValue: []byte{2}, ExtraData: []byte{2}}}, 381 {3, trillian.MapLeaf{Index: keyHash, LeafHash: []byte{3}, LeafValue: []byte{3}, ExtraData: []byte{3}}}, 382 } 383 384 for _, tc := range tests { 385 func() { 386 // Write the current test case. 387 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 388 mapTX := tx.(*mapTreeTX) 389 mapTX.treeTX.writeRevision = tc.rev 390 if err := tx.Set(ctx, keyHash, tc.leaf); err != nil { 391 t.Fatalf("Failed to set %v to %v: %v", keyHash, tc.leaf, err) 392 } 393 return nil 394 }) 395 396 // Read at a point in time in the future. Expect to get the latest value. 397 // Read at each point in the past. Expect to get that exact point in history. 398 for i := int64(0); i < int64(len(tests)); i++ { 399 func() { 400 expectRev := i 401 if expectRev > tc.rev { 402 expectRev = tc.rev // For future revisions, expect the current value. 403 } 404 405 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx2 storage.MapTreeTX) error { 406 readValues, err := tx2.Get(ctx, i, [][]byte{keyHash}) 407 if err != nil { 408 t.Fatalf("At i %d failed to get %v: %v", i, keyHash, err) 409 } 410 if got, want := len(readValues), 1; got != want { 411 t.Fatalf("At i %d got %d values, expected %d", i, got, want) 412 } 413 if got, want := &readValues[0], &tests[expectRev].leaf; !proto.Equal(got, want) { 414 t.Fatalf("At i %d read back %v, but expected %v", i, got, want) 415 } 416 return nil 417 }) 418 }() 419 } 420 }() 421 } 422 } 423 424 func TestGetSignedMapRootNotExist(t *testing.T) { 425 testdb.SkipIfNoMySQL(t) 426 427 cleanTestDB(DB) 428 tree := createTreeOrPanic(DB, storageto.MapTree) // Uninitialized: no revision 0 MapRoot exists. 429 s := NewMapStorage(DB) 430 431 ctx := context.Background() 432 err := s.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.MapTreeTX) error { 433 _, err := tx.GetSignedMapRoot(ctx, 0) 434 if got, want := err, storage.ErrTreeNeedsInit; got != want { 435 t.Fatalf("GetSignedMapRoot: %v, want %v", got, want) 436 } 437 return nil 438 }) 439 if err != nil { 440 t.Fatalf("ReadWriteTransaction: %v", err) 441 } 442 } 443 444 func TestLatestSignedMapRootNoneWritten(t *testing.T) { 445 // TODO(phad): I'm considering removing this test, because for an Map that has been 446 // initialized there should always be the revision 0 SMR written to the DB, and 447 // without initialization the error path is identical to that tested in the func 448 // TestGetSignedMapRootNotExist above. 449 t.Skip("TODO: remove this as it can no longer occur.") 450 451 cleanTestDB(DB) 452 ctx := context.Background() 453 tree := createInitializedMapForTests(ctx, t, DB) 454 s := NewMapStorage(DB) 455 456 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 457 root, err := tx.LatestSignedMapRoot(ctx) 458 if err != nil { 459 t.Fatalf("Failed to read an empty map root: %v", err) 460 } 461 if len(root.MapRoot) != 0 || root.Signature != nil { 462 t.Fatalf("Read a root with contents when it should be empty: %v", root) 463 } 464 return nil 465 }) 466 } 467 468 func TestGetSignedMapRoot(t *testing.T) { 469 testdb.SkipIfNoMySQL(t) 470 471 cleanTestDB(DB) 472 ctx := context.Background() 473 tree := createInitializedMapForTests(ctx, t, DB) 474 s := NewMapStorage(DB) 475 476 revision := int64(5) 477 root := MustSignMapRoot(&types.MapRootV1{ 478 TimestampNanos: 98765, 479 Revision: uint64(revision), 480 RootHash: []byte(dummyHash), 481 }) 482 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 483 if err := tx.StoreSignedMapRoot(ctx, *root); err != nil { 484 t.Fatalf("Failed to store signed root: %v", err) 485 } 486 return nil 487 }) 488 489 { 490 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx2 storage.MapTreeTX) error { 491 root2, err := tx2.GetSignedMapRoot(ctx, revision) 492 if err != nil { 493 t.Fatalf("Failed to get back new map root: %v", err) 494 } 495 if !proto.Equal(root, &root2) { 496 t.Fatalf("Getting root round trip failed: <%#v> and: <%#v>", root, root2) 497 } 498 return nil 499 }) 500 } 501 } 502 503 func TestLatestSignedMapRoot(t *testing.T) { 504 testdb.SkipIfNoMySQL(t) 505 506 cleanTestDB(DB) 507 ctx := context.Background() 508 tree := createInitializedMapForTests(ctx, t, DB) 509 s := NewMapStorage(DB) 510 511 root := MustSignMapRoot(&types.MapRootV1{ 512 TimestampNanos: 98765, 513 Revision: 5, 514 RootHash: []byte(dummyHash), 515 }) 516 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 517 if err := tx.StoreSignedMapRoot(ctx, *root); err != nil { 518 t.Fatalf("Failed to store signed root: %v", err) 519 } 520 return nil 521 }) 522 523 { 524 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx2 storage.MapTreeTX) error { 525 root2, err := tx2.LatestSignedMapRoot(ctx) 526 if err != nil { 527 t.Fatalf("Failed to read back new map root: %v", err) 528 } 529 if !proto.Equal(root, &root2) { 530 t.Fatalf("Root round trip failed: <%#v> and: <%#v>", root, root2) 531 } 532 return nil 533 }) 534 } 535 } 536 537 func TestDuplicateSignedMapRoot(t *testing.T) { 538 testdb.SkipIfNoMySQL(t) 539 540 cleanTestDB(DB) 541 ctx := context.Background() 542 tree := createInitializedMapForTests(ctx, t, DB) 543 s := NewMapStorage(DB) 544 545 runMapTX(ctx, s, tree, t, func(ctx context.Context, tx storage.MapTreeTX) error { 546 root := MustSignMapRoot(&types.MapRootV1{ 547 TimestampNanos: 98765, 548 Revision: 5, 549 RootHash: []byte(dummyHash), 550 }) 551 if err := tx.StoreSignedMapRoot(ctx, *root); err != nil { 552 t.Fatalf("Failed to store signed map root: %v", err) 553 } 554 // Shouldn't be able to do it again 555 if err := tx.StoreSignedMapRoot(ctx, *root); err == nil { 556 t.Fatal("Allowed duplicate signed map root") 557 } 558 return nil 559 }) 560 } 561 562 func TestReadOnlyMapTX_Rollback(t *testing.T) { 563 testdb.SkipIfNoMySQL(t) 564 565 cleanTestDB(DB) 566 s := NewMapStorage(DB) 567 tx, err := s.Snapshot(context.Background()) 568 if err != nil { 569 t.Fatalf("Snapshot() = (_, %v), want = (_, nil)", err) 570 } 571 defer tx.Close() 572 // It's a bit hard to have a more meaningful test. This should suffice. 573 if err := tx.Rollback(); err != nil { 574 t.Errorf("Rollback() = (_, %v), want = (_, nil)", err) 575 } 576 } 577 578 func runMapTX(ctx context.Context, s storage.MapStorage, tree *trillian.Tree, t *testing.T, f storage.MapTXFunc) { 579 if err := s.ReadWriteTransaction(ctx, tree, f); err != nil { 580 t.Fatalf("Failed to begin map tx: %v", err) 581 } 582 } 583 584 func createInitializedMapForTests(ctx context.Context, t *testing.T, db *sql.DB) *trillian.Tree { 585 t.Helper() 586 tree := createTreeOrPanic(db, storageto.MapTree) 587 588 s := NewMapStorage(db) 589 signer := tcrypto.NewSigner(tree.TreeId, testonly.NewSignerWithFixedSig(nil, []byte("sig")), crypto.SHA256) 590 err := s.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.MapTreeTX) error { 591 initialRoot, _ := signer.SignMapRoot(&types.MapRootV1{ 592 RootHash: []byte("rootHash"), 593 Revision: 0, 594 }) 595 596 if err := tx.StoreSignedMapRoot(ctx, *initialRoot); err != nil { 597 t.Fatalf("Failed to StoreSignedMapRoot: %v", err) 598 } 599 return nil 600 }) 601 if err != nil { 602 t.Fatalf("ReadWriteTransaction() = %v", err) 603 } 604 605 return tree 606 } 607 608 func mapTree(mapID int64) *trillian.Tree { 609 return &trillian.Tree{ 610 TreeId: mapID, 611 TreeType: trillian.TreeType_MAP, 612 HashStrategy: trillian.HashStrategy_TEST_MAP_HASHER, 613 } 614 }