From 9f04935caa0ba104421c5c856bae92d5307e3869 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 22 May 2018 13:05:27 -0400 Subject: [PATCH] merkle: remove unused funcs. unexport simplemap. improv docs --- merkle/simple_map.go | 44 ++++++++++-------- merkle/simple_map_test.go | 17 ++++--- merkle/simple_proof.go | 18 ++++++-- merkle/simple_tree.go | 93 ++++++++++++-------------------------- merkle/simple_tree_test.go | 3 +- merkle/types.go | 2 + tmhash/hash.go | 2 +- 7 files changed, 84 insertions(+), 95 deletions(-) diff --git a/merkle/simple_map.go b/merkle/simple_map.go index 41d1ccd56..b073ecdd6 100644 --- a/merkle/simple_map.go +++ b/merkle/simple_map.go @@ -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) } diff --git a/merkle/simple_map_test.go b/merkle/simple_map_test.go index 6e1004db2..9d0c25a2a 100644 --- a/merkle/simple_map_test.go +++ b/merkle/simple_map_test.go @@ -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")) diff --git a/merkle/simple_proof.go b/merkle/simple_proof.go index ca6ccf372..f52d1ad9f 100644 --- a/merkle/simple_proof.go +++ b/merkle/simple_proof.go @@ -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{} diff --git a/merkle/simple_tree.go b/merkle/simple_tree.go index 3c31b1337..c23f84264 100644 --- a/merkle/simple_tree.go +++ b/merkle/simple_tree.go @@ -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) + } +} diff --git a/merkle/simple_tree_test.go b/merkle/simple_tree_test.go index 8c4ed01f8..db8e3d7ff 100644 --- a/merkle/simple_tree_test.go +++ b/merkle/simple_tree_test.go @@ -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) diff --git a/merkle/types.go b/merkle/types.go index a0c491a7e..ddc420659 100644 --- a/merkle/types.go +++ b/merkle/types.go @@ -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 } diff --git a/tmhash/hash.go b/tmhash/hash.go index 9f684ce01..1b29d8680 100644 --- a/tmhash/hash.go +++ b/tmhash/hash.go @@ -5,7 +5,7 @@ import ( "hash" ) -var ( +const ( Size = 20 BlockSize = sha256.BlockSize )