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  }