Hashable -> Hasher; SimpleMap upgrade; No "SimpleHashFromBinary" (#128)

* Update SimpleMap to hash both keys and values for benefit; Hashable is Hasher; Don't assume go-wire
This commit is contained in:
Jae Kwon 2018-01-25 20:05:23 -08:00 committed by Anton Kaliaev
parent 7ef6d4b813
commit 580c3db8f9
No known key found for this signature in database
GPG Key ID: 7B6881D965918214
7 changed files with 61 additions and 60 deletions

6
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: 1990fb145d5c5098b5ee467c59506e81b6c3b973667eeb63d83abd7ef831b919
updated: 2018-01-14T21:24:21.241420637-08:00
updated: 2018-01-21T03:46:56.821595635-08:00
imports:
- name: github.com/davecgh/go-spew
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
@ -65,7 +65,7 @@ imports:
- name: github.com/spf13/pflag
version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f
- name: github.com/spf13/viper
version: 8ef37cbca71638bf32f3d5e194117d4cb46da163
version: 25b30aa063fc18e48662b86996252eabdcf2f0c7
- name: github.com/syndtr/goleveldb
version: b89cc31ef7977104127d34c1bd31ebd1a9db2199
subpackages:
@ -82,7 +82,7 @@ imports:
- leveldb/table
- leveldb/util
- name: github.com/tendermint/go-wire
version: b93ebdd4f306833936c243561ec30af3455dc764
version: 0cce10e82786f2d501827fbe158747dbc4ceeb43
- name: github.com/tendermint/log15
version: f91285dece9f4875421b481da3e613d83d44f29b
- name: golang.org/x/crypto

View File

@ -18,25 +18,25 @@ func NewSimpleMap() *SimpleMap {
}
}
func (sm *SimpleMap) Set(key string, value interface{}) {
func (sm *SimpleMap) Set(key string, value Hasher) {
sm.sorted = false
// Is value Hashable?
var vBytes []byte
if hashable, ok := value.(Hashable); ok {
vBytes = hashable.Hash()
} else {
vBytes, _ = wire.MarshalBinary(value)
}
// 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: []byte(key),
Value: vBytes,
Key: khash,
Value: vhash,
})
}
// Merkle root hash of items sorted by key.
// NOTE: Behavior is undefined when key is duplicate.
// 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)
@ -51,7 +51,6 @@ func (sm *SimpleMap) Sort() {
}
// Returns a copy of sorted KVPairs.
// CONTRACT: The returned slice must not be mutated.
func (sm *SimpleMap) KVPairs() cmn.KVPairs {
sm.Sort()
kvs := make(cmn.KVPairs, len(sm.kvs))
@ -78,9 +77,9 @@ func (kv kvPair) Hash() []byte {
}
func hashKVPairs(kvs cmn.KVPairs) []byte {
kvsH := make([]Hashable, 0, len(kvs))
kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range kvs {
kvsH = append(kvsH, kvPair(kvp))
}
return SimpleHashFromHashables(kvsH)
return SimpleHashFromHashers(kvsH)
}

View File

@ -7,41 +7,47 @@ import (
"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", "value1")
assert.Equal(t, "d7df3e1d47fe38b51f8d897a88828026807a86b6", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key1", strHasher("value1"))
assert.Equal(t, "19618304d1ad2635c4238bce87f72331b22a11a1", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db.Set("key1", "value2")
assert.Equal(t, "db415336c9be129ac38259b935a49d8e9c248c88", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key1", strHasher("value2"))
assert.Equal(t, "51cb96d3d41e1714def72eb4bacc211de9ddf284", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db.Set("key1", "value1")
db.Set("key2", "value2")
assert.Equal(t, "fdb900a04c1de42bd3d924fc644e28a4bdce30ce", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
assert.Equal(t, "58a0a99d5019fdcad4bcf55942e833b2dfab9421", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db.Set("key2", "value2") // NOTE: out of order
db.Set("key1", "value1")
assert.Equal(t, "fdb900a04c1de42bd3d924fc644e28a4bdce30ce", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
assert.Equal(t, "58a0a99d5019fdcad4bcf55942e833b2dfab9421", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db.Set("key1", "value1")
db.Set("key2", "value2")
db.Set("key3", "value3")
assert.Equal(t, "488cfdaea108ef8bd406f6163555752392ae1b4a", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
db.Set("key3", strHasher("value3"))
assert.Equal(t, "cb56db3c7993e977f4c2789559ae3e5e468a6e9b", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := NewSimpleMap()
db.Set("key2", "value2") // NOTE: out of order
db.Set("key1", "value1")
db.Set("key3", "value3")
assert.Equal(t, "488cfdaea108ef8bd406f6163555752392ae1b4a", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
db.Set("key3", strHasher("value3"))
assert.Equal(t, "cb56db3c7993e977f4c2789559ae3e5e468a6e9b", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
}

View File

@ -10,8 +10,8 @@ type SimpleProof struct {
}
// proofs[0] is the proof for items[0].
func SimpleProofsFromHashables(items []Hashable) (rootHash []byte, proofs []*SimpleProof) {
trails, rootSPN := trailsFromHashables(items)
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 {
@ -109,7 +109,7 @@ func (spn *SimpleProofNode) FlattenAunts() [][]byte {
// trails[0].Hash is the leaf hash for items[0].
// trails[i].Parent.Parent....Parent == root for all i.
func trailsFromHashables(items []Hashable) (trails []*SimpleProofNode, root *SimpleProofNode) {
func trailsFromHashers(items []Hasher) (trails []*SimpleProofNode, root *SimpleProofNode) {
// Recursive impl.
switch len(items) {
case 0:
@ -118,8 +118,8 @@ func trailsFromHashables(items []Hashable) (trails []*SimpleProofNode, root *Sim
trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil}
return []*SimpleProofNode{trail}, trail
default:
lefts, leftRoot := trailsFromHashables(items[:(len(items)+1)/2])
rights, rightRoot := trailsFromHashables(items[(len(items)+1)/2:])
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

View File

@ -54,28 +54,25 @@ func SimpleHashFromHashes(hashes [][]byte) []byte {
}
}
// Convenience for SimpleHashFromHashes.
func SimpleHashFromBinaries(items []interface{}) []byte {
hashes := make([][]byte, len(items))
for i, item := range items {
hashes[i] = SimpleHashFromBinary(item)
// 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)
}
// General Convenience
func SimpleHashFromBinary(item interface{}) []byte {
func SimpleHashFromBytes(bz []byte) []byte {
hasher := ripemd160.New()
bz, err := wire.MarshalBinary(item)
if err != nil {
panic(err)
}
hasher.Write(bz)
return hasher.Sum(nil)
}
// Convenience for SimpleHashFromHashes.
func SimpleHashFromHashables(items []Hashable) []byte {
func SimpleHashFromHashers(items []Hasher) []byte {
hashes := make([][]byte, len(items))
for i, item := range items {
hash := item.Hash()
@ -84,8 +81,7 @@ func SimpleHashFromHashables(items []Hashable) []byte {
return SimpleHashFromHashes(hashes)
}
// Convenience for SimpleHashFromHashes.
func SimpleHashFromMap(m map[string]interface{}) []byte {
func SimpleHashFromMap(m map[string]Hasher) []byte {
sm := NewSimpleMap()
for k, v := range m {
sm.Set(k, v)

View File

@ -19,14 +19,14 @@ func TestSimpleProof(t *testing.T) {
total := 100
items := make([]Hashable, total)
items := make([]Hasher, total)
for i := 0; i < total; i++ {
items[i] = testItem(RandBytes(32))
}
rootHash := SimpleHashFromHashables(items)
rootHash := SimpleHashFromHashers(items)
rootHash2, proofs := SimpleProofsFromHashables(items)
rootHash2, proofs := SimpleProofsFromHashers(items)
if !bytes.Equal(rootHash, rootHash2) {
t.Errorf("Unmatched root hashes: %X vs %X", rootHash, rootHash2)

View File

@ -18,6 +18,6 @@ type Tree interface {
IterateRange(start []byte, end []byte, ascending bool, fx func(key []byte, value []byte) (stop bool)) (stopped bool)
}
type Hashable interface {
type Hasher interface {
Hash() []byte
}