github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/log/sequencer_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 log 16 17 import ( 18 "context" 19 "crypto" 20 "errors" 21 "fmt" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/golang/mock/gomock" 27 "github.com/google/trillian" 28 "github.com/google/trillian/crypto/keys/pem" 29 "github.com/google/trillian/merkle/rfc6962" 30 "github.com/google/trillian/quota" 31 "github.com/google/trillian/storage" 32 "github.com/google/trillian/testonly" 33 "github.com/google/trillian/types" 34 "github.com/google/trillian/util" 35 36 tcrypto "github.com/google/trillian/crypto" 37 stestonly "github.com/google/trillian/storage/testonly" 38 ) 39 40 var ( 41 // These can be shared between tests as they're never modified 42 testLeaf16Data = []byte("testdataforleaf") 43 testLeaf16Hash, _ = rfc6962.DefaultHasher.HashLeaf(testLeaf16Data) 44 testLeaf16 = &trillian.LogLeaf{ 45 MerkleLeafHash: testLeaf16Hash, 46 LeafValue: testLeaf16Data, 47 ExtraData: nil, 48 LeafIndex: 16, 49 IntegrateTimestamp: testonly.MustToTimestampProto(fakeTime()), 50 } 51 52 testRoot16 = &types.LogRootV1{ 53 TreeSize: 16, 54 Revision: 5, 55 // RootHash can't be nil because that's how the sequencer currently 56 // detects that there was no stored tree head. 57 RootHash: []byte{}, 58 TimestampNanos: uint64(fakeTimeForTest.Add(-10 * time.Millisecond).UnixNano()), 59 } 60 61 fixedSigner = newSignerWithFixedSig([]byte("signed")) 62 testSignedRoot16, _ = tcrypto.NewSigner(0, fixedSigner, crypto.SHA256).SignLogRoot(testRoot16) 63 newSignedRoot16, _ = tcrypto.NewSigner(0, fixedSigner, crypto.SHA256). 64 SignLogRoot(&types.LogRootV1{ 65 TimestampNanos: uint64(fakeTimeForTest.UnixNano()), 66 TreeSize: testRoot16.TreeSize, 67 Revision: testRoot16.Revision + 1, 68 RootHash: testRoot16.RootHash, 69 }) 70 71 testRoot17 = &types.LogRootV1{ 72 TreeSize: 16, 73 Revision: 5, 74 // RootHash can't be nil because that's how the sequencer currently 75 // detects that there was no stored tree head. 76 RootHash: []byte{}, 77 TimestampNanos: uint64(fakeTimeForTest.UnixNano()), 78 } 79 testSignedRoot17, _ = tcrypto.NewSigner(0, fixedSigner, crypto.SHA256).SignLogRoot(testRoot17) 80 81 testRoot18 = &types.LogRootV1{ 82 TreeSize: 16, 83 Revision: 5, 84 // RootHash can't be nil because that's how the sequencer currently 85 // detects that there was no stored tree head. 86 RootHash: []byte{}, 87 TimestampNanos: uint64(fakeTimeForTest.Add(10 * time.Millisecond).UnixNano()), 88 } 89 testSignedRoot18, _ = tcrypto.NewSigner(0, fixedSigner, crypto.SHA256).SignLogRoot(testRoot18) 90 91 // These will be accepted in either order because of custom sorting in the mock 92 updatedNodes = []storage.Node{ 93 { 94 NodeID: storage.NodeID{Path: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, PrefixLenBits: 64}, 95 Hash: testonly.MustDecodeBase64("L5Iyd7aFOVewxiRm29xD+EU+jvEo4RfufBijKdflWMk="), 96 NodeRevision: 6, 97 }, 98 { 99 NodeID: storage.NodeID{Path: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, PrefixLenBits: 59}, 100 Hash: testonly.MustDecodeBase64("R57DrKTGuZdjCNXjv6InGrm4rABLOn9yWpdHmYOoLwU="), 101 NodeRevision: 6, 102 }, 103 } 104 105 testRoot = &types.LogRootV1{ 106 RootHash: []byte{71, 158, 195, 172, 164, 198, 185, 151, 99, 8, 213, 227, 191, 162, 39, 26, 185, 184, 172, 0, 75, 58, 127, 114, 90, 151, 71, 153, 131, 168, 47, 5}, 107 TimestampNanos: uint64(fakeTimeForTest.UnixNano()), 108 Revision: 6, 109 TreeSize: 17, 110 } 111 testSignedRoot, _ = tcrypto.NewSigner(0, fixedSigner, crypto.SHA256).SignLogRoot(testRoot) 112 ) 113 114 var fakeTimeForTest = fakeTime() 115 116 // Any tests relying on time should use this fixed value 117 const fakeTimeStr string = "2016-05-25T10:55:05Z" 118 119 // testParameters bundles up values needed for setting mock expectations in tests 120 type testParameters struct { 121 logID int64 122 signer crypto.Signer 123 124 beginFails bool 125 dequeueLimit int 126 127 shouldCommit bool 128 commitFails bool 129 commitError error 130 131 skipDequeue bool 132 dequeuedLeaves []*trillian.LogLeaf 133 dequeuedError error 134 135 latestSignedRootError error 136 latestSignedRoot *trillian.SignedLogRoot 137 138 updatedLeaves *[]*trillian.LogLeaf 139 updatedLeavesError error 140 141 merkleNodesSet *[]storage.Node 142 merkleNodesSetError error 143 144 skipStoreSignedRoot bool 145 storeSignedRoot *trillian.SignedLogRoot 146 storeSignedRootError error 147 148 writeRevision int64 149 150 overrideDequeueTime *time.Time 151 152 // qm is the quota.Manager to be used. If nil, quota.Noop() is used instead. 153 qm quota.Manager 154 } 155 156 // Tests get their own mock context so they can be run in parallel safely 157 type testContext struct { 158 mockTx *storage.MockLogTreeTX 159 fakeStorage storage.LogStorage 160 signer *tcrypto.Signer 161 sequencer *Sequencer 162 } 163 164 // This gets modified so tests need their own copies 165 func getLeaf42() *trillian.LogLeaf { 166 return &trillian.LogLeaf{ 167 MerkleLeafHash: testLeaf16Hash, 168 LeafValue: testLeaf16Data, 169 ExtraData: nil, 170 LeafIndex: 42, 171 } 172 } 173 174 func fakeTime() time.Time { 175 fakeTimeForTest, err := time.Parse(time.RFC3339, fakeTimeStr) 176 177 if err != nil { 178 panic(fmt.Sprintf("Test has an invalid fake time: %s", err)) 179 } 180 181 return fakeTimeForTest 182 } 183 184 func newSignerWithFixedSig(sig []byte) crypto.Signer { 185 key, err := pem.UnmarshalPublicKey(testonly.DemoPublicKey) 186 if err != nil { 187 panic(err) 188 } 189 190 return testonly.NewSignerWithFixedSig(key, sig) 191 } 192 193 func newSignerWithErr(signErr error) (crypto.Signer, error) { 194 key, err := pem.UnmarshalPublicKey(testonly.DemoPublicKey) 195 if err != nil { 196 return nil, err 197 } 198 199 return testonly.NewSignerWithErr(key, signErr), nil 200 } 201 202 func createTestContext(ctrl *gomock.Controller, params testParameters) (testContext, context.Context) { 203 fakeStorage := &stestonly.FakeLogStorage{} 204 mockTx := storage.NewMockLogTreeTX(ctrl) 205 206 mockTx.EXPECT().WriteRevision().AnyTimes().Return(params.writeRevision) 207 if params.beginFails { 208 fakeStorage.TXErr = errors.New("TX") 209 } else { 210 mockTx.EXPECT().Close() 211 fakeStorage.TX = mockTx 212 } 213 214 if params.shouldCommit { 215 if !params.commitFails { 216 mockTx.EXPECT().Commit().Return(nil) 217 } else { 218 mockTx.EXPECT().Commit().Return(params.commitError) 219 } 220 } 221 // Close is always called, regardless of explicit commits 222 mockTx.EXPECT().Close().AnyTimes().Return(nil) 223 224 if !params.skipDequeue { 225 if params.overrideDequeueTime != nil { 226 mockTx.EXPECT().DequeueLeaves(gomock.Any(), params.dequeueLimit, *params.overrideDequeueTime).Return(params.dequeuedLeaves, params.dequeuedError) 227 } else { 228 mockTx.EXPECT().DequeueLeaves(gomock.Any(), params.dequeueLimit, fakeTimeForTest).Return(params.dequeuedLeaves, params.dequeuedError) 229 } 230 } 231 232 if params.latestSignedRoot != nil { 233 mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(*params.latestSignedRoot, params.latestSignedRootError) 234 } 235 236 if params.updatedLeaves != nil { 237 mockTx.EXPECT().UpdateSequencedLeaves(gomock.Any(), *params.updatedLeaves).Return(params.updatedLeavesError) 238 } 239 240 if params.merkleNodesSet != nil { 241 mockTx.EXPECT().SetMerkleNodes(gomock.Any(), stestonly.NodeSet(*params.merkleNodesSet)).Return(params.merkleNodesSetError) 242 } 243 244 if !params.skipStoreSignedRoot { 245 if params.storeSignedRoot != nil { 246 mockTx.EXPECT().StoreSignedLogRoot(gomock.Any(), *params.storeSignedRoot).Return(params.storeSignedRootError) 247 } else { 248 // At the moment if we're going to fail the operation we accept any root 249 mockTx.EXPECT().StoreSignedLogRoot(gomock.Any(), gomock.Any()).Return(params.storeSignedRootError) 250 } 251 } 252 253 signer := tcrypto.NewSigner(0, params.signer, crypto.SHA256) 254 qm := params.qm 255 if qm == nil { 256 qm = quota.Noop() 257 } 258 sequencer := NewSequencer(rfc6962.DefaultHasher, util.NewFakeTimeSource(fakeTimeForTest), fakeStorage, signer, nil, qm) 259 return testContext{mockTx: mockTx, fakeStorage: fakeStorage, signer: signer, sequencer: sequencer}, context.Background() 260 } 261 262 // Tests for sequencer. Currently relies on having a database set up. This might change in future 263 // as it would be better if it was not tied to a specific storage mechanism. 264 265 func TestIntegrateBatch(t *testing.T) { 266 signerErr, err := newSignerWithErr(errors.New("signerfailed")) 267 if err != nil { 268 t.Fatalf("Failed to create test signer (%v)", err) 269 } 270 leaves16 := []*trillian.LogLeaf{testLeaf16} 271 guardWindow := time.Second * 10 272 expectedCutoffTime := fakeTimeForTest.Add(-guardWindow) 273 noLeaves := []*trillian.LogLeaf{} 274 noNodes := []storage.Node{} 275 specs := []quota.Spec{ 276 {Group: quota.Tree, Kind: quota.Read, TreeID: 154035}, 277 {Group: quota.Tree, Kind: quota.Write, TreeID: 154035}, 278 {Group: quota.Global, Kind: quota.Read}, 279 {Group: quota.Global, Kind: quota.Write}, 280 } 281 282 var tests = []struct { 283 desc string 284 params testParameters 285 guardWindow time.Duration 286 maxRootDuration time.Duration 287 wantCount int 288 errStr string 289 }{ 290 { 291 desc: "begin-tx-fails", 292 params: testParameters{ 293 logID: 154035, 294 beginFails: true, 295 skipDequeue: true, 296 skipStoreSignedRoot: true, 297 }, 298 errStr: "TX", 299 }, 300 { 301 desc: "nothing-queued-no-max", 302 params: testParameters{ 303 logID: 154035, 304 dequeueLimit: 1, 305 shouldCommit: true, 306 latestSignedRoot: testSignedRoot16, 307 dequeuedLeaves: noLeaves, 308 skipStoreSignedRoot: true, 309 }, 310 }, 311 { 312 desc: "nothing-queued-within-max", 313 params: testParameters{ 314 logID: 154035, 315 dequeueLimit: 1, 316 shouldCommit: true, 317 latestSignedRoot: testSignedRoot16, 318 dequeuedLeaves: noLeaves, 319 skipStoreSignedRoot: true, 320 }, 321 maxRootDuration: 15 * time.Millisecond, 322 }, 323 { 324 desc: "nothing-queued-after-max", 325 params: testParameters{ 326 logID: 154035, 327 dequeueLimit: 1, 328 shouldCommit: true, 329 latestSignedRoot: testSignedRoot16, 330 dequeuedLeaves: noLeaves, 331 writeRevision: int64(testRoot16.Revision + 1), 332 updatedLeaves: &noLeaves, 333 merkleNodesSet: &noNodes, 334 signer: fixedSigner, 335 storeSignedRoot: newSignedRoot16, 336 }, 337 maxRootDuration: 9 * time.Millisecond, 338 }, 339 { 340 desc: "nothing-queued-on-max", 341 params: testParameters{ 342 logID: 154035, 343 dequeueLimit: 1, 344 shouldCommit: true, 345 latestSignedRoot: testSignedRoot16, 346 dequeuedLeaves: noLeaves, 347 writeRevision: int64(testRoot16.Revision + 1), 348 updatedLeaves: &noLeaves, 349 merkleNodesSet: &noNodes, 350 signer: fixedSigner, 351 storeSignedRoot: newSignedRoot16, 352 }, 353 maxRootDuration: 10 * time.Millisecond, 354 }, 355 { 356 // Tests that the guard interval is being passed to storage correctly. 357 // Actual operation of the window is tested by storage tests. 358 desc: "guard-interval", 359 params: testParameters{ 360 logID: 154035, 361 dequeueLimit: 1, 362 shouldCommit: true, 363 latestSignedRoot: testSignedRoot16, 364 dequeuedLeaves: []*trillian.LogLeaf{}, 365 skipStoreSignedRoot: true, 366 overrideDequeueTime: &expectedCutoffTime, 367 }, 368 guardWindow: guardWindow, 369 }, 370 { 371 desc: "dequeue-fails", 372 params: testParameters{ 373 logID: 154035, 374 dequeueLimit: 1, 375 latestSignedRoot: testSignedRoot16, 376 dequeuedError: errors.New("dequeue"), 377 skipStoreSignedRoot: true, 378 }, 379 errStr: "dequeue", 380 }, 381 { 382 desc: "get-signed-root-fails", 383 params: testParameters{ 384 logID: 154035, 385 dequeueLimit: 1, 386 latestSignedRoot: testSignedRoot16, 387 latestSignedRootError: errors.New("root"), 388 skipDequeue: true, 389 skipStoreSignedRoot: true, 390 }, 391 errStr: "root", 392 }, 393 { 394 desc: "update-seq-leaves-fails", 395 params: testParameters{ 396 logID: 154035, 397 writeRevision: int64(testRoot16.Revision + 1), 398 dequeueLimit: 1, 399 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 400 latestSignedRoot: testSignedRoot16, 401 updatedLeaves: &leaves16, 402 updatedLeavesError: errors.New("unsequenced"), 403 skipStoreSignedRoot: true, 404 }, 405 errStr: "unsequenced", 406 }, 407 { 408 desc: "set-merkle-nodes-fails", 409 params: testParameters{ 410 logID: 154035, 411 writeRevision: int64(testRoot16.Revision + 1), 412 dequeueLimit: 1, 413 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 414 latestSignedRoot: testSignedRoot16, 415 updatedLeaves: &leaves16, 416 merkleNodesSet: &updatedNodes, 417 merkleNodesSetError: errors.New("setmerklenodes"), 418 skipStoreSignedRoot: true, 419 }, 420 errStr: "setmerklenodes", 421 }, 422 { 423 desc: "store-root-fails", 424 params: testParameters{ 425 logID: 154035, 426 writeRevision: int64(testRoot16.Revision + 1), 427 dequeueLimit: 1, 428 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 429 latestSignedRoot: testSignedRoot16, 430 updatedLeaves: &leaves16, 431 merkleNodesSet: &updatedNodes, 432 storeSignedRoot: nil, 433 storeSignedRootError: errors.New("storesignedroot"), 434 signer: fixedSigner, 435 }, 436 errStr: "storesignedroot", 437 }, 438 { 439 desc: "signer-fails", 440 params: testParameters{ 441 logID: 154035, 442 writeRevision: int64(testRoot16.Revision + 1), 443 dequeueLimit: 1, 444 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 445 latestSignedRoot: testSignedRoot16, 446 updatedLeaves: &leaves16, 447 merkleNodesSet: &updatedNodes, 448 storeSignedRoot: nil, 449 signer: signerErr, 450 skipStoreSignedRoot: true, 451 }, 452 errStr: "signerfailed", 453 }, 454 { 455 desc: "commit-fails", 456 params: testParameters{ 457 logID: 154035, 458 writeRevision: int64(testRoot16.Revision + 1), 459 dequeueLimit: 1, 460 shouldCommit: true, 461 commitFails: true, 462 commitError: errors.New("commit"), 463 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 464 latestSignedRoot: testSignedRoot16, 465 updatedLeaves: &leaves16, 466 merkleNodesSet: &updatedNodes, 467 storeSignedRoot: nil, 468 signer: fixedSigner, 469 }, 470 errStr: "commit", 471 }, 472 { 473 desc: "sequence-leaf-16", 474 params: testParameters{ 475 logID: 154035, 476 writeRevision: int64(testRoot16.Revision + 1), 477 dequeueLimit: 1, 478 shouldCommit: true, 479 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 480 latestSignedRoot: testSignedRoot16, 481 updatedLeaves: &leaves16, 482 merkleNodesSet: &updatedNodes, 483 storeSignedRoot: testSignedRoot, 484 signer: fixedSigner, 485 }, 486 wantCount: 1, 487 }, 488 { 489 desc: "prev-root-timestamp-equals", 490 params: testParameters{ 491 logID: 154035, 492 writeRevision: int64(testRoot16.Revision + 1), 493 dequeueLimit: 1, 494 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 495 latestSignedRoot: testSignedRoot17, 496 updatedLeaves: &leaves16, 497 merkleNodesSet: &updatedNodes, 498 skipStoreSignedRoot: true, 499 }, 500 errStr: "refusing to sign root with timestamp earlier than previous root (1464173705000000000 <= 1464173705000000000)", 501 }, 502 { 503 desc: "prev-root-timestamp-in-future", 504 params: testParameters{ 505 logID: 154035, 506 writeRevision: int64(testRoot16.Revision + 1), 507 dequeueLimit: 1, 508 dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, 509 latestSignedRoot: testSignedRoot18, 510 updatedLeaves: &leaves16, 511 merkleNodesSet: &updatedNodes, 512 skipStoreSignedRoot: true, 513 }, 514 errStr: "refusing to sign root with timestamp earlier than previous root (1464173705000000000 <= 1464173705010000000)", 515 }, 516 } 517 518 for _, test := range tests { 519 t.Run(test.desc, func(t *testing.T) { 520 ctrl := gomock.NewController(t) 521 defer ctrl.Finish() 522 523 qm := quota.NewMockManager(ctrl) 524 test.params.qm = qm 525 if test.wantCount > 0 { 526 qm.EXPECT().PutTokens(gomock.Any(), test.wantCount, specs).Return(nil) 527 } 528 c, ctx := createTestContext(ctrl, test.params) 529 tree := &trillian.Tree{TreeId: test.params.logID, TreeType: trillian.TreeType_LOG} 530 531 got, err := c.sequencer.IntegrateBatch(ctx, tree, 1, test.guardWindow, test.maxRootDuration) 532 if err != nil { 533 if test.errStr == "" { 534 t.Errorf("IntegrateBatch(%+v)=%v,%v; want _,nil", test.params, got, err) 535 } else if !strings.Contains(err.Error(), test.errStr) || got != 0 { 536 t.Errorf("IntegrateBatch(%+v)=%v,%v; want 0, error with %q", test.params, got, err, test.errStr) 537 } 538 return 539 } 540 if got != test.wantCount { 541 t.Errorf("IntegrateBatch(%+v)=%v,nil; want %v,nil", test.params, got, test.wantCount) 542 } 543 }) 544 } 545 } 546 547 func TestIntegrateBatch_PutTokens(t *testing.T) { 548 cryptoSigner := newSignerWithFixedSig(testSignedRoot.LogRootSignature) 549 550 ctrl := gomock.NewController(t) 551 defer ctrl.Finish() 552 553 // Needed to create a signer 554 hasher := rfc6962.DefaultHasher 555 ts := util.NewFakeTimeSource(fakeTimeForTest) 556 signer := tcrypto.NewSigner(0, cryptoSigner, crypto.SHA256) 557 558 // Needed for IntegrateBatch calls 559 const treeID int64 = 1234 560 const limit = 1000 561 const guardWindow = 10 * time.Second 562 const maxRootDuration = 1 * time.Hour 563 564 // Expected PutTokens specs 565 specs := []quota.Spec{ 566 {Group: quota.Tree, Kind: quota.Read, TreeID: treeID}, 567 {Group: quota.Tree, Kind: quota.Write, TreeID: treeID}, 568 {Group: quota.Global, Kind: quota.Read}, 569 {Group: quota.Global, Kind: quota.Write}, 570 } 571 572 oneHundredLeaves := make([]*trillian.LogLeaf, 100) 573 for i := range oneHundredLeaves { 574 oneHundredLeaves[i] = &trillian.LogLeaf{ 575 LeafValue: []byte(fmt.Sprintf("leaf-%v", i)), 576 } 577 } 578 579 tests := []struct { 580 desc string 581 leaves []*trillian.LogLeaf 582 quotaFactor float64 583 wantLeaves, wantTokens int 584 }{ 585 {desc: "noLeaves"}, 586 { 587 desc: "singleLeaf", 588 leaves: []*trillian.LogLeaf{getLeaf42()}, 589 wantLeaves: 1, 590 wantTokens: 1, 591 }, 592 { 593 desc: "badFactor", 594 leaves: oneHundredLeaves, 595 quotaFactor: 0.7, // factor <1 is normalized to 1 596 wantLeaves: 100, 597 wantTokens: 100, 598 }, 599 { 600 desc: "factorOne", 601 leaves: oneHundredLeaves, 602 quotaFactor: 1, 603 wantLeaves: 100, 604 wantTokens: 100, 605 }, 606 { 607 desc: "10%-factor", 608 leaves: oneHundredLeaves, 609 quotaFactor: 1.1, 610 wantLeaves: 100, 611 wantTokens: 110, 612 }, 613 } 614 615 any := gomock.Any() 616 ctx := context.Background() 617 for _, test := range tests { 618 func() { 619 if test.quotaFactor != 0 { 620 defer func(qf float64) { 621 QuotaIncreaseFactor = qf 622 }(QuotaIncreaseFactor) 623 QuotaIncreaseFactor = test.quotaFactor 624 } 625 626 // Correctness of operation is tested elsewhere. The focus here is the interaction 627 // between Sequencer and quota.Manager. 628 logTX := storage.NewMockLogTreeTX(ctrl) 629 logTX.EXPECT().DequeueLeaves(any, any, any).Return(test.leaves, nil) 630 logTX.EXPECT().LatestSignedLogRoot(any).Return(*testSignedRoot16, nil) 631 logTX.EXPECT().WriteRevision().AnyTimes().Return(int64(testRoot16.Revision + 1)) 632 logTX.EXPECT().UpdateSequencedLeaves(any, any).AnyTimes().Return(nil) 633 logTX.EXPECT().SetMerkleNodes(any, any).AnyTimes().Return(nil) 634 logTX.EXPECT().StoreSignedLogRoot(any, any).AnyTimes().Return(nil) 635 logTX.EXPECT().Commit().Return(nil) 636 logTX.EXPECT().Close().Return(nil) 637 logStorage := &stestonly.FakeLogStorage{TX: logTX} 638 639 qm := quota.NewMockManager(ctrl) 640 if test.wantTokens > 0 { 641 qm.EXPECT().PutTokens(any, test.wantTokens, specs) 642 } 643 644 sequencer := NewSequencer(hasher, ts, logStorage, signer, nil /* mf */, qm) 645 tree := &trillian.Tree{TreeId: treeID, TreeType: trillian.TreeType_LOG} 646 leaves, err := sequencer.IntegrateBatch(ctx, tree, limit, guardWindow, maxRootDuration) 647 if err != nil { 648 t.Errorf("%v: IntegrateBatch() returned err = %v", test.desc, err) 649 return 650 } 651 if leaves != test.wantLeaves { 652 t.Errorf("%v: IntegrateBatch() returned %v leaves, want = %v", test.desc, leaves, test.wantLeaves) 653 } 654 }() 655 } 656 } 657 658 func TestSignRoot(t *testing.T) { 659 signerErr, err := newSignerWithErr(errors.New("signerfailed")) 660 if err != nil { 661 t.Fatalf("Failed to create test signer (%v)", err) 662 } 663 var tests = []struct { 664 desc string 665 params testParameters 666 errStr string 667 }{ 668 { 669 desc: "begin-tx-fails", 670 params: testParameters{ 671 logID: 154035, 672 beginFails: true, 673 skipDequeue: true, 674 skipStoreSignedRoot: true, 675 }, 676 errStr: "TX", 677 }, 678 { 679 desc: "sign-latest-root-fails", 680 params: testParameters{ 681 logID: 154035, 682 writeRevision: int64(testRoot16.Revision + 1), 683 dequeueLimit: 1, 684 latestSignedRoot: testSignedRoot16, 685 latestSignedRootError: errors.New("root"), 686 skipDequeue: true, 687 skipStoreSignedRoot: true, 688 }, 689 errStr: "root", 690 }, 691 { 692 desc: "signer-fails", 693 params: testParameters{ 694 logID: 154035, 695 writeRevision: int64(testRoot16.Revision + 1), 696 dequeueLimit: 1, 697 latestSignedRoot: testSignedRoot16, 698 storeSignedRoot: nil, 699 signer: signerErr, 700 skipDequeue: true, 701 skipStoreSignedRoot: true, 702 }, 703 errStr: "signer", 704 }, 705 { 706 desc: "store-root-fail", 707 params: testParameters{ 708 logID: 154035, 709 writeRevision: int64(testRoot16.Revision + 1), 710 latestSignedRoot: testSignedRoot16, 711 storeSignedRoot: nil, 712 storeSignedRootError: errors.New("storesignedroot"), 713 signer: fixedSigner, 714 skipDequeue: true, 715 }, 716 errStr: "storesignedroot", 717 }, 718 { 719 desc: "root-commit-fail", 720 params: testParameters{ 721 logID: 154035, 722 writeRevision: int64(testRoot16.Revision + 1), 723 shouldCommit: true, 724 commitFails: true, 725 commitError: errors.New("commit"), 726 latestSignedRoot: testSignedRoot16, 727 storeSignedRoot: nil, 728 signer: fixedSigner, 729 skipDequeue: true, 730 }, 731 errStr: "commit", 732 }, 733 { 734 desc: "existing-root", 735 params: testParameters{ 736 logID: 154035, 737 writeRevision: int64(testRoot16.Revision + 1), 738 latestSignedRoot: testSignedRoot16, 739 storeSignedRoot: newSignedRoot16, 740 signer: fixedSigner, 741 shouldCommit: true, 742 skipDequeue: true, 743 }, 744 }, 745 { 746 desc: "no-existing-root", 747 params: testParameters{ 748 logID: 154035, 749 writeRevision: int64(testRoot16.Revision + 1), 750 latestSignedRoot: &trillian.SignedLogRoot{}, 751 latestSignedRootError: storage.ErrTreeNeedsInit, 752 skipStoreSignedRoot: true, 753 signer: fixedSigner, 754 shouldCommit: false, 755 skipDequeue: true, 756 }, 757 errStr: storage.ErrTreeNeedsInit.Error(), 758 }, 759 } 760 761 for _, test := range tests { 762 t.Run(test.desc, func(t *testing.T) { 763 ctrl := gomock.NewController(t) 764 defer ctrl.Finish() 765 c, ctx := createTestContext(ctrl, test.params) 766 tree := &trillian.Tree{TreeId: test.params.logID, TreeType: trillian.TreeType_LOG} 767 err := c.sequencer.SignRoot(ctx, tree) 768 if test.errStr != "" { 769 if err == nil { 770 t.Errorf("SignRoot(%+v)=nil; want error with %q", test.params, test.errStr) 771 } else if !strings.Contains(err.Error(), test.errStr) { 772 t.Errorf("SignRoot(%+v)=%v; want error with %q", test.params, err, test.errStr) 773 } 774 return 775 } 776 if err != nil { 777 t.Errorf("SignRoot()=%v; want nil", err) 778 } 779 }) 780 } 781 }