forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
/
range_query_validator.go
155 lines (141 loc) · 5.46 KB
/
range_query_validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
Copyright IBM Corp. 2016 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package statebasedval
import (
"bytes"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwset"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
)
type rangeQueryValidator interface {
init(rqInfo *rwset.RangeQueryInfo, itr statedb.ResultsIterator) error
validate() (bool, error)
}
type rangeQueryResultsValidator struct {
rqInfo *rwset.RangeQueryInfo
itr statedb.ResultsIterator
}
func (v *rangeQueryResultsValidator) init(rqInfo *rwset.RangeQueryInfo, itr statedb.ResultsIterator) error {
v.rqInfo = rqInfo
v.itr = itr
return nil
}
// validate iterates through the results that are present in the range-query-info (captured during simulation)
// and the iterator (latest view of the `committed state` i.e., db + updates). At first mismatch between the results
// from the two sources (the range-query-info and the iterator), the validate retruns false.
func (v *rangeQueryResultsValidator) validate() (bool, error) {
rqResults := v.rqInfo.Results
itr := v.itr
var result statedb.QueryResult
var err error
if result, err = itr.Next(); err != nil {
return false, err
}
if len(rqResults) == 0 {
return result == nil, nil
}
for i := 0; i < len(rqResults); i++ {
kvRead := rqResults[i]
logger.Debugf("comparing kvRead=[%#v] to queryResponse=[%#v]", kvRead, result)
if result == nil {
logger.Debugf("Query response nil. Key [%s] got deleted", kvRead.Key)
return false, nil
}
versionedKV := result.(*statedb.VersionedKV)
if versionedKV.Key != kvRead.Key {
logger.Debugf("key name mismatch: Key in rwset = [%s], key in query results = [%s]", kvRead.Key, versionedKV.Key)
return false, nil
}
if !version.AreSame(versionedKV.Version, kvRead.Version) {
logger.Debugf(`Version mismatch for key [%s]: Version in rwset = [%#v], latest version = [%#v]`,
versionedKV.Key, versionedKV.Version, kvRead.Version)
return false, nil
}
if result, err = itr.Next(); err != nil {
return false, err
}
}
if result, err = itr.Next(); err != nil {
return false, err
}
if result != nil {
// iterator is not exhausted - which means that there are extra results in the given range
logger.Debugf("Extra result = [%#v]", result)
return false, nil
}
return true, nil
}
type rangeQueryHashValidator struct {
rqInfo *rwset.RangeQueryInfo
itr statedb.ResultsIterator
resultsHelper *rwset.RangeQueryResultsHelper
}
func (v *rangeQueryHashValidator) init(rqInfo *rwset.RangeQueryInfo, itr statedb.ResultsIterator) error {
v.rqInfo = rqInfo
v.itr = itr
var err error
v.resultsHelper, err = rwset.NewRangeQueryResultsHelper(true, rqInfo.ResultHash.MaxDegree)
return err
}
// validate iterates through the iterator (latest view of the `committed state` i.e., db + updates)
// and starts building the merkle tree incrementally (Both, during simulation and during validation,
// rwset.RangeQueryResultsHelper is used for building the merkle tree incrementally ).
//
// This function also keeps comparing the under-construction merkle tree with the merkle tree
// summary present in the range-query-info (built during simulation).
// This function returns false on first mismatch between the nodes of the two merkle trees
// at the desired level (the maxLevel of the merkle tree in range-query-info).
func (v *rangeQueryHashValidator) validate() (bool, error) {
itr := v.itr
lastMatchedIndex := -1
inMerkle := v.rqInfo.ResultHash
var merkle *rwset.MerkleSummary
logger.Debugf("inMerkle: %#v", inMerkle)
for {
var result statedb.QueryResult
var err error
if result, err = itr.Next(); err != nil {
return false, err
}
logger.Debugf("Processing result = %#v", result)
if result == nil {
if _, merkle, err = v.resultsHelper.Done(); err != nil {
return false, err
}
equals := inMerkle.Equal(merkle)
logger.Debugf("Combined iterator exhausted. merkle=%#v, equals=%t", merkle, equals)
return equals, nil
}
versionedKV := result.(*statedb.VersionedKV)
v.resultsHelper.AddResult(rwset.NewKVRead(versionedKV.Key, versionedKV.Version))
merkle := v.resultsHelper.GetMerkleSummary()
if merkle.MaxLevel < inMerkle.MaxLevel {
logger.Debugf("Hashes still under construction. Noting to compare yet. Need more results. Continuing...")
continue
}
if lastMatchedIndex == len(merkle.MaxLevelHashes)-1 {
logger.Debugf("Need more results to build next entry [index=%d] at level [%d]. Continuing...",
lastMatchedIndex+1, merkle.MaxLevel)
continue
}
if len(merkle.MaxLevelHashes) > len(inMerkle.MaxLevelHashes) {
logger.Debugf("Entries exceeded from what are present in the incoming merkleSummary. Validation failed")
return false, nil
}
lastMatchedIndex++
if !bytes.Equal(merkle.MaxLevelHashes[lastMatchedIndex], inMerkle.MaxLevelHashes[lastMatchedIndex]) {
logger.Debugf("Hashes does not match at index [%d]. Validation failed", lastMatchedIndex)
return false, nil
}
}
}