diff --git a/proof_range.go b/proof_range.go index 1cf4cdac2..942ba6e2f 100644 --- a/proof_range.go +++ b/proof_range.go @@ -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"` @@ -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 @@ -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) @@ -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] } } @@ -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 }) @@ -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 }) diff --git a/proof_test.go b/proof_test.go index 7ffc6592b..f4833c043 100644 --- a/proof_test.go +++ b/proof_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tmlibs/db" "testing" ) @@ -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{ @@ -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() @@ -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 }) @@ -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: { @@ -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. @@ -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, }, @@ -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, }, @@ -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, }, @@ -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"), }, @@ -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"), }, @@ -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"), }, @@ -801,6 +809,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) { PathToKeys: []*PathToKey{ dummyPathToKey(tree, []byte{0x11}).dropRoot(), }, + Versions: []int64{1}, }, expectedError: ErrInvalidProof, }, @@ -815,6 +824,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) { Path: dummyPathToKey(tree, []byte{0x0a}), Node: dummyLeafNode([]byte{0x0a}, []byte{0x0a}), }, + Versions: []int64{}, }, expectedError: ErrInvalidProof, }, @@ -829,6 +839,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) { Path: dummyPathToKey(tree, []byte{0x99}), Node: dummyLeafNode([]byte{0x99}, []byte{0x99}), }, + Versions: []int64{}, }, expectedError: ErrInvalidProof, }, @@ -844,6 +855,7 @@ func TestTreeKeyRangeProofVerify(t *testing.T) { PathToKeys: []*PathToKey{ dummyPathToKey(tree, []byte{0x2e}), }, + Versions: []int64{1}, }, expectedError: ErrInvalidProof, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, @@ -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, }, diff --git a/tree.go b/tree.go index 9dbc580f3..85cf6e21e 100644 --- a/tree.go +++ b/tree.go @@ -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 }