Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recalculate intermediate hashes using current tree. #369

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions merkle/merkle_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 ss 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, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: %v", err)
}
if index >= snapshot {
return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: index %d is >= snapshot %d", index, snapshot)
}
if index < 0 {
return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: index %d is < 0", index)
}
if maxBitLen <= 0 {
return nil, fmt.Errorf("invalid parameter to CalcInclusionProofNodeAddresses: maxBitLen %d <= 0", maxBitLen)
}

return pathFromNodeToRootAtSnapshot(index, 0, snapshot, treeSize, maxBitLen)
Expand All @@ -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, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: %v", err)
}
if err := checkSnapshot("snapshot2", snapshot2, treeSize); err != nil {
return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: %v", err)
}
if snapshot1 > snapshot2 {
return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: snapshot1 %d > snapshot2 %d", snapshot1, snapshot2)
}
if maxBitLen <= 0 {
return nil, fmt.Errorf("invalid parameter to CalcConsistencyProofNodeAddresses: maxBitLen %d <= 0", maxBitLen)
}

return snapshotConsistency(snapshot1, snapshot2, treeSize, maxBitLen)
Expand Down Expand Up @@ -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 {
Expand Down
34 changes: 15 additions & 19 deletions server/log_rpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading