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 hash: 1990fb145d5c5098b5ee467c59506e81b6c3b973667eeb63d83abd7ef831b919
updated: 2018-01-14T21:24:21.241420637-08:00 updated: 2018-01-21T03:46:56.821595635-08:00
imports: imports:
- name: github.com/davecgh/go-spew - name: github.com/davecgh/go-spew
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
@ -65,7 +65,7 @@ imports:
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f
- name: github.com/spf13/viper - name: github.com/spf13/viper
version: 8ef37cbca71638bf32f3d5e194117d4cb46da163 version: 25b30aa063fc18e48662b86996252eabdcf2f0c7
- name: github.com/syndtr/goleveldb - name: github.com/syndtr/goleveldb
version: b89cc31ef7977104127d34c1bd31ebd1a9db2199 version: b89cc31ef7977104127d34c1bd31ebd1a9db2199
subpackages: subpackages:
@ -82,7 +82,7 @@ imports:
- leveldb/table - leveldb/table
- leveldb/util - leveldb/util
- name: github.com/tendermint/go-wire - name: github.com/tendermint/go-wire
version: b93ebdd4f306833936c243561ec30af3455dc764 version: 0cce10e82786f2d501827fbe158747dbc4ceeb43
- name: github.com/tendermint/log15 - name: github.com/tendermint/log15
version: f91285dece9f4875421b481da3e613d83d44f29b version: f91285dece9f4875421b481da3e613d83d44f29b
- name: golang.org/x/crypto - 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 sm.sorted = false
// Is value Hashable? // Hash the key to blind it... why not?
var vBytes []byte khash := SimpleHashFromBytes([]byte(key))
if hashable, ok := value.(Hashable); ok {
vBytes = hashable.Hash() // And the value is hashed too, so you can
} else { // check for equality with a cached value (say)
vBytes, _ = wire.MarshalBinary(value) // and make a determination to fetch or not.
} vhash := value.Hash()
sm.kvs = append(sm.kvs, cmn.KVPair{ sm.kvs = append(sm.kvs, cmn.KVPair{
Key: []byte(key), Key: khash,
Value: vBytes, Value: vhash,
}) })
} }
// Merkle root hash of items sorted by key. // Merkle root hash of items sorted by key
// NOTE: Behavior is undefined when key is duplicate. // (UNSTABLE: and by value too if duplicate key).
func (sm *SimpleMap) Hash() []byte { func (sm *SimpleMap) Hash() []byte {
sm.Sort() sm.Sort()
return hashKVPairs(sm.kvs) return hashKVPairs(sm.kvs)
@ -51,7 +51,6 @@ func (sm *SimpleMap) Sort() {
} }
// Returns a copy of sorted KVPairs. // Returns a copy of sorted KVPairs.
// CONTRACT: The returned slice must not be mutated.
func (sm *SimpleMap) KVPairs() cmn.KVPairs { func (sm *SimpleMap) KVPairs() cmn.KVPairs {
sm.Sort() sm.Sort()
kvs := make(cmn.KVPairs, len(sm.kvs)) kvs := make(cmn.KVPairs, len(sm.kvs))
@ -78,9 +77,9 @@ func (kv kvPair) Hash() []byte {
} }
func hashKVPairs(kvs cmn.KVPairs) []byte { func hashKVPairs(kvs cmn.KVPairs) []byte {
kvsH := make([]Hashable, 0, len(kvs)) kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range kvs { for _, kvp := range kvs {
kvsH = append(kvsH, kvPair(kvp)) kvsH = append(kvsH, kvPair(kvp))
} }
return SimpleHashFromHashables(kvsH) return SimpleHashFromHashers(kvsH)
} }

View File

@ -7,41 +7,47 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
type strHasher string
func (str strHasher) Hash() []byte {
return SimpleHashFromBytes([]byte(str))
}
func TestSimpleMap(t *testing.T) { func TestSimpleMap(t *testing.T) {
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key1", "value1") db.Set("key1", strHasher("value1"))
assert.Equal(t, "d7df3e1d47fe38b51f8d897a88828026807a86b6", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") assert.Equal(t, "19618304d1ad2635c4238bce87f72331b22a11a1", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
} }
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key1", "value2") db.Set("key1", strHasher("value2"))
assert.Equal(t, "db415336c9be129ac38259b935a49d8e9c248c88", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") assert.Equal(t, "51cb96d3d41e1714def72eb4bacc211de9ddf284", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
} }
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key1", "value1") db.Set("key1", strHasher("value1"))
db.Set("key2", "value2") db.Set("key2", strHasher("value2"))
assert.Equal(t, "fdb900a04c1de42bd3d924fc644e28a4bdce30ce", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") assert.Equal(t, "58a0a99d5019fdcad4bcf55942e833b2dfab9421", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
} }
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key2", "value2") // NOTE: out of order db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", "value1") db.Set("key1", strHasher("value1"))
assert.Equal(t, "fdb900a04c1de42bd3d924fc644e28a4bdce30ce", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") assert.Equal(t, "58a0a99d5019fdcad4bcf55942e833b2dfab9421", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
} }
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key1", "value1") db.Set("key1", strHasher("value1"))
db.Set("key2", "value2") db.Set("key2", strHasher("value2"))
db.Set("key3", "value3") db.Set("key3", strHasher("value3"))
assert.Equal(t, "488cfdaea108ef8bd406f6163555752392ae1b4a", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") assert.Equal(t, "cb56db3c7993e977f4c2789559ae3e5e468a6e9b", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
} }
{ {
db := NewSimpleMap() db := NewSimpleMap()
db.Set("key2", "value2") // NOTE: out of order db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", "value1") db.Set("key1", strHasher("value1"))
db.Set("key3", "value3") db.Set("key3", strHasher("value3"))
assert.Equal(t, "488cfdaea108ef8bd406f6163555752392ae1b4a", fmt.Sprintf("%x", db.Hash()), "Hash didn't match") 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]. // proofs[0] is the proof for items[0].
func SimpleProofsFromHashables(items []Hashable) (rootHash []byte, proofs []*SimpleProof) { func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleProof) {
trails, rootSPN := trailsFromHashables(items) trails, rootSPN := trailsFromHashers(items)
rootHash = rootSPN.Hash rootHash = rootSPN.Hash
proofs = make([]*SimpleProof, len(items)) proofs = make([]*SimpleProof, len(items))
for i, trail := range trails { 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[0].Hash is the leaf hash for items[0].
// trails[i].Parent.Parent....Parent == root for all i. // 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. // Recursive impl.
switch len(items) { switch len(items) {
case 0: case 0:
@ -118,8 +118,8 @@ func trailsFromHashables(items []Hashable) (trails []*SimpleProofNode, root *Sim
trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil} trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil}
return []*SimpleProofNode{trail}, trail return []*SimpleProofNode{trail}, trail
default: default:
lefts, leftRoot := trailsFromHashables(items[:(len(items)+1)/2]) lefts, leftRoot := trailsFromHashers(items[:(len(items)+1)/2])
rights, rightRoot := trailsFromHashables(items[(len(items)+1)/2:]) rights, rightRoot := trailsFromHashers(items[(len(items)+1)/2:])
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
root := &SimpleProofNode{rootHash, nil, nil, nil} root := &SimpleProofNode{rootHash, nil, nil, nil}
leftRoot.Parent = root leftRoot.Parent = root

View File

@ -54,28 +54,25 @@ func SimpleHashFromHashes(hashes [][]byte) []byte {
} }
} }
// Convenience for SimpleHashFromHashes. // NOTE: Do not implement this, use SimpleHashFromByteslices instead.
func SimpleHashFromBinaries(items []interface{}) []byte { // type Byteser interface { Bytes() []byte }
hashes := make([][]byte, len(items)) // func SimpleHashFromBytesers(items []Byteser) []byte { ... }
for i, item := range items {
hashes[i] = SimpleHashFromBinary(item) func SimpleHashFromByteslices(bzs [][]byte) []byte {
hashes := make([][]byte, len(bzs))
for i, bz := range bzs {
hashes[i] = SimpleHashFromBytes(bz)
} }
return SimpleHashFromHashes(hashes) return SimpleHashFromHashes(hashes)
} }
// General Convenience func SimpleHashFromBytes(bz []byte) []byte {
func SimpleHashFromBinary(item interface{}) []byte {
hasher := ripemd160.New() hasher := ripemd160.New()
bz, err := wire.MarshalBinary(item)
if err != nil {
panic(err)
}
hasher.Write(bz) hasher.Write(bz)
return hasher.Sum(nil) return hasher.Sum(nil)
} }
// Convenience for SimpleHashFromHashes. func SimpleHashFromHashers(items []Hasher) []byte {
func SimpleHashFromHashables(items []Hashable) []byte {
hashes := make([][]byte, len(items)) hashes := make([][]byte, len(items))
for i, item := range items { for i, item := range items {
hash := item.Hash() hash := item.Hash()
@ -84,8 +81,7 @@ func SimpleHashFromHashables(items []Hashable) []byte {
return SimpleHashFromHashes(hashes) return SimpleHashFromHashes(hashes)
} }
// Convenience for SimpleHashFromHashes. func SimpleHashFromMap(m map[string]Hasher) []byte {
func SimpleHashFromMap(m map[string]interface{}) []byte {
sm := NewSimpleMap() sm := NewSimpleMap()
for k, v := range m { for k, v := range m {
sm.Set(k, v) sm.Set(k, v)

View File

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