From 8ef86f8800f456f73811776595bef05bcc44ed29 Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Tue, 7 Feb 2017 15:31:10 +0000 Subject: [PATCH 1/2] Recalculate intermediate hashes using current tree. --- merkle/merkle_path.go | 40 ++++++- server/log_rpc_server.go | 34 +++--- server/log_rpc_server_test.go | 172 +++++++++++------------------- storage/mock_storage.go | 92 +++++++++------- storage/mysql/log_storage.go | 14 ++- storage/mysql/log_storage_test.go | 92 ---------------- storage/mysql/map_storage.go | 11 +- storage/tree_storage.go | 41 +++---- 8 files changed, 201 insertions(+), 295 deletions(-) diff --git a/merkle/merkle_path.go b/merkle/merkle_path.go index 5ea3fc400c..872097d3f7 100644 --- a/merkle/merkle_path.go +++ b/merkle/merkle_path.go @@ -38,14 +38,35 @@ func (n NodeFetch) Equivalent(other NodeFetch) bool { return n.Rehash == other.Rehash && n.NodeID.Equivalent(other.NodeID) } +// checkSnapshot performs a couple of simple sanity checks on sa and treeSize +// and returns an error if there's a problem. +func checkSnapshot(ssDesc string, ss, treeSize int64) error { + if ss < 1 { + return fmt.Errorf("%s %d < 1", ssDesc, ss) + } + if ss > treeSize { + return fmt.Errorf("%s %d > treeSize %d", ssDesc, ss, treeSize) + } + return nil +} + // CalcInclusionProofNodeAddresses returns the tree node IDs needed to // build an inclusion proof for a specified leaf and tree size. The snapshot parameter // is the tree size being queried for, treeSize is the actual size of the tree at the revision // we are using to fetch nodes (this can be > snapshot). The maxBitLen parameter // is copied into all the returned nodeIDs. func CalcInclusionProofNodeAddresses(snapshot, index, treeSize int64, maxBitLen int) ([]NodeFetch, error) { - if snapshot > treeSize || index >= snapshot || index < 0 || snapshot < 1 || maxBitLen <= 0 { - return nil, fmt.Errorf("invalid params s: %d index: %d ts: %d, bitlen:%d", snapshot, index, treeSize, maxBitLen) + if err := checkSnapshot("snapshot", snapshot, treeSize); err != nil { + return nil, err + } + if index >= snapshot { + return nil, fmt.Errorf("index %d is >= snapshot %d", index, snapshot) + } + if index < 0 { + return nil, fmt.Errorf("index %d is < 0", index) + } + if maxBitLen <= 0 { + return nil, fmt.Errorf("maxBitLen %d <= 0", maxBitLen) } return pathFromNodeToRootAtSnapshot(index, 0, snapshot, treeSize, maxBitLen) @@ -60,8 +81,17 @@ func CalcInclusionProofNodeAddresses(snapshot, index, treeSize int64, maxBitLen // coordinates within the new tree. It is assumed that they will be fetched from storage // at a revision corresponding to the STH associated with the treeSize parameter. func CalcConsistencyProofNodeAddresses(snapshot1, snapshot2, treeSize int64, maxBitLen int) ([]NodeFetch, error) { - if snapshot1 > snapshot2 || snapshot1 > treeSize || snapshot2 > treeSize || snapshot1 < 1 || snapshot2 < 1 || maxBitLen <= 0 { - return nil, fmt.Errorf("invalid params s1: %d s2: %d ts: %d, bitlen:%d", snapshot1, snapshot2, treeSize, maxBitLen) + if err := checkSnapshot("snapshot1", snapshot1, treeSize); err != nil { + return nil, err + } + if err := checkSnapshot("snapshot2", snapshot2, treeSize); err != nil { + return nil, err + } + if snapshot1 > snapshot2 { + return nil, fmt.Errorf("snapshot1 %d > snapshot2 %d", snapshot1, snapshot2) + } + if maxBitLen <= 0 { + return nil, fmt.Errorf("maxBitLen %d <= 0", maxBitLen) } return snapshotConsistency(snapshot1, snapshot2, treeSize, maxBitLen) @@ -104,7 +134,7 @@ func snapshotConsistency(snapshot1, snapshot2, treeSize int64, maxBitLen int) ([ } func pathFromNodeToRootAtSnapshot(node int64, level int, snapshot, treeSize int64, maxBitLen int) ([]NodeFetch, error) { - glog.V(vLevel).Infof("pathFromNodeToRootAtSnapshot: N:%d, L:%d, S:%d TS:%d", node, level, snapshot, treeSize) + glog.V(vLevel).Infof("pathFromNodeToRootAtSnapshot(%d, %d, %d, %d, %d)", node, level, snapshot, treeSize, maxBitLen) proof := make([]NodeFetch, 0, bitLen(snapshot)+1) if snapshot == 0 { diff --git a/server/log_rpc_server.go b/server/log_rpc_server.go index 14f82fbe9d..16e2ca3b42 100644 --- a/server/log_rpc_server.go +++ b/server/log_rpc_server.go @@ -121,13 +121,12 @@ func (t *TrillianLogRPCServer) GetInclusionProof(ctx context.Context, req *trill return nil, err } - treeRevision, treeSize, err := tx.GetTreeRevisionIncludingSize(req.TreeSize) + root, err := tx.LatestSignedLogRoot() if err != nil { - tx.Rollback() return nil, err } - proof, err := getInclusionProofForLeafIndexAtRevision(tx, req.TreeSize, treeRevision, treeSize, req.LeafIndex) + proof, err := getInclusionProofForLeafIndex(tx, req.TreeSize, req.LeafIndex, root.TreeSize) if err != nil { tx.Rollback() return nil, err @@ -163,24 +162,23 @@ func (t *TrillianLogRPCServer) GetInclusionProofByHash(ctx context.Context, req return nil, err } - treeRevision, treeSize, err := tx.GetTreeRevisionIncludingSize(req.TreeSize) + // Find the leaf index of the supplied hash + leafHashes := [][]byte{req.LeafHash} + leaves, err := tx.GetLeavesByHash(leafHashes, req.OrderBySequence) if err != nil { tx.Rollback() return nil, err } - // Find the leaf index of the supplied hash - leafHashes := [][]byte{req.LeafHash} - leaves, err := tx.GetLeavesByHash(leafHashes, req.OrderBySequence) + root, err := tx.LatestSignedLogRoot() if err != nil { - tx.Rollback() return nil, err } // TODO(Martin2112): Need to define a limit on number of results or some form of paging etc. proofs := make([]*trillian.Proof, 0, len(leaves)) for _, leaf := range leaves { - proof, err := getInclusionProofForLeafIndexAtRevision(tx, req.TreeSize, treeRevision, treeSize, leaf.LeafIndex) + proof, err := getInclusionProofForLeafIndex(tx, req.TreeSize, leaf.LeafIndex, root.TreeSize) if err != nil { tx.Rollback() return nil, err @@ -221,20 +219,19 @@ func (t *TrillianLogRPCServer) GetConsistencyProof(ctx context.Context, req *tri return nil, err } - secondTreeRevision, secondTreeSize, err := tx.GetTreeRevisionIncludingSize(req.SecondTreeSize) + root, err := tx.LatestSignedLogRoot() if err != nil { - tx.Rollback() return nil, err } - nodeFetches, err := merkle.CalcConsistencyProofNodeAddresses(req.FirstTreeSize, req.SecondTreeSize, secondTreeSize, proofMaxBitLen) + nodeFetches, err := merkle.CalcConsistencyProofNodeAddresses(req.FirstTreeSize, req.SecondTreeSize, root.TreeSize, proofMaxBitLen) if err != nil { return nil, err } // Do all the node fetches at the second tree revision, which is what the node ids were calculated // against. - proof, err := fetchNodesAndBuildProof(tx, secondTreeRevision, 0, nodeFetches) + proof, err := fetchNodesAndBuildProof(tx, tx.ReadRevision(), 0, nodeFetches) if err != nil { tx.Rollback() return nil, err @@ -354,13 +351,12 @@ func (t *TrillianLogRPCServer) GetEntryAndProof(ctx context.Context, req *trilli return nil, err } - treeRevision, treeSize, err := tx.GetTreeRevisionIncludingSize(req.TreeSize) + root, err := tx.LatestSignedLogRoot() if err != nil { - tx.Rollback() return nil, err } - proof, err := getInclusionProofForLeafIndexAtRevision(tx, req.TreeSize, treeRevision, treeSize, req.LeafIndex) + proof, err := getInclusionProofForLeafIndex(tx, req.TreeSize, req.LeafIndex, root.TreeSize) if err != nil { tx.Rollback() return nil, err @@ -464,17 +460,17 @@ func validateLeafHashes(leafHashes [][]byte) bool { return true } -// getInclusionProofForLeafIndexAtRevision is used by multiple handlers. It does the storage fetching +// getInclusionProofForLeafIndex is used by multiple handlers. It does the storage fetching // and makes additional checks on the returned proof. Returns a Proof suitable for inclusion in // an RPC response -func getInclusionProofForLeafIndexAtRevision(tx storage.ReadOnlyLogTreeTX, snapshot, treeRevision, treeSize, leafIndex int64) (trillian.Proof, error) { +func getInclusionProofForLeafIndex(tx storage.ReadOnlyLogTreeTX, snapshot, leafIndex, treeSize int64) (trillian.Proof, error) { // We have the tree size and leaf index so we know the nodes that we need to serve the proof proofNodeIDs, err := merkle.CalcInclusionProofNodeAddresses(snapshot, leafIndex, treeSize, proofMaxBitLen) if err != nil { return trillian.Proof{}, err } - return fetchNodesAndBuildProof(tx, treeRevision, leafIndex, proofNodeIDs) + return fetchNodesAndBuildProof(tx, tx.ReadRevision(), leafIndex, proofNodeIDs) } // getLeavesByHashInternal does the work of fetching leaves by either their raw data or merkle diff --git a/server/log_rpc_server_test.go b/server/log_rpc_server_test.go index cbb31386f2..c36a970330 100644 --- a/server/log_rpc_server_test.go +++ b/server/log_rpc_server_test.go @@ -49,7 +49,8 @@ var queueRequestEmpty = trillian.QueueLeavesRequest{LogId: logID1, Leaves: []*tr var getLogRootRequest1 = trillian.GetLatestSignedLogRootRequest{LogId: logID1} var getLogRootRequest2 = trillian.GetLatestSignedLogRootRequest{LogId: logID2} -var signedRoot1 = trillian.SignedLogRoot{TimestampNanos: 987654321, RootHash: []byte("A NICE HASH"), TreeSize: 7} +var revision1 = int64(5) +var signedRoot1 = trillian.SignedLogRoot{TimestampNanos: 987654321, RootHash: []byte("A NICE HASH"), TreeSize: 7, TreeRevision: revision1} var getByHashRequest1 = trillian.GetLeavesByHashRequest{LogId: logID1, LeafHash: [][]byte{[]byte("test"), []byte("data")}} var getByHashRequestBadHash = trillian.GetLeavesByHashRequest{LogId: logID1, LeafHash: [][]byte{[]byte(""), []byte("data")}} @@ -562,29 +563,12 @@ func TestGetProofByHashBeginTXFails(t *testing.T) { test.executeBeginFailsTest(t, getInclusionProofByHashRequest25.LogId) } -func TestGetProofByHashNoRevisionForTreeSize(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - test := newParameterizedTest(ctrl, "GetInclusionProofByHash", readOnly, - func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest25.TreeSize).Return(int64(0), int64(0), errors.New("STORAGE")) - }, - func(s *TrillianLogRPCServer) error { - _, err := s.GetInclusionProofByHash(context.Background(), &getInclusionProofByHashRequest25) - return err - }) - - test.executeStorageFailureTest(t, getInclusionProofByHashRequest25.LogId) -} - func TestGetProofByHashNoLeafForHash(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "GetInclusionProofByHash", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest25.TreeSize).Return(int64(17), getInclusionProofByHashRequest25.TreeSize, nil) t.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{}, errors.New("STORAGE")) }, func(s *TrillianLogRPCServer) error { @@ -601,9 +585,10 @@ func TestGetProofByHashGetNodesFails(t *testing.T) { test := newParameterizedTest(ctrl, "GetInclusionProofByHash", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest7.TreeSize).Return(int64(3), getInclusionProofByHashRequest7.TreeSize, nil) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) t.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{{LeafIndex: 2}}, nil) - t.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("STORAGE")) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("STORAGE")) }, func(s *TrillianLogRPCServer) error { _, err := s.GetInclusionProofByHash(context.Background(), &getInclusionProofByHashRequest7) @@ -621,10 +606,11 @@ func TestGetProofByHashWrongNodeCountFetched(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByHashRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest7.TreeSize).Return(int64(3), getInclusionProofByHashRequest7.TreeSize, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) mockTx.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{{LeafIndex: 2}}, nil) // The server expects three nodes from storage but we return only two - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -644,10 +630,11 @@ func TestGetProofByHashWrongNodeReturned(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByHashRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest7.TreeSize).Return(int64(3), getInclusionProofByHashRequest7.TreeSize, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) mockTx.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{{LeafIndex: 2}}, nil) // We set this up so one of the returned nodes has the wrong ID - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: testonly.MustCreateNodeIDForTreeCoords(4, 5, 64), NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: testonly.MustCreateNodeIDForTreeCoords(4, 5, 64), NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -665,9 +652,10 @@ func TestGetProofByHashCommitFails(t *testing.T) { test := newParameterizedTest(ctrl, "GetInclusionProofByHash", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) t.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{{LeafIndex: 2}}, nil) - t.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) }, func(s *TrillianLogRPCServer) error { _, err := s.GetInclusionProofByHash(context.Background(), &getInclusionProofByHashRequest7) @@ -685,9 +673,10 @@ func TestGetProofByHash(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByHashRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByHashRequest7.TreeSize).Return(int64(3), getInclusionProofByHashRequest7.TreeSize, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) mockTx.EXPECT().GetLeavesByHash([][]byte{[]byte("ahash")}, false).Return([]trillian.LogLeaf{{LeafIndex: 2}}, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -754,30 +743,15 @@ func TestGetProofByIndexBeginTXFails(t *testing.T) { test.executeBeginFailsTest(t, getInclusionProofByIndexRequest25.LogId) } -func TestGetProofByIndexNoRevisionForTreeSize(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - test := newParameterizedTest(ctrl, "GetInclusionProof", readOnly, - func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest25.TreeSize).Return(int64(0), int64(0), errors.New("STORAGE")) - }, - func(s *TrillianLogRPCServer) error { - _, err := s.GetInclusionProof(context.Background(), &getInclusionProofByIndexRequest25) - return err - }) - - test.executeStorageFailureTest(t, getInclusionProofByIndexRequest25.LogId) -} - func TestGetProofByIndexGetNodesFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "GetInclusionProof", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) - t.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("STORAGE")) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("STORAGE")) }, func(s *TrillianLogRPCServer) error { _, err := s.GetInclusionProof(context.Background(), &getInclusionProofByIndexRequest7) @@ -795,9 +769,10 @@ func TestGetProofByIndexWrongNodeCountFetched(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByHashRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) // The server expects three nodes from storage but we return only two - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -817,9 +792,10 @@ func TestGetProofByIndexWrongNodeReturned(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByIndexRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) // We set this up so one of the returned nodes has the wrong ID - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: testonly.MustCreateNodeIDForTreeCoords(4, 5, 64), NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: testonly.MustCreateNodeIDForTreeCoords(4, 5, 64), NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -837,8 +813,9 @@ func TestGetProofByIndexCommitFails(t *testing.T) { test := newParameterizedTest(ctrl, "GetInclusionProof", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) - t.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{{NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3}}, nil) }, func(s *TrillianLogRPCServer) error { _, err := s.GetInclusionProof(context.Background(), &getInclusionProofByIndexRequest7) @@ -856,8 +833,9 @@ func TestGetProofByIndex(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getInclusionProofByIndexRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getInclusionProofByIndexRequest7.TreeSize).Return(int64(3), getInclusionProofByIndexRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -925,26 +903,6 @@ func TestGetEntryAndProofBeginTXFails(t *testing.T) { } } -func TestGetEntryAndProofGetTreeSizeAtRevisionFails(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockStorage := storage.NewMockLogStorage(ctrl) - mockTx := storage.NewMockLogTreeTX(ctrl) - mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest17.LogId).Return(mockTx, nil) - - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest17.TreeSize).Return(int64(0), int64(0), errors.New("NOREVISION")) - mockTx.EXPECT().Rollback().Return(nil) - - registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) - server := NewTrillianLogRPCServer(registry, fakeTimeSource) - - _, err := server.GetEntryAndProof(context.Background(), &getEntryAndProofRequest17) - if err == nil || !strings.Contains(err.Error(), "NOREVISION") { - t.Fatalf("get entry and proof returned no or wrong error when no revision: %v", err) - } -} - func TestGetEntryAndProofGetMerkleNodesFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -953,8 +911,9 @@ func TestGetEntryAndProofGetMerkleNodesFails(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest7.TreeSize).Return(int64(3), getEntryAndProofRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("GetNodes")) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{}, errors.New("GetNodes")) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -974,8 +933,9 @@ func TestGetEntryAndProofGetLeavesFails(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest7.TreeSize).Return(int64(3), getEntryAndProofRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -999,8 +959,9 @@ func TestGetEntryAndProofGetLeavesReturnsMultiple(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest7.TreeSize).Return(int64(3), getEntryAndProofRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -1025,8 +986,9 @@ func TestGetEntryAndProofCommitFails(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest7.TreeSize).Return(int64(3), getEntryAndProofRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -1050,8 +1012,9 @@ func TestGetEntryAndProof(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getEntryAndProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getEntryAndProofRequest7.TreeSize).Return(int64(3), getEntryAndProofRequest7.TreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(3), nodeIdsInclusionSize7Index2).Return([]storage.Node{ + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsInclusionSize7Index2).Return([]storage.Node{ {NodeID: nodeIdsInclusionSize7Index2[0], NodeRevision: 3, Hash: []byte("nodehash0")}, {NodeID: nodeIdsInclusionSize7Index2[1], NodeRevision: 2, Hash: []byte("nodehash1")}, {NodeID: nodeIdsInclusionSize7Index2[2], NodeRevision: 3, Hash: []byte("nodehash2")}}, nil) @@ -1191,30 +1154,15 @@ func TestGetConsistencyProofBeginTXFails(t *testing.T) { test.executeBeginFailsTest(t, getConsistencyProofRequest25.LogId) } -func TestGetConsistencyProofGetTreeRevisionForSecondTreeSizeFails(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - test := newParameterizedTest(ctrl, "GetConsistencyProof", readOnly, - func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest25.SecondTreeSize).Return(int64(0), int64(0), errors.New("STORAGE")) - }, - func(s *TrillianLogRPCServer) error { - _, err := s.GetConsistencyProof(context.Background(), &getConsistencyProofRequest25) - return err - }) - - test.executeStorageFailureTest(t, getConsistencyProofRequest25.LogId) -} - func TestGetConsistencyProofGetNodesFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "GetConsistencyProof", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest7.SecondTreeSize).Return(int64(5), getConsistencyProofRequest7.SecondTreeSize, nil) - t.EXPECT().GetMerkleNodes(int64(5), nodeIdsConsistencySize4ToSize7).Return([]storage.Node{}, errors.New("STORAGE")) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsConsistencySize4ToSize7).Return([]storage.Node{}, errors.New("STORAGE")) }, func(s *TrillianLogRPCServer) error { _, err := s.GetConsistencyProof(context.Background(), &getConsistencyProofRequest7) @@ -1232,9 +1180,10 @@ func TestGetConsistencyProofGetNodesReturnsWrongCount(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getConsistencyProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest7.SecondTreeSize).Return(int64(5), getConsistencyProofRequest7.SecondTreeSize, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) // The server expects one node from storage but we return two - mockTx.EXPECT().GetMerkleNodes(int64(5), nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeRevision: 3}, {NodeRevision: 2}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -1254,9 +1203,10 @@ func TestGetConsistencyProofGetNodesReturnsWrongNode(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getConsistencyProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest7.SecondTreeSize).Return(int64(5), getConsistencyProofRequest7.SecondTreeSize, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) // Return an unexpected node that wasn't requested - mockTx.EXPECT().GetMerkleNodes(int64(5), nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(1, 2, 64), NodeRevision: 3}}, nil) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(1, 2, 64), NodeRevision: 3}}, nil) mockTx.EXPECT().Rollback().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) @@ -1274,8 +1224,9 @@ func TestGetConsistencyProofCommitFails(t *testing.T) { test := newParameterizedTest(ctrl, "GetConsistencyProof", readOnly, func(t *storage.MockLogTreeTX) { - t.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest7.SecondTreeSize).Return(int64(5), getConsistencyProofRequest7.SecondTreeSize, nil) - t.EXPECT().GetMerkleNodes(int64(5), nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(2, 1, 64), NodeRevision: 3}}, nil) + t.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + t.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + t.EXPECT().GetMerkleNodes(revision1, nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(2, 1, 64), NodeRevision: 3}}, nil) }, func(s *TrillianLogRPCServer) error { _, err := s.GetConsistencyProof(context.Background(), &getConsistencyProofRequest7) @@ -1293,8 +1244,9 @@ func TestGetConsistencyProof(t *testing.T) { mockTx := storage.NewMockLogTreeTX(ctrl) mockStorage.EXPECT().SnapshotForTree(gomock.Any(), getConsistencyProofRequest7.LogId).Return(mockTx, nil) - mockTx.EXPECT().GetTreeRevisionIncludingSize(getConsistencyProofRequest7.SecondTreeSize).Return(int64(5), getConsistencyProofRequest7.SecondTreeSize, nil) - mockTx.EXPECT().GetMerkleNodes(int64(5), nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(2, 1, 64), NodeRevision: 3, Hash: []byte("nodehash")}}, nil) + mockTx.EXPECT().LatestSignedLogRoot().Return(signedRoot1, nil) + mockTx.EXPECT().ReadRevision().Return(signedRoot1.TreeRevision) + mockTx.EXPECT().GetMerkleNodes(revision1, nodeIdsConsistencySize4ToSize7).Return([]storage.Node{{NodeID: testonly.MustCreateNodeIDForTreeCoords(2, 1, 64), NodeRevision: 3, Hash: []byte("nodehash")}}, nil) mockTx.EXPECT().Commit().Return(nil) registry := testonly.NewRegistryWithLogProvider(mockStorageProviderFunc(mockStorage)) diff --git a/storage/mock_storage.go b/storage/mock_storage.go index 397503bc87..d896161a49 100644 --- a/storage/mock_storage.go +++ b/storage/mock_storage.go @@ -172,18 +172,6 @@ func (_mr *_MockLogTreeTXRecorder) GetSequencedLeafCount() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSequencedLeafCount") } -func (_m *MockLogTreeTX) GetTreeRevisionIncludingSize(_param0 int64) (int64, int64, error) { - ret := _m.ctrl.Call(_m, "GetTreeRevisionIncludingSize", _param0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -func (_mr *_MockLogTreeTXRecorder) GetTreeRevisionIncludingSize(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetTreeRevisionIncludingSize", arg0) -} - func (_m *MockLogTreeTX) IsOpen() bool { ret := _m.ctrl.Call(_m, "IsOpen") ret0, _ := ret[0].(bool) @@ -215,6 +203,16 @@ func (_mr *_MockLogTreeTXRecorder) QueueLeaves(arg0, arg1 interface{}) *gomock.C return _mr.mock.ctrl.RecordCall(_mr.mock, "QueueLeaves", arg0, arg1) } +func (_m *MockLogTreeTX) ReadRevision() int64 { + ret := _m.ctrl.Call(_m, "ReadRevision") + ret0, _ := ret[0].(int64) + return ret0 +} + +func (_mr *_MockLogTreeTXRecorder) ReadRevision() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadRevision") +} + func (_m *MockLogTreeTX) Rollback() error { ret := _m.ctrl.Call(_m, "Rollback") ret0, _ := ret[0].(error) @@ -361,18 +359,6 @@ func (_mr *_MockMapTreeTXRecorder) GetMerkleNodes(arg0, arg1 interface{}) *gomoc return _mr.mock.ctrl.RecordCall(_mr.mock, "GetMerkleNodes", arg0, arg1) } -func (_m *MockMapTreeTX) GetTreeRevisionIncludingSize(_param0 int64) (int64, int64, error) { - ret := _m.ctrl.Call(_m, "GetTreeRevisionIncludingSize", _param0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -func (_mr *_MockMapTreeTXRecorder) GetTreeRevisionIncludingSize(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetTreeRevisionIncludingSize", arg0) -} - func (_m *MockMapTreeTX) IsOpen() bool { ret := _m.ctrl.Call(_m, "IsOpen") ret0, _ := ret[0].(bool) @@ -394,6 +380,16 @@ func (_mr *_MockMapTreeTXRecorder) LatestSignedMapRoot() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "LatestSignedMapRoot") } +func (_m *MockMapTreeTX) ReadRevision() int64 { + ret := _m.ctrl.Call(_m, "ReadRevision") + ret0, _ := ret[0].(int64) + return ret0 +} + +func (_mr *_MockMapTreeTXRecorder) ReadRevision() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadRevision") +} + func (_m *MockMapTreeTX) Rollback() error { ret := _m.ctrl.Call(_m, "Rollback") ret0, _ := ret[0].(error) @@ -582,16 +578,14 @@ func (_mr *_MockReadOnlyLogTreeTXRecorder) GetSequencedLeafCount() *gomock.Call return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSequencedLeafCount") } -func (_m *MockReadOnlyLogTreeTX) GetTreeRevisionIncludingSize(_param0 int64) (int64, int64, error) { - ret := _m.ctrl.Call(_m, "GetTreeRevisionIncludingSize", _param0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 +func (_m *MockReadOnlyLogTreeTX) IsOpen() bool { + ret := _m.ctrl.Call(_m, "IsOpen") + ret0, _ := ret[0].(bool) + return ret0 } -func (_mr *_MockReadOnlyLogTreeTXRecorder) GetTreeRevisionIncludingSize(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetTreeRevisionIncludingSize", arg0) +func (_mr *_MockReadOnlyLogTreeTXRecorder) IsOpen() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "IsOpen") } func (_m *MockReadOnlyLogTreeTX) LatestSignedLogRoot() (trillian.SignedLogRoot, error) { @@ -605,6 +599,16 @@ func (_mr *_MockReadOnlyLogTreeTXRecorder) LatestSignedLogRoot() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "LatestSignedLogRoot") } +func (_m *MockReadOnlyLogTreeTX) ReadRevision() int64 { + ret := _m.ctrl.Call(_m, "ReadRevision") + ret0, _ := ret[0].(int64) + return ret0 +} + +func (_mr *_MockReadOnlyLogTreeTXRecorder) ReadRevision() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadRevision") +} + func (_m *MockReadOnlyLogTreeTX) Rollback() error { ret := _m.ctrl.Call(_m, "Rollback") ret0, _ := ret[0].(error) @@ -668,16 +672,14 @@ func (_mr *_MockReadOnlyMapTreeTXRecorder) GetMerkleNodes(arg0, arg1 interface{} return _mr.mock.ctrl.RecordCall(_mr.mock, "GetMerkleNodes", arg0, arg1) } -func (_m *MockReadOnlyMapTreeTX) GetTreeRevisionIncludingSize(_param0 int64) (int64, int64, error) { - ret := _m.ctrl.Call(_m, "GetTreeRevisionIncludingSize", _param0) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(int64) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 +func (_m *MockReadOnlyMapTreeTX) IsOpen() bool { + ret := _m.ctrl.Call(_m, "IsOpen") + ret0, _ := ret[0].(bool) + return ret0 } -func (_mr *_MockReadOnlyMapTreeTXRecorder) GetTreeRevisionIncludingSize(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "GetTreeRevisionIncludingSize", arg0) +func (_mr *_MockReadOnlyMapTreeTXRecorder) IsOpen() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "IsOpen") } func (_m *MockReadOnlyMapTreeTX) LatestSignedMapRoot() (trillian.SignedMapRoot, error) { @@ -691,6 +693,16 @@ func (_mr *_MockReadOnlyMapTreeTXRecorder) LatestSignedMapRoot() *gomock.Call { return _mr.mock.ctrl.RecordCall(_mr.mock, "LatestSignedMapRoot") } +func (_m *MockReadOnlyMapTreeTX) ReadRevision() int64 { + ret := _m.ctrl.Call(_m, "ReadRevision") + ret0, _ := ret[0].(int64) + return ret0 +} + +func (_mr *_MockReadOnlyMapTreeTXRecorder) ReadRevision() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "ReadRevision") +} + func (_m *MockReadOnlyMapTreeTX) Rollback() error { ret := _m.ctrl.Call(_m, "Rollback") ret0, _ := ret[0].(error) diff --git a/storage/mysql/log_storage.go b/storage/mysql/log_storage.go index 69b834eb2a..c797a5fa62 100644 --- a/storage/mysql/log_storage.go +++ b/storage/mysql/log_storage.go @@ -165,12 +165,12 @@ func (m *mySQLLogStorage) beginInternal(ctx context.Context, treeID int64) (stor allowDuplicates: allowDuplicates, } - root, err := ltx.LatestSignedLogRoot() + ltx.root, err = ltx.fetchLatestRoot() if err != nil { ttx.Rollback() return nil, err } - ltx.treeTX.writeRevision = root.TreeRevision + 1 + ltx.treeTX.writeRevision = ltx.root.TreeRevision + 1 return ltx, nil } @@ -190,9 +190,14 @@ func (m *mySQLLogStorage) SnapshotForTree(ctx context.Context, treeID int64) (st type logTreeTX struct { treeTX ls *mySQLLogStorage + root trillian.SignedLogRoot allowDuplicates bool } +func (t *logTreeTX) ReadRevision() int64 { + return t.root.TreeRevision +} + func (t *logTreeTX) WriteRevision() int64 { return t.treeTX.writeRevision } @@ -390,6 +395,11 @@ func (t *logTreeTX) GetLeavesByHash(leafHashes [][]byte, orderBySequence bool) ( } func (t *logTreeTX) LatestSignedLogRoot() (trillian.SignedLogRoot, error) { + return t.root, nil +} + +// fetchLatestRoot reads the latest SignedLogRoot from the DB and returns it. +func (t *logTreeTX) fetchLatestRoot() (trillian.SignedLogRoot, error) { var timestamp, treeSize, treeRevision int64 var rootHash, rootSignatureBytes []byte var rootSignature trillian.DigitallySigned diff --git a/storage/mysql/log_storage_test.go b/storage/mysql/log_storage_test.go index b38ba3eeaf..48c9152713 100644 --- a/storage/mysql/log_storage_test.go +++ b/storage/mysql/log_storage_test.go @@ -715,98 +715,6 @@ func TestLatestSignedLogRoot(t *testing.T) { } } -func TestGetTreeRevisionAtSize(t *testing.T) { - logID := createLogID("TestGetTreeRevisionAtSize") - cleanTestDB(DB) - prepareTestLogDB(DB, logID, t) - s := prepareTestLogStorage(DB, logID, t) - - { - tx := beginLogTx(s, logID, t) - - // TODO: Tidy up the log id as it looks silly chained 3 times like this - root := trillian.SignedLogRoot{LogId: logID.logID, TimestampNanos: 98765, TreeSize: 16, TreeRevision: 5, RootHash: []byte(dummyHash), Signature: &trillian.DigitallySigned{Signature: []byte("notempty")}} - root2 := trillian.SignedLogRoot{LogId: logID.logID, TimestampNanos: 198765, TreeSize: 27, TreeRevision: 11, RootHash: []byte(dummyHash), Signature: &trillian.DigitallySigned{Signature: []byte("notempty")}} - - if err := tx.StoreSignedLogRoot(root); err != nil { - t.Fatalf("Failed to store signed root: %v", err) - } - - if err := tx.StoreSignedLogRoot(root2); err != nil { - t.Fatalf("Failed to store signed root2: %v", err) - } - - commit(tx, t) - } - - { - tx := beginLogTx(s, logID, t) - defer commit(tx, t) - - // First two are tree head sizes and returned version should match - if treeRevision1, treeSize1, err := tx.GetTreeRevisionIncludingSize(16); err != nil || treeRevision1 != 5 || treeSize1 != 16 { - t.Fatalf("Want revision=5, size=16, err=nil got: revision=%d size=%d err=%v", treeRevision1, treeSize1, err) - } - - if treeRevision2, treeSize2, err := tx.GetTreeRevisionIncludingSize(27); err != nil || treeRevision2 != 11 || treeSize2 != 27 { - t.Fatalf("Want revision=11, size=27, err=nil got: revision=%d size=%d err=%v", treeRevision2, treeSize2, err) - } - - // A tree size between revisions should return the next highest - if treeRevision3, treeSize3, err := tx.GetTreeRevisionIncludingSize(21); err != nil || treeRevision3 != 11 || treeSize3 != 27 { - t.Fatalf("Want revision=11, size=27, err=nil got: revision=%d size=%d err=%v", treeRevision3, treeSize3, err) - } - - // A value >= largest tree size should not be allowed - if treeRevision4, treeSize4, err := tx.GetTreeRevisionIncludingSize(30); err == nil { - t.Fatalf("Got: revision=%d size=%d err=%v for tree size 30, want: 0, 0, error", treeRevision4, treeSize4, err) - } - } -} - -func TestGetTreeRevisionMultipleSameSize(t *testing.T) { - logID := createLogID("TestGetTreeRevisionAtSize") - cleanTestDB(DB) - prepareTestLogDB(DB, logID, t) - s := prepareTestLogStorage(DB, logID, t) - - { - tx := beginLogTx(s, logID, t) - - // Normally tree heads at the same tree size must have the same revision because nothing was - // added between them by definition, this is an artificial situation just for testing. - // TODO: Tidy up the log id as it looks silly chained 3 times like this - root := trillian.SignedLogRoot{LogId: logID.logID, TimestampNanos: 98765, TreeSize: 16, TreeRevision: 11, RootHash: []byte(dummyHash), Signature: &trillian.DigitallySigned{Signature: []byte("notempty")}} - root2 := trillian.SignedLogRoot{LogId: logID.logID, TimestampNanos: 198765, TreeSize: 16, TreeRevision: 13, RootHash: []byte(dummyHash), Signature: &trillian.DigitallySigned{Signature: []byte("notempty")}} - - if err := tx.StoreSignedLogRoot(root); err != nil { - t.Fatalf("Failed to store signed root: %v", err) - } - - if err := tx.StoreSignedLogRoot(root2); err != nil { - t.Fatalf("Failed to store signed root2: %v", err) - } - - commit(tx, t) - } - - { - tx := beginLogTx(s, logID, t) - defer commit(tx, t) - - // We should get back the first revision at size 16 - treeRevision, treeSize, err := tx.GetTreeRevisionIncludingSize(16) - - if err != nil { - t.Fatalf("Failed to get tree revision: %v", err) - } - - if treeRevision != 11 || treeSize != 16 { - t.Fatalf("got revision=%d, size=%d, want: revision=11, size=16", treeRevision, treeSize) - } - } -} - func TestDuplicateSignedLogRoot(t *testing.T) { logID := createLogID("TestDuplicateSignedLogRoot") cleanTestDB(DB) diff --git a/storage/mysql/map_storage.go b/storage/mysql/map_storage.go index 5d26b41e9a..bb551e1bba 100644 --- a/storage/mysql/map_storage.go +++ b/storage/mysql/map_storage.go @@ -62,11 +62,11 @@ func (m *mySQLMapStorage) BeginForTree(ctx context.Context, treeID int64) (stora ms: m, } - root, err := mtx.LatestSignedMapRoot() + mtx.root, err = mtx.LatestSignedMapRoot() if err != nil { return nil, err } - mtx.treeTX.writeRevision = root.MapRevision + 1 + mtx.treeTX.writeRevision = mtx.root.MapRevision + 1 return mtx, nil } @@ -81,7 +81,12 @@ func (m *mySQLMapStorage) SnapshotForTree(ctx context.Context, treeID int64) (st type mapTreeTX struct { treeTX - ms *mySQLMapStorage + ms *mySQLMapStorage + root trillian.SignedMapRoot +} + +func (m *mapTreeTX) ReadRevision() int64 { + return m.root.MapRevision } func (m *mapTreeTX) WriteRevision() int64 { diff --git a/storage/tree_storage.go b/storage/tree_storage.go index 68e2e59f06..2ec71d3c75 100644 --- a/storage/tree_storage.go +++ b/storage/tree_storage.go @@ -18,8 +18,21 @@ package storage // A ReadOnlyTreeTX can only modify the tree specified in its creation. type ReadOnlyTreeTX interface { NodeReader + + // ReadRevision returns the tree revision that was current at the time this + // transaction was started. + ReadRevision() int64 + + // Commit attempts to commit any reads performed under this transaction. Commit() error + + // Rollback aborts this transaction. Rollback() error + + // Open indicates if this transaction is open. An open transaction is one for which + // Commit() or Rollback() has never been called. Implementations must do all clean up + // in these methods so transactions are assumed closed regardless of the reported success. + IsOpen() bool } // TreeTX represents an in-process tree-modifying transaction. @@ -29,18 +42,8 @@ type ReadOnlyTreeTX interface { // released any resources owned by the TreeTX. // A TreeTX can only modify the tree specified in its creation. type TreeTX interface { - NodeReaderWriter - - // Commit applies the operations performed to the underlying storage, or returns an error. - Commit() error - - // Rollback aborts any performed operations. No updates must be applied to the underlying storage. - Rollback() error - - // Open indicates if this transaction is open. An open transaction is one for which - // Commit() or Rollback() has never been called. Implementations must do all clean up - // in these methods so transactions are assumed closed regardless of the reported success. - IsOpen() bool + ReadOnlyTreeTX + NodeWriter // WriteRevision returns the tree revision that any writes through this TreeTX will be stored at. WriteRevision() int64 @@ -48,22 +51,12 @@ type TreeTX interface { // NodeReader provides a read-only interface into the stored tree nodes. type NodeReader interface { - // GetTreeRevisionIncludingSize returns the revision and actual size for a tree at a requested - // size. - // - // It is an error to request tree sizes larger than the currently published tree size. - // This may return a revision for any tree size at least as large as that requested. The - // size of the tree is returned along with the corresponding revision. The caller should - // be aware that this may differ from the requested size. - GetTreeRevisionIncludingSize(treeSize int64) (revision, size int64, err error) // GetMerkleNodes looks up the set of nodes identified by ids, at treeRevision, and returns them. GetMerkleNodes(treeRevision int64, ids []NodeID) ([]Node, error) } -// NodeReaderWriter provides a read-write interface into the stored tree nodes. -type NodeReaderWriter interface { - NodeReader - +// NodeWriter provides a write interface into the stored tree nodes. +type NodeWriter interface { // SetMerkleNodes stores the provided nodes, at the transaction's writeRevision. SetMerkleNodes(nodes []Node) error } From 61b0e96ef254a9e0dd12728aaf35890e2d2c1986 Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Wed, 8 Feb 2017 11:09:29 +0000 Subject: [PATCH 2/2] fixes --- merkle/merkle_path.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/merkle/merkle_path.go b/merkle/merkle_path.go index 872097d3f7..2f38212c55 100644 --- a/merkle/merkle_path.go +++ b/merkle/merkle_path.go @@ -38,7 +38,7 @@ func (n NodeFetch) Equivalent(other NodeFetch) bool { return n.Rehash == other.Rehash && n.NodeID.Equivalent(other.NodeID) } -// checkSnapshot performs a couple of simple sanity checks on sa and treeSize +// checkSnapshot performs a couple of simple sanity checks on ss and treeSize // and returns an error if there's a problem. func checkSnapshot(ssDesc string, ss, treeSize int64) error { if ss < 1 { @@ -57,16 +57,16 @@ func checkSnapshot(ssDesc string, ss, treeSize int64) error { // is copied into all the returned nodeIDs. func CalcInclusionProofNodeAddresses(snapshot, index, treeSize int64, maxBitLen int) ([]NodeFetch, error) { if err := checkSnapshot("snapshot", snapshot, treeSize); err != nil { - return nil, err + return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: %v", err) } if index >= snapshot { - return nil, fmt.Errorf("index %d is >= snapshot %d", index, snapshot) + return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: index %d is >= snapshot %d", index, snapshot) } if index < 0 { - return nil, fmt.Errorf("index %d is < 0", index) + return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: index %d is < 0", index) } if maxBitLen <= 0 { - return nil, fmt.Errorf("maxBitLen %d <= 0", maxBitLen) + return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: maxBitLen %d <= 0", maxBitLen) } return pathFromNodeToRootAtSnapshot(index, 0, snapshot, treeSize, maxBitLen) @@ -82,16 +82,16 @@ func CalcInclusionProofNodeAddresses(snapshot, index, treeSize int64, maxBitLen // at a revision corresponding to the STH associated with the treeSize parameter. func CalcConsistencyProofNodeAddresses(snapshot1, snapshot2, treeSize int64, maxBitLen int) ([]NodeFetch, error) { if err := checkSnapshot("snapshot1", snapshot1, treeSize); err != nil { - return nil, err + return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: %v", err) } if err := checkSnapshot("snapshot2", snapshot2, treeSize); err != nil { - return nil, err + return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: %v", err) } if snapshot1 > snapshot2 { - return nil, fmt.Errorf("snapshot1 %d > snapshot2 %d", snapshot1, snapshot2) + return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: snapshot1 %d > snapshot2 %d", snapshot1, snapshot2) } if maxBitLen <= 0 { - return nil, fmt.Errorf("maxBitLen %d <= 0", maxBitLen) + return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: maxBitLen %d <= 0", maxBitLen) } return snapshotConsistency(snapshot1, snapshot2, treeSize, maxBitLen)