@ -0,0 +1,4 @@ | |||||
## Simple Merkle Tree | |||||
For smaller static data structures that don't require immutable snapshots or mutability; | |||||
for instance the transactions and validation signatures of a block can be hashed using this simple merkle tree logic. |
@ -0,0 +1,84 @@ | |||||
package merkle | |||||
import ( | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
"golang.org/x/crypto/ripemd160" | |||||
) | |||||
type SimpleMap struct { | |||||
kvs cmn.KVPairs | |||||
sorted bool | |||||
} | |||||
func NewSimpleMap() *SimpleMap { | |||||
return &SimpleMap{ | |||||
kvs: nil, | |||||
sorted: false, | |||||
} | |||||
} | |||||
func (sm *SimpleMap) Set(key string, value Hasher) { | |||||
sm.sorted = false | |||||
// Hash the key to blind it... why not? | |||||
khash := SimpleHashFromBytes([]byte(key)) | |||||
// And the value is hashed too, so you can | |||||
// check for equality with a cached value (say) | |||||
// and make a determination to fetch or not. | |||||
vhash := value.Hash() | |||||
sm.kvs = append(sm.kvs, cmn.KVPair{ | |||||
Key: khash, | |||||
Value: vhash, | |||||
}) | |||||
} | |||||
// Merkle root hash of items sorted by key | |||||
// (UNSTABLE: and by value too if duplicate key). | |||||
func (sm *SimpleMap) Hash() []byte { | |||||
sm.Sort() | |||||
return hashKVPairs(sm.kvs) | |||||
} | |||||
func (sm *SimpleMap) Sort() { | |||||
if sm.sorted { | |||||
return | |||||
} | |||||
sm.kvs.Sort() | |||||
sm.sorted = true | |||||
} | |||||
// Returns a copy of sorted KVPairs. | |||||
func (sm *SimpleMap) KVPairs() cmn.KVPairs { | |||||
sm.Sort() | |||||
kvs := make(cmn.KVPairs, len(sm.kvs)) | |||||
copy(kvs, sm.kvs) | |||||
return kvs | |||||
} | |||||
//---------------------------------------- | |||||
// A local extension to KVPair that can be hashed. | |||||
type KVPair cmn.KVPair | |||||
func (kv KVPair) Hash() []byte { | |||||
hasher := ripemd160.New() | |||||
err := encodeByteSlice(hasher, kv.Key) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
err = encodeByteSlice(hasher, kv.Value) | |||||
if err != nil { | |||||
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)) | |||||
} | |||||
return SimpleHashFromHashers(kvsH) | |||||
} |
@ -0,0 +1,53 @@ | |||||
package merkle | |||||
import ( | |||||
"fmt" | |||||
"testing" | |||||
"github.com/stretchr/testify/assert" | |||||
) | |||||
type strHasher string | |||||
func (str strHasher) Hash() []byte { | |||||
return SimpleHashFromBytes([]byte(str)) | |||||
} | |||||
func TestSimpleMap(t *testing.T) { | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
assert.Equal(t, "acdb4f121bc6f25041eb263ab463f1cd79236a32", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key1", strHasher("value2")) | |||||
assert.Equal(t, "b8cbf5adee8c524e14f531da9b49adbbbd66fffa", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key2", strHasher("value2")) | |||||
assert.Equal(t, "1708aabc85bbe00242d3db8c299516aa54e48c38", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key2", strHasher("value2")) // NOTE: out of order | |||||
db.Set("key1", strHasher("value1")) | |||||
assert.Equal(t, "1708aabc85bbe00242d3db8c299516aa54e48c38", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key2", strHasher("value2")) | |||||
db.Set("key3", strHasher("value3")) | |||||
assert.Equal(t, "e728afe72ce351eed6aca65c5f78da19b9a6e214", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
{ | |||||
db := NewSimpleMap() | |||||
db.Set("key2", strHasher("value2")) // NOTE: out of order | |||||
db.Set("key1", strHasher("value1")) | |||||
db.Set("key3", strHasher("value3")) | |||||
assert.Equal(t, "e728afe72ce351eed6aca65c5f78da19b9a6e214", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") | |||||
} | |||||
} |
@ -0,0 +1,144 @@ | |||||
package merkle | |||||
import ( | |||||
"bytes" | |||||
"fmt" | |||||
) | |||||
type SimpleProof struct { | |||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child. | |||||
} | |||||
// proofs[0] is the proof for items[0]. | |||||
func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleProof) { | |||||
trails, rootSPN := trailsFromHashers(items) | |||||
rootHash = rootSPN.Hash | |||||
proofs = make([]*SimpleProof, len(items)) | |||||
for i, trail := range trails { | |||||
proofs[i] = &SimpleProof{ | |||||
Aunts: trail.FlattenAunts(), | |||||
} | |||||
} | |||||
return | |||||
} | |||||
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*SimpleProof) { | |||||
sm := NewSimpleMap() | |||||
for k, v := range m { | |||||
sm.Set(k, v) | |||||
} | |||||
sm.Sort() | |||||
kvs := sm.kvs | |||||
kvsH := make([]Hasher, 0, len(kvs)) | |||||
for _, kvp := range kvs { | |||||
kvsH = append(kvsH, KVPair(kvp)) | |||||
} | |||||
return SimpleProofsFromHashers(kvsH) | |||||
} | |||||
// Verify that leafHash is a leaf hash of the simple-merkle-tree | |||||
// which hashes to rootHash. | |||||
func (sp *SimpleProof) Verify(index int, total int, leafHash []byte, rootHash []byte) bool { | |||||
computedHash := computeHashFromAunts(index, total, leafHash, sp.Aunts) | |||||
return computedHash != nil && bytes.Equal(computedHash, rootHash) | |||||
} | |||||
func (sp *SimpleProof) String() string { | |||||
return sp.StringIndented("") | |||||
} | |||||
func (sp *SimpleProof) StringIndented(indent string) string { | |||||
return fmt.Sprintf(`SimpleProof{ | |||||
%s Aunts: %X | |||||
%s}`, | |||||
indent, sp.Aunts, | |||||
indent) | |||||
} | |||||
// Use the leafHash and innerHashes to get the root merkle hash. | |||||
// If the length of the innerHashes slice isn't exactly correct, the result is nil. | |||||
// Recursive impl. | |||||
func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][]byte) []byte { | |||||
if index >= total || index < 0 || total <= 0 { | |||||
return nil | |||||
} | |||||
switch total { | |||||
case 0: | |||||
panic("Cannot call computeHashFromAunts() with 0 total") | |||||
case 1: | |||||
if len(innerHashes) != 0 { | |||||
return nil | |||||
} | |||||
return leafHash | |||||
default: | |||||
if len(innerHashes) == 0 { | |||||
return nil | |||||
} | |||||
numLeft := (total + 1) / 2 | |||||
if index < numLeft { | |||||
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) | |||||
if leftHash == nil { | |||||
return nil | |||||
} | |||||
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1]) | |||||
} | |||||
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) | |||||
if rightHash == nil { | |||||
return nil | |||||
} | |||||
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash) | |||||
} | |||||
} | |||||
// 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 | |||||
// hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child. | |||||
type SimpleProofNode struct { | |||||
Hash []byte | |||||
Parent *SimpleProofNode | |||||
Left *SimpleProofNode // Left sibling (only one of Left,Right is set) | |||||
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. | |||||
func (spn *SimpleProofNode) FlattenAunts() [][]byte { | |||||
// Nonrecursive impl. | |||||
innerHashes := [][]byte{} | |||||
for spn != nil { | |||||
if spn.Left != nil { | |||||
innerHashes = append(innerHashes, spn.Left.Hash) | |||||
} else if spn.Right != nil { | |||||
innerHashes = append(innerHashes, spn.Right.Hash) | |||||
} else { | |||||
break | |||||
} | |||||
spn = spn.Parent | |||||
} | |||||
return innerHashes | |||||
} | |||||
// trails[0].Hash is the leaf hash for items[0]. | |||||
// trails[i].Parent.Parent....Parent == root for all i. | |||||
func trailsFromHashers(items []Hasher) (trails []*SimpleProofNode, root *SimpleProofNode) { | |||||
// Recursive impl. | |||||
switch len(items) { | |||||
case 0: | |||||
return nil, nil | |||||
case 1: | |||||
trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil} | |||||
return []*SimpleProofNode{trail}, trail | |||||
default: | |||||
lefts, leftRoot := trailsFromHashers(items[:(len(items)+1)/2]) | |||||
rights, rightRoot := trailsFromHashers(items[(len(items)+1)/2:]) | |||||
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) | |||||
root := &SimpleProofNode{rootHash, nil, nil, nil} | |||||
leftRoot.Parent = root | |||||
leftRoot.Right = rightRoot | |||||
rightRoot.Parent = root | |||||
rightRoot.Left = leftRoot | |||||
return append(lefts, rights...), root | |||||
} | |||||
} |
@ -0,0 +1,91 @@ | |||||
/* | |||||
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 ( | |||||
"golang.org/x/crypto/ripemd160" | |||||
) | |||||
func SimpleHashFromTwoHashes(left []byte, right []byte) []byte { | |||||
var hasher = ripemd160.New() | |||||
err := encodeByteSlice(hasher, left) | |||||
if err != nil { | |||||
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 := ripemd160.New() | |||||
hasher.Write(bz) | |||||
return hasher.Sum(nil) | |||||
} | |||||
func SimpleHashFromHashers(items []Hasher) []byte { | |||||
hashes := make([][]byte, len(items)) | |||||
for i, item := range items { | |||||
hash := item.Hash() | |||||
hashes[i] = hash | |||||
} | |||||
return SimpleHashFromHashes(hashes) | |||||
} | |||||
func SimpleHashFromMap(m map[string]Hasher) []byte { | |||||
sm := NewSimpleMap() | |||||
for k, v := range m { | |||||
sm.Set(k, v) | |||||
} | |||||
return sm.Hash() | |||||
} |
@ -0,0 +1,87 @@ | |||||
package merkle | |||||
import ( | |||||
"bytes" | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
. "github.com/tendermint/tmlibs/test" | |||||
"testing" | |||||
) | |||||
type testItem []byte | |||||
func (tI testItem) Hash() []byte { | |||||
return []byte(tI) | |||||
} | |||||
func TestSimpleProof(t *testing.T) { | |||||
total := 100 | |||||
items := make([]Hasher, total) | |||||
for i := 0; i < total; i++ { | |||||
items[i] = testItem(cmn.RandBytes(32)) | |||||
} | |||||
rootHash := SimpleHashFromHashers(items) | |||||
rootHash2, proofs := SimpleProofsFromHashers(items) | |||||
if !bytes.Equal(rootHash, rootHash2) { | |||||
t.Errorf("Unmatched root hashes: %X vs %X", rootHash, rootHash2) | |||||
} | |||||
// For each item, check the trail. | |||||
for i, item := range items { | |||||
itemHash := item.Hash() | |||||
proof := proofs[i] | |||||
// Verify success | |||||
ok := proof.Verify(i, total, itemHash, rootHash) | |||||
if !ok { | |||||
t.Errorf("Verification failed for index %v.", i) | |||||
} | |||||
// Wrong item index should make it fail | |||||
{ | |||||
ok = proof.Verify((i+1)%total, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong index %v.", i) | |||||
} | |||||
} | |||||
// Trail too long should make it fail | |||||
origAunts := proof.Aunts | |||||
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32)) | |||||
{ | |||||
ok = proof.Verify(i, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong trail length.") | |||||
} | |||||
} | |||||
proof.Aunts = origAunts | |||||
// Trail too short should make it fail | |||||
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1] | |||||
{ | |||||
ok = proof.Verify(i, total, itemHash, rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for wrong trail length.") | |||||
} | |||||
} | |||||
proof.Aunts = origAunts | |||||
// Mutating the itemHash should make it fail. | |||||
ok = proof.Verify(i, total, MutateByteSlice(itemHash), rootHash) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for mutated leaf hash") | |||||
} | |||||
// Mutating the rootHash should make it fail. | |||||
ok = proof.Verify(i, total, itemHash, MutateByteSlice(rootHash)) | |||||
if ok { | |||||
t.Errorf("Expected verification to fail for mutated root hash") | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,47 @@ | |||||
package merkle | |||||
import ( | |||||
"encoding/binary" | |||||
"io" | |||||
) | |||||
type Tree interface { | |||||
Size() (size int) | |||||
Height() (height int8) | |||||
Has(key []byte) (has bool) | |||||
Proof(key []byte) (value []byte, proof []byte, exists bool) // TODO make it return an index | |||||
Get(key []byte) (index int, value []byte, exists bool) | |||||
GetByIndex(index int) (key []byte, value []byte) | |||||
Set(key []byte, value []byte) (updated bool) | |||||
Remove(key []byte) (value []byte, removed bool) | |||||
HashWithCount() (hash []byte, count int) | |||||
Hash() (hash []byte) | |||||
Save() (hash []byte) | |||||
Load(hash []byte) | |||||
Copy() Tree | |||||
Iterate(func(key []byte, value []byte) (stop bool)) (stopped bool) | |||||
IterateRange(start []byte, end []byte, ascending bool, fx func(key []byte, value []byte) (stop bool)) (stopped bool) | |||||
} | |||||
type Hasher interface { | |||||
Hash() []byte | |||||
} | |||||
//----------------------------------------------------------------------- | |||||
// NOTE: these are duplicated from go-amino so we dont need go-amino as a dep | |||||
func encodeByteSlice(w io.Writer, bz []byte) (err error) { | |||||
err = encodeUvarint(w, uint64(len(bz))) | |||||
if err != nil { | |||||
return | |||||
} | |||||
_, err = w.Write(bz) | |||||
return | |||||
} | |||||
func encodeUvarint(w io.Writer, i uint64) (err error) { | |||||
var buf [10]byte | |||||
n := binary.PutUvarint(buf[:], i) | |||||
_, err = w.Write(buf[0:n]) | |||||
return | |||||
} |