Browse Source

merkle: remove unused funcs. unexport simplemap. improv docs

pull/1782/head
Ethan Buchman 7 years ago
parent
commit
9f04935caa
7 changed files with 84 additions and 95 deletions
  1. +26
    -18
      merkle/simple_map.go
  2. +10
    -7
      merkle/simple_map_test.go
  3. +13
    -5
      merkle/simple_proof.go
  4. +30
    -63
      merkle/simple_tree.go
  5. +2
    -1
      merkle/simple_tree_test.go
  6. +2
    -0
      merkle/types.go
  7. +1
    -1
      tmhash/hash.go

+ 26
- 18
merkle/simple_map.go View File

@ -5,23 +5,27 @@ import (
"github.com/tendermint/go-crypto/tmhash"
)
type SimpleMap struct {
// Merkle tree from a map.
// Leaves are `hash(key) | hash(value)`.
// Leaves are sorted before Merkle hashing.
type simpleMap struct {
kvs cmn.KVPairs
sorted bool
}
func NewSimpleMap() *SimpleMap {
return &SimpleMap{
func newSimpleMap() *simpleMap {
return &simpleMap{
kvs: nil,
sorted: false,
}
}
func (sm *SimpleMap) Set(key string, value Hasher) {
// Set hashes the key and value and appends it to the kv pairs.
func (sm *simpleMap) Set(key string, value Hasher) {
sm.sorted = false
// Hash the key to blind it... why not?
khash := SimpleHashFromBytes([]byte(key))
khash := tmhash.Sum([]byte(key))
// And the value is hashed too, so you can
// check for equality with a cached value (say)
@ -34,14 +38,14 @@ func (sm *SimpleMap) Set(key string, value Hasher) {
})
}
// Merkle root hash of items sorted by key
// Hash Merkle root hash of items sorted by key
// (UNSTABLE: and by value too if duplicate key).
func (sm *SimpleMap) Hash() []byte {
func (sm *simpleMap) Hash() []byte {
sm.Sort()
return hashKVPairs(sm.kvs)
}
func (sm *SimpleMap) Sort() {
func (sm *simpleMap) Sort() {
if sm.sorted {
return
}
@ -50,7 +54,8 @@ func (sm *SimpleMap) Sort() {
}
// Returns a copy of sorted KVPairs.
func (sm *SimpleMap) KVPairs() cmn.KVPairs {
// NOTE these contain the hashed key and value.
func (sm *simpleMap) KVPairs() cmn.KVPairs {
sm.Sort()
kvs := make(cmn.KVPairs, len(sm.kvs))
copy(kvs, sm.kvs)
@ -60,25 +65,28 @@ func (sm *SimpleMap) KVPairs() cmn.KVPairs {
//----------------------------------------
// A local extension to KVPair that can be hashed.
type KVPair cmn.KVPair
// XXX: key and value do not need to already be hashed -
// the kvpair ("abc", "def") would not give the same result
// as ("ab", "cdef") as we're using length-prefixing.
type kvPair cmn.KVPair
func (kv KVPair) Hash() []byte {
func (kv kvPair) Hash() []byte {
hasher := tmhash.New()
err := encodeByteSlice(hasher, kv.Key)
if err != nil {
panic(err)
}
panic(err)
}
err = encodeByteSlice(hasher, kv.Value)
if err != nil {
panic(err)
}
panic(err)
}
return hasher.Sum(nil)
}
func hashKVPairs(kvs cmn.KVPairs) []byte {
kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range kvs {
kvsH = append(kvsH, KVPair(kvp))
kvsH := make([]Hasher, len(kvs))
for i, kvp := range kvs {
kvsH[i] = kvPair(kvp)
}
return SimpleHashFromHashers(kvsH)
}

+ 10
- 7
merkle/simple_map_test.go View File

@ -5,46 +5,49 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tendermint/go-crypto/tmhash"
)
type strHasher string
func (str strHasher) Hash() []byte {
return SimpleHashFromBytes([]byte(str))
h := tmhash.New()
h.Write([]byte(str))
return h.Sum(nil)
}
func TestSimpleMap(t *testing.T) {
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
assert.Equal(t, "3dafc06a52039d029be57c75c9d16356a4256ef4", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key1", strHasher("value2"))
assert.Equal(t, "03eb5cfdff646bc4e80fec844e72fd248a1c6b2c", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
db.Set("key3", strHasher("value3"))
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
db.Set("key3", strHasher("value3"))


+ 13
- 5
merkle/simple_proof.go View File

@ -5,10 +5,12 @@ import (
"fmt"
)
// SimpleProof represents a simple merkle proof.
type SimpleProof struct {
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
}
// SimpleProofsFromHashers computes inclusion proof for given items.
// proofs[0] is the proof for items[0].
func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleProof) {
trails, rootSPN := trailsFromHashers(items)
@ -22,8 +24,11 @@ func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleP
return
}
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values
// in the underlying key-value pairs.
// The keys are sorted before the proofs are computed.
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*SimpleProof) {
sm := NewSimpleMap()
sm := newSimpleMap()
for k, v := range m {
sm.Set(k, v)
}
@ -31,7 +36,7 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*Simple
kvs := sm.kvs
kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range kvs {
kvsH = append(kvsH, KVPair(kvp))
kvsH = append(kvsH, kvPair(kvp))
}
return SimpleProofsFromHashers(kvsH)
}
@ -43,10 +48,13 @@ func (sp *SimpleProof) Verify(index int, total int, leafHash []byte, rootHash []
return computedHash != nil && bytes.Equal(computedHash, rootHash)
}
// String implements the stringer interface for SimpleProof.
// It is a wrapper around StringIndented.
func (sp *SimpleProof) String() string {
return sp.StringIndented("")
}
// StringIndented generates a canonical string representation of a SimpleProof.
func (sp *SimpleProof) StringIndented(indent string) string {
return fmt.Sprintf(`SimpleProof{
%s Aunts: %X
@ -90,7 +98,7 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][
}
}
// Helper structure to construct merkle proof.
// SimpleProofNode is a helper structure to construct merkle proof.
// The node and the tree is thrown away afterwards.
// Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil.
// node.Parent.Hash = hash(node.Hash, node.Right.Hash) or
@ -102,8 +110,8 @@ type SimpleProofNode struct {
Right *SimpleProofNode // Right sibling (only one of Left,Right is set)
}
// Starting from a leaf SimpleProofNode, FlattenAunts() will return
// the inner hashes for the item corresponding to the leaf.
// FlattenAunts will return the inner hashes for the item corresponding to the leaf,
// starting from a leaf SimpleProofNode.
func (spn *SimpleProofNode) FlattenAunts() [][]byte {
// Nonrecursive impl.
innerHashes := [][]byte{}


+ 30
- 63
merkle/simple_tree.go View File

@ -1,91 +1,58 @@
/*
Computes a deterministic minimal height merkle tree hash.
If the number of items is not a power of two, some leaves
will be at different levels. Tries to keep both sides of
the tree the same size, but the left may be one greater.
Use this for short deterministic trees, such as the validator list.
For larger datasets, use IAVLTree.
*
/ \
/ \
/ \
/ \
* *
/ \ / \
/ \ / \
/ \ / \
* * * h6
/ \ / \ / \
h0 h1 h2 h3 h4 h5
*/
package merkle
import (
"github.com/tendermint/go-crypto/tmhash"
)
func SimpleHashFromTwoHashes(left []byte, right []byte) []byte {
// SimpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
func SimpleHashFromTwoHashes(left, right []byte) []byte {
var hasher = tmhash.New()
err := encodeByteSlice(hasher, left)
if err != nil {
panic(err)
}
panic(err)
}
err = encodeByteSlice(hasher, right)
if err != nil {
panic(err)
}
return hasher.Sum(nil)
}
func SimpleHashFromHashes(hashes [][]byte) []byte {
// Recursive impl.
switch len(hashes) {
case 0:
return nil
case 1:
return hashes[0]
default:
left := SimpleHashFromHashes(hashes[:(len(hashes)+1)/2])
right := SimpleHashFromHashes(hashes[(len(hashes)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
}
}
// NOTE: Do not implement this, use SimpleHashFromByteslices instead.
// type Byteser interface { Bytes() []byte }
// func SimpleHashFromBytesers(items []Byteser) []byte { ... }
func SimpleHashFromByteslices(bzs [][]byte) []byte {
hashes := make([][]byte, len(bzs))
for i, bz := range bzs {
hashes[i] = SimpleHashFromBytes(bz)
}
return SimpleHashFromHashes(hashes)
}
func SimpleHashFromBytes(bz []byte) []byte {
hasher := tmhash.New()
hasher.Write(bz)
panic(err)
}
return hasher.Sum(nil)
}
// SimpleHashFromHashers computes a Merkle tree from items that can be hashed.
func SimpleHashFromHashers(items []Hasher) []byte {
hashes := make([][]byte, len(items))
for i, item := range items {
hash := item.Hash()
hashes[i] = hash
}
return SimpleHashFromHashes(hashes)
return simpleHashFromHashes(hashes)
}
// SimpleHashFromMap computes a Merkle tree from sorted map.
// Like calling SimpleHashFromHashers with
// `item = []byte(Hash(key) | Hash(value))`,
// sorted by `item`.
func SimpleHashFromMap(m map[string]Hasher) []byte {
sm := NewSimpleMap()
sm := newSimpleMap()
for k, v := range m {
sm.Set(k, v)
}
return sm.Hash()
}
//----------------------------------------------------------------
// Expects hashes!
func simpleHashFromHashes(hashes [][]byte) []byte {
// Recursive impl.
switch len(hashes) {
case 0:
return nil
case 1:
return hashes[0]
default:
left := simpleHashFromHashes(hashes[:(len(hashes)+1)/2])
right := simpleHashFromHashes(hashes[(len(hashes)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
}
}

+ 2
- 1
merkle/simple_tree_test.go View File

@ -7,6 +7,7 @@ import (
. "github.com/tendermint/tmlibs/test"
"testing"
"github.com/tendermint/go-crypto/tmhash"
)
type testItem []byte
@ -21,7 +22,7 @@ func TestSimpleProof(t *testing.T) {
items := make([]Hasher, total)
for i := 0; i < total; i++ {
items[i] = testItem(cmn.RandBytes(32))
items[i] = testItem(cmn.RandBytes(tmhash.Size))
}
rootHash := SimpleHashFromHashers(items)


+ 2
- 0
merkle/types.go View File

@ -5,6 +5,7 @@ import (
"io"
)
// Tree is a Merkle tree interface.
type Tree interface {
Size() (size int)
Height() (height int8)
@ -23,6 +24,7 @@ type Tree interface {
IterateRange(start []byte, end []byte, ascending bool, fx func(key []byte, value []byte) (stop bool)) (stopped bool)
}
// Hasher represents a hashable piece of data which can be hashed in the Tree.
type Hasher interface {
Hash() []byte
}


+ 1
- 1
tmhash/hash.go View File

@ -5,7 +5,7 @@ import (
"hash"
)
var (
const (
Size = 20
BlockSize = sha256.BlockSize
)


Loading…
Cancel
Save