github.com/bartle-stripe/trillian@v1.2.1/storage/cache/log_subtree_cache.go (about) 1 // Copyright 2017 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 cache 16 17 import ( 18 "fmt" 19 20 "github.com/google/trillian/merkle" 21 "github.com/google/trillian/merkle/hashers" 22 "github.com/google/trillian/storage" 23 "github.com/google/trillian/storage/storagepb" 24 ) 25 26 // NewLogSubtreeCache creates and returns a SubtreeCache appropriate for use with a log 27 // tree. The caller must supply the strata depths to be used and a suitable LogHasher. 28 func NewLogSubtreeCache(logStrata []int, hasher hashers.LogHasher) SubtreeCache { 29 return NewSubtreeCache(logStrata, populateLogSubtreeNodes(hasher), prepareLogSubtreeWrite()) 30 } 31 32 // LogPopulateFunc obtains a log storage population function based on a supplied LogHasher. 33 // This is intended for use by storage utilities. 34 func LogPopulateFunc(hasher hashers.LogHasher) storage.PopulateSubtreeFunc { 35 return populateLogSubtreeNodes(hasher) 36 } 37 38 // populateLogSubtreeNodes re-creates a Log subtree's InternalNodes from the 39 // subtree Leaves map. 40 // 41 // This uses the CompactMerkleTree to repopulate internal nodes, and so will 42 // handle imperfect (but left-hand dense) subtrees. Note that we only rebuild internal 43 // nodes when the subtree is fully populated. For an explanation of why see the comments 44 // below for PrepareLogSubtreeWrite. 45 func populateLogSubtreeNodes(hasher hashers.LogHasher) storage.PopulateSubtreeFunc { 46 return func(st *storagepb.SubtreeProto) error { 47 cmt := merkle.NewCompactMerkleTree(hasher) 48 if st.Depth < 1 { 49 return fmt.Errorf("populate log subtree with invalid depth: %d", st.Depth) 50 } 51 // maxLeaves is the number of leaves that fully populates a subtree of the depth we are 52 // working with. 53 maxLeaves := 1 << uint(st.Depth) 54 55 // If the subtree is fully populated then the internal node map is expected to be nil but in 56 // case it isn't we recreate it as we're about to rebuild the contents. We'll check 57 // below that the number of nodes is what we expected to have. 58 if st.InternalNodes == nil || len(st.Leaves) == maxLeaves { 59 st.InternalNodes = make(map[string][]byte) 60 } 61 62 // We need to update the subtree root hash regardless of whether it's fully populated 63 for leafIndex := int64(0); leafIndex < int64(len(st.Leaves)); leafIndex++ { 64 nodeID := storage.NewNodeIDFromPrefix(st.Prefix, logStrataDepth, leafIndex, logStrataDepth, maxLogDepth) 65 _, sfx := nodeID.Split(len(st.Prefix), int(st.Depth)) 66 sfxKey := sfx.String() 67 h := st.Leaves[sfxKey] 68 if h == nil { 69 return fmt.Errorf("unexpectedly got nil for subtree leaf suffix %s", sfx) 70 } 71 seq, err := cmt.AddLeafHash(h, func(height int, index int64, h []byte) error { 72 if height == logStrataDepth && index == 0 { 73 // no space for the root in the node cache 74 return nil 75 } 76 77 subDepth := logStrataDepth - height 78 nodeID := storage.NewNodeIDFromPrefix(st.Prefix, subDepth, index, logStrataDepth, maxLogDepth) 79 _, sfx := nodeID.Split(len(st.Prefix), int(st.Depth)) 80 sfxKey := sfx.String() 81 // Don't put leaves into the internal map and only update if we're rebuilding internal 82 // nodes. If the subtree was saved with internal nodes then we don't touch the map. 83 if height > 0 && len(st.Leaves) == maxLeaves { 84 st.InternalNodes[sfxKey] = h 85 } 86 return nil 87 }) 88 if err != nil { 89 return err 90 } 91 if got, expected := seq, leafIndex; got != expected { 92 return fmt.Errorf("got seq of %d, but expected %d", got, expected) 93 } 94 } 95 st.RootHash = cmt.CurrentRoot() 96 97 // Additional check - after population we should have the same number of internal nodes 98 // as before the subtree was written to storage. Either because they were loaded from 99 // storage or just rebuilt above. 100 if got, want := uint32(len(st.InternalNodes)), st.InternalNodeCount; got != want { 101 // TODO(Martin2112): Possibly replace this with stronger checks on the data in 102 // subtrees on disk so we can detect corruption. 103 return fmt.Errorf("log repop got: %d internal nodes, want: %d", got, want) 104 } 105 106 return nil 107 } 108 } 109 110 // prepareLogSubtreeWrite prepares a log subtree for writing. If the subtree is fully 111 // populated the internal nodes are cleared. Otherwise they are written. 112 // 113 // To see why this is necessary consider the case where a tree has a single full subtree 114 // and then an additional leaf is added. 115 // 116 // This causes an extra level to be added to the tree with an internal node that is a hash 117 // of the root of the left full subtree and the new leaf. Note that the leaves remain at 118 // level zero in the overall tree coordinate space but they are now in a lower subtree stratum 119 // than they were before the last node was added as the tree has grown above them. 120 // 121 // Thus in the case just discussed the internal nodes cannot be correctly reconstructed 122 // in isolation when the tree is reloaded because of the dependency on another subtree. 123 // 124 // Fully populated subtrees don't have this problem because by definition they can only 125 // contain internal nodes built from their own contents. 126 func prepareLogSubtreeWrite() storage.PrepareSubtreeWriteFunc { 127 return func(st *storagepb.SubtreeProto) error { 128 st.InternalNodeCount = uint32(len(st.InternalNodes)) 129 if st.Depth < 1 { 130 return fmt.Errorf("prepare subtree for log write invalid depth: %d", st.Depth) 131 } 132 maxLeaves := 1 << uint(st.Depth) 133 // If the subtree is fully populated we can safely clear the internal nodes 134 if len(st.Leaves) == maxLeaves { 135 st.InternalNodes = nil 136 } 137 return nil 138 } 139 }