github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/testonly/hammer/contents.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 hammer
    16  
    17  import (
    18  	"bytes"
    19  	"crypto/sha256"
    20  	"fmt"
    21  	"math/rand"
    22  	"sort"
    23  	"sync"
    24  
    25  	"github.com/golang/glog"
    26  	"github.com/google/trillian"
    27  )
    28  
    29  // mapContents is a complete copy of the map's contents at a particular revision.
    30  type mapContents struct {
    31  	data map[mapKey]string
    32  	rev  int64
    33  }
    34  type mapKey [sha256.Size]byte
    35  
    36  func (m *mapContents) empty() bool {
    37  	return len(m.data) == 0
    38  }
    39  
    40  // pickKey randomly selects a key that already exists in a given copy of the
    41  // map's contents. Assumes that the copy is non-empty.
    42  func (m *mapContents) pickKey(prng *rand.Rand) []byte {
    43  	if m.empty() {
    44  		panic("internal error: can't pick a key, map data is empty!")
    45  	}
    46  
    47  	choice := prng.Intn(len(m.data))
    48  	// Need sorted keys for reproduceability.
    49  	keys := make([]mapKey, 0)
    50  	for k := range m.data {
    51  		keys = append(keys, k)
    52  	}
    53  	sort.SliceStable(keys, func(i, j int) bool {
    54  		return bytes.Compare(keys[i][:], keys[j][:]) == -1
    55  	})
    56  	return keys[choice][:]
    57  }
    58  
    59  // checkContents compares information returned from the Map against a local copy
    60  // of the map's contents.
    61  func (m *mapContents) checkContents(leafInclusions []*trillian.MapLeafInclusion, extraSize uint) error {
    62  	for _, inc := range leafInclusions {
    63  		leaf := inc.Leaf
    64  		var key mapKey
    65  		copy(key[:], leaf.Index)
    66  		value, ok := m.data[key]
    67  		if ok {
    68  			if string(leaf.LeafValue) != value {
    69  				return fmt.Errorf("got leaf[%v].LeafValue=%q, want %q", key, leaf.LeafValue, value)
    70  			}
    71  			if want := extraDataForValue(leaf.LeafValue, extraSize); !bytes.Equal(leaf.ExtraData, want) {
    72  				return fmt.Errorf("got leaf[%v].ExtraData=%q, want %q", key, leaf.ExtraData, want)
    73  			}
    74  		} else {
    75  			if len(leaf.LeafValue) > 0 {
    76  				return fmt.Errorf("got leaf[%v].LeafValue=%q, want not-present", key, leaf.LeafValue)
    77  			}
    78  		}
    79  	}
    80  	return nil
    81  }
    82  
    83  // updatedWith returns a new mapContents object that has been updated to include the
    84  // given leaves and revision.  A nil receiver object is allowed.
    85  func (m *mapContents) updatedWith(rev uint64, leaves []*trillian.MapLeaf) *mapContents {
    86  	// Start from previous map contents
    87  	result := mapContents{rev: int64(rev), data: make(map[mapKey]string)}
    88  	if m != nil {
    89  		for k, v := range m.data {
    90  			result.data[k] = v
    91  		}
    92  	}
    93  	// Update with given leaves
    94  	for _, leaf := range leaves {
    95  		var k mapKey
    96  		copy(k[:], leaf.Index)
    97  		if leaf.LeafValue != nil {
    98  			result.data[k] = string(leaf.LeafValue)
    99  		} else {
   100  			delete(result.data, k)
   101  		}
   102  	}
   103  
   104  	return &result
   105  }
   106  
   107  // How many copies of map contents to hold on to.
   108  const copyCount = 10
   109  
   110  type versionedMapContents struct {
   111  	mu sync.RWMutex
   112  
   113  	// contents holds copies of the map at different revisions,
   114  	// from later to earlier (so [0] is the most recent).
   115  	contents [copyCount]*mapContents
   116  }
   117  
   118  func (p *versionedMapContents) empty() bool {
   119  	return p.lastCopy() == nil
   120  }
   121  
   122  // prevCopy returns the specified copy of the map's contents.
   123  func (p *versionedMapContents) prevCopy(which int) *mapContents {
   124  	p.mu.RLock()
   125  	defer p.mu.RUnlock()
   126  	return p.contents[which]
   127  }
   128  
   129  // lastCopy returns the most recent copy of the map's contents.
   130  func (p *versionedMapContents) lastCopy() *mapContents {
   131  	return p.prevCopy(0)
   132  }
   133  
   134  // pickCopy returns a previous copy of the map's contents, returning
   135  // nil if there are no local copies.
   136  func (p *versionedMapContents) pickCopy(prng *rand.Rand) *mapContents {
   137  	p.mu.RLock()
   138  	defer p.mu.RUnlock()
   139  	// Count the number of filled copies.
   140  	i := 0
   141  	for ; i < copyCount; i++ {
   142  		if p.contents[i] == nil {
   143  			break
   144  		}
   145  	}
   146  	if i == 0 {
   147  		// No copied contents yet
   148  		return nil
   149  	}
   150  	choice := prng.Intn(i)
   151  	return p.contents[choice]
   152  }
   153  
   154  func (p *versionedMapContents) updateContentsWith(rev uint64, leaves []*trillian.MapLeaf) error {
   155  	p.mu.Lock()
   156  	defer p.mu.Unlock()
   157  
   158  	// Sanity check on rev being +ve and monotone increasing.
   159  	if rev < 1 {
   160  		return errInvariant{fmt.Sprintf("got rev %d, want >=1 when trying to update hammer state with contents", rev)}
   161  	}
   162  	if p.contents[0] != nil && int64(rev) <= p.contents[0].rev {
   163  		return errInvariant{fmt.Sprintf("got rev %d, want >%d when trying to update hammer state with new contents", rev, p.contents[0].rev)}
   164  	}
   165  
   166  	// Shuffle earlier contents along.
   167  	for i := copyCount - 1; i > 0; i-- {
   168  		p.contents[i] = p.contents[i-1]
   169  	}
   170  	p.contents[0] = p.contents[1].updatedWith(rev, leaves)
   171  
   172  	if glog.V(3) {
   173  		p.dumpLockedContents()
   174  	}
   175  	return nil
   176  }
   177  
   178  // dumpLockedContents shows the local copies of the map's contents; it should be called with p.mu held.
   179  func (p *versionedMapContents) dumpLockedContents() {
   180  	fmt.Println("Contents\n~~~~~~~~")
   181  	for i, c := range p.contents {
   182  		if c == nil {
   183  			break
   184  		}
   185  		fmt.Printf(" slot #%d\n", i)
   186  		fmt.Printf("  revision: %d\n", c.rev)
   187  		fmt.Println("  data:")
   188  		for k, v := range c.data {
   189  			fmt.Printf("   k: %s v: %v\n", string(k[:]), v)
   190  		}
   191  	}
   192  	fmt.Println("~~~~~~~~")
   193  }