Skip to content

Commit

Permalink
Get range proofs working on versioned tree
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudhead committed Dec 15, 2017
1 parent d0692f4 commit e1c5a46
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 20 deletions.
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, Versions: []int64{}}
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

0 comments on commit e1c5a46

Please sign in to comment.