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  }