Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialization with proofs #23

Merged
merged 1 commit into from
Dec 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions proof_range.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (proof *KeyLastInRangeProof) Verify(startKey, endKey, key, value []byte, ro
// KeyRangeProof is proof that a range of keys does or does not exist.
type KeyRangeProof struct {
RootHash data.Bytes `json:"root_hash"`
Version int64 `json:"version"`
Versions []int64 `json:"versions"`
PathToKeys []*PathToKey `json:"paths"`

Left *pathWithNode `json:"left"`
Expand All @@ -134,11 +134,11 @@ type KeyRangeProof struct {
func (proof *KeyRangeProof) Verify(
startKey, endKey []byte, limit int, keys, values [][]byte, root []byte,
) error {
if len(proof.PathToKeys) != len(keys) || len(values) != len(keys) {
return ErrInvalidInputs
if len(proof.PathToKeys) != len(keys) || len(values) != len(keys) || len(proof.Versions) != len(keys) {
return errors.WithStack(ErrInvalidInputs)
}
if limit > 0 && len(keys) > limit {
return ErrInvalidInputs
return errors.WithStack(ErrInvalidInputs)
}

// If startKey > endKey, reverse the keys and values, since our proofs are
Expand Down Expand Up @@ -180,7 +180,7 @@ func (proof *KeyRangeProof) Verify(
leafNode := proofLeafNode{
KeyBytes: keys[i],
ValueBytes: values[i],
Version: proof.Version,
Version: proof.Versions[i],
}
if err := path.verify(leafNode.Hash(), root); err != nil {
return errors.WithStack(err)
Expand Down Expand Up @@ -235,27 +235,32 @@ func (t *Tree) getRangeWithProof(keyStart, keyEnd []byte, limit int) (
}
t.root.hashWithCount() // Ensure that all hashes are calculated.

rangeProof = &KeyRangeProof{RootHash: t.root.hash, Version: t.root.version}
rangeProof = &KeyRangeProof{RootHash: t.root.hash}
rangeStart, rangeEnd := keyStart, keyEnd
ascending := bytes.Compare(keyStart, keyEnd) == -1
if !ascending {
rangeStart, rangeEnd = rangeEnd, rangeStart
}

limited := t.IterateRangeInclusive(rangeStart, rangeEnd, ascending, func(k, v []byte) bool {
versions := []int64{}
limited := t.IterateRangeInclusive(rangeStart, rangeEnd, ascending, func(k, v []byte, version int64) bool {
keys = append(keys, k)
values = append(values, v)
versions = append(versions, version)
return len(keys) == limit
})

// Construct the paths such that they are always in ascending order.
rangeProof.PathToKeys = make([]*PathToKey, len(keys))
rangeProof.Versions = make([]int64, len(keys))
for i, k := range keys {
path, _, _ := t.root.pathToKey(t, k)
if ascending {
rangeProof.PathToKeys[i] = path
rangeProof.Versions[i] = versions[i]
} else {
rangeProof.PathToKeys[len(keys)-i-1] = path
rangeProof.Versions[len(keys)-i-1] = versions[i]
}
}

Expand Down Expand Up @@ -339,7 +344,7 @@ func (t *Tree) getFirstInRangeWithProof(keyStart, keyEnd []byte) (
proof.Version = t.root.version

// Get the first value in the range.
t.IterateRangeInclusive(keyStart, keyEnd, true, func(k, v []byte) bool {
t.IterateRangeInclusive(keyStart, keyEnd, true, func(k, v []byte, _ int64) bool {
key, value = k, v
return true
})
Expand Down Expand Up @@ -386,7 +391,7 @@ func (t *Tree) getLastInRangeWithProof(keyStart, keyEnd []byte) (
proof.Version = t.root.version

// Get the last value in the range.
t.IterateRangeInclusive(keyStart, keyEnd, false, func(k, v []byte) bool {
t.IterateRangeInclusive(keyStart, keyEnd, false, func(k, v []byte, _ int64) bool {
key, value = k, v
return true
})
Expand Down
31 changes: 22 additions & 9 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tmlibs/db"

"testing"
)
Expand Down Expand Up @@ -445,7 +446,7 @@ func TestTreeKeyLastInRangeProofsVerify(t *testing.T) {
}

func TestTreeKeyRangeProof(t *testing.T) {
tree := NewTree(nil, 0)
tree := NewVersionedTree(db.NewMemDB(), 0)
require := require.New(t)
keys := [][]byte{}
for _, ikey := range []byte{
Expand All @@ -454,6 +455,7 @@ func TestTreeKeyRangeProof(t *testing.T) {
key := []byte{ikey}
keys = append(keys, key)
tree.Set(key, []byte(randstr(8)))
tree.SaveVersion()
}
root := tree.Hash()

Expand Down Expand Up @@ -515,7 +517,7 @@ func TestTreeKeyRangeProof(t *testing.T) {

for limit := -1; limit < len(keys); limit++ {
var expected [][]byte
tree.IterateRangeInclusive(startKey, endKey, ascending, func(k, v []byte) bool {
tree.IterateRangeInclusive(startKey, endKey, ascending, func(k, v []byte, _ int64) bool {
expected = append(expected, k)
return len(expected) == limit
})
Expand Down Expand Up @@ -555,7 +557,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
keyStart: []byte{0x0},
keyEnd: []byte{0xff},
root: root,
invalidProof: &KeyRangeProof{RootHash: root, Version: 1},
invalidProof: &KeyRangeProof{RootHash: root},
expectedError: ErrInvalidProof,
},
1: {
Expand All @@ -564,7 +566,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
resultKeys: [][]byte{{0x1}, {0x2}},
resultVals: [][]byte{{0x1}},
root: root,
invalidProof: &KeyRangeProof{RootHash: root, Version: 1},
invalidProof: &KeyRangeProof{RootHash: root},
expectedError: ErrInvalidInputs,
},
2: { // An invalid proof with two adjacent paths which don't prove anything useful.
Expand Down Expand Up @@ -700,6 +702,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x11}),
Node: dummyLeafNode([]byte{0x11}, []byte{0x11}),
},
Versions: []int64{1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -716,6 +719,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x11}),
Node: dummyLeafNode([]byte{0x11}, []byte{0x11}),
},
Versions: []int64{1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -732,6 +736,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x32}),
Node: dummyLeafNode([]byte{0x32}, []byte{0x32}),
},
Versions: []int64{1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -747,6 +752,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
dummyPathToKey(tree, []byte{0x11}),
dummyPathToKey(tree, []byte{0x50}),
},
Versions: []int64{1, 1},
},
expectedError: errors.New("paths #0 and #1 are not adjacent"),
},
Expand All @@ -767,6 +773,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0xf7}),
Node: dummyLeafNode([]byte{0xf7}, []byte{0xf7}),
},
Versions: []int64{1, 1, 1},
},
expectedError: errors.New("paths #2 and #3 are not adjacent"),
},
Expand All @@ -787,6 +794,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0xa}),
Node: dummyLeafNode([]byte{0xa}, []byte{0xa}),
},
Versions: []int64{1, 1, 1},
},
expectedError: errors.New("paths #0 and #1 are not adjacent"),
},
Expand All @@ -801,6 +809,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
PathToKeys: []*PathToKey{
dummyPathToKey(tree, []byte{0x11}).dropRoot(),
},
Versions: []int64{1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -815,6 +824,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x0a}),
Node: dummyLeafNode([]byte{0x0a}, []byte{0x0a}),
},
Versions: []int64{},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -829,6 +839,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x99}),
Node: dummyLeafNode([]byte{0x99}, []byte{0x99}),
},
Versions: []int64{},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -844,6 +855,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
PathToKeys: []*PathToKey{
dummyPathToKey(tree, []byte{0x2e}),
},
Versions: []int64{1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -868,6 +880,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x50}),
Node: dummyLeafNode([]byte{0x50}, []byte{0x50}),
},
Versions: []int64{1, 1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -888,8 +901,8 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
dummyPathToKey(tree, []byte{0x2e}),
dummyPathToKey(tree, []byte{0x32}),
},
Right: nil,
Version: 1,
Right: nil,
Versions: []int64{1, 1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -914,7 +927,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x50}),
Node: dummyLeafNode([]byte{0x50}, []byte{0x50}),
},
Version: 1,
Versions: []int64{1, 1},
},
expectedError: ErrInvalidProof,
},
Expand All @@ -941,7 +954,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x50}),
Node: dummyLeafNode([]byte{0x50}, []byte{0x50}),
},
Version: 1,
Versions: []int64{1, 1, 1},
},
expectedError: nil,
},
Expand All @@ -968,7 +981,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) {
Path: dummyPathToKey(tree, []byte{0x50}),
Node: dummyLeafNode([]byte{0x50}, []byte{0x50}),
},
Version: 1,
Versions: []int64{1, 1, 1},
},
expectedError: nil,
},
Expand Down
4 changes: 2 additions & 2 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,13 @@ func (t *Tree) IterateRange(start, end []byte, ascending bool, fn func(key []byt

// IterateRangeInclusive makes a callback for all nodes with key between start and end inclusive.
// If either are nil, then it is open on that side (nil, nil is the same as Iterate)
func (t *Tree) IterateRangeInclusive(start, end []byte, ascending bool, fn func(key []byte, value []byte) bool) (stopped bool) {
func (t *Tree) IterateRangeInclusive(start, end []byte, ascending bool, fn func(key, value []byte, version int64) bool) (stopped bool) {
if t.root == nil {
return false
}
return t.root.traverseInRange(t, start, end, ascending, true, 0, func(node *Node, _ uint8) bool {
if node.height == 0 {
return fn(node.key, node.value)
return fn(node.key, node.value, node.version)
} else {
return false
}
Expand Down