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