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  }