github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/validation/validator_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package validation 8 9 import ( 10 "crypto/sha256" 11 "fmt" 12 "os" 13 "testing" 14 15 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 16 "github.com/hyperledger/fabric-protos-go/peer" 17 "github.com/osdi23p228/fabric/common/flogging" 18 "github.com/osdi23p228/fabric/core/ledger/internal/version" 19 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate" 20 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 21 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb" 22 "github.com/osdi23p228/fabric/core/ledger/util" 23 "github.com/stretchr/testify/require" 24 ) 25 26 type keyValue struct { 27 namespace string 28 collection string 29 key string 30 keyHash []byte 31 value []byte 32 version *version.Height 33 } 34 35 const ( 36 levelDBtestEnvName = "levelDB_LockBasedTxMgr" 37 couchDBtestEnvName = "couchDB_LockBasedTxMgr" 38 ) 39 40 var ( 41 // Tests will be run against each environment in this array 42 // For example, to skip CouchDB tests, remove &couchDBLockBasedEnv{} 43 testEnvs = map[string]privacyenabledstate.TestEnv{ 44 levelDBtestEnvName: &privacyenabledstate.LevelDBTestEnv{}, 45 couchDBtestEnvName: &privacyenabledstate.CouchDBTestEnv{}, 46 } 47 48 testHashFunc = func(data []byte) ([]byte, error) { 49 h := sha256.New() 50 if _, err := h.Write(data); err != nil { 51 return nil, err 52 } 53 return h.Sum(nil), nil 54 } 55 ) 56 57 func TestMain(m *testing.M) { 58 flogging.ActivateSpec("statevalidator,statebasedval,statecouchdb=debug") 59 exitCode := m.Run() 60 for _, testEnv := range testEnvs { 61 testEnv.StopExternalResource() 62 } 63 os.Exit(exitCode) 64 } 65 66 func TestValidatorBulkLoadingOfCache(t *testing.T) { 67 testDBEnv := testEnvs[couchDBtestEnvName] 68 testDBEnv.Init(t) 69 defer testDBEnv.Cleanup() 70 db := testDBEnv.GetDBHandle("testdb") 71 72 testValidator := &validator{db: db, hashFunc: testHashFunc} 73 74 //populate db with initial data 75 batch := privacyenabledstate.NewUpdateBatch() 76 77 // Create two public KV pairs 78 pubKV1 := keyValue{namespace: "ns1", key: "key1", value: []byte("value1"), version: version.NewHeight(1, 0)} 79 pubKV2 := keyValue{namespace: "ns1", key: "key2", value: []byte("value2"), version: version.NewHeight(1, 1)} 80 81 // Create two hashed KV pairs 82 hashedKV1 := keyValue{namespace: "ns2", collection: "col1", key: "hashedPvtKey1", 83 keyHash: util.ComputeStringHash("hashedPvtKey1"), value: []byte("value1"), 84 version: version.NewHeight(1, 2)} 85 hashedKV2 := keyValue{namespace: "ns2", collection: "col2", key: "hashedPvtKey2", 86 keyHash: util.ComputeStringHash("hashedPvtKey2"), value: []byte("value2"), 87 version: version.NewHeight(1, 3)} 88 89 // Store the public and hashed KV pairs to DB 90 batch.PubUpdates.Put(pubKV1.namespace, pubKV1.key, pubKV1.value, pubKV1.version) 91 batch.PubUpdates.Put(pubKV2.namespace, pubKV2.key, pubKV2.value, pubKV2.version) 92 batch.HashUpdates.Put(hashedKV1.namespace, hashedKV1.collection, hashedKV1.keyHash, hashedKV1.value, hashedKV1.version) 93 batch.HashUpdates.Put(hashedKV2.namespace, hashedKV2.collection, hashedKV2.keyHash, hashedKV2.value, hashedKV2.version) 94 95 db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4)) 96 97 // Construct read set for transaction 1. It contains two public KV pairs (pubKV1, pubKV2) and two 98 // hashed KV pairs (hashedKV1, hashedKV2). 99 rwsetBuilder1 := rwsetutil.NewRWSetBuilder() 100 rwsetBuilder1.AddToReadSet(pubKV1.namespace, pubKV1.key, pubKV1.version) 101 rwsetBuilder1.AddToReadSet(pubKV2.namespace, pubKV2.key, pubKV2.version) 102 rwsetBuilder1.AddToHashedReadSet(hashedKV1.namespace, hashedKV1.collection, hashedKV1.key, hashedKV1.version) 103 rwsetBuilder1.AddToHashedReadSet(hashedKV2.namespace, hashedKV2.collection, hashedKV2.key, hashedKV2.version) 104 105 // Construct read set for transaction 1. It contains KV pairs which are not in the state db. 106 rwsetBuilder2 := rwsetutil.NewRWSetBuilder() 107 rwsetBuilder2.AddToReadSet("ns3", "key1", nil) 108 rwsetBuilder2.AddToHashedReadSet("ns3", "col1", "hashedPvtKey1", nil) 109 110 // Construct internal block 111 transRWSets := getTestPubSimulationRWSet(t, rwsetBuilder1, rwsetBuilder2) 112 var trans []*transaction 113 for i, tranRWSet := range transRWSets { 114 tx := &transaction{ 115 id: fmt.Sprintf("txid-%d", i), 116 indexInBlock: i, 117 validationCode: peer.TxValidationCode_VALID, 118 rwset: tranRWSet, 119 } 120 trans = append(trans, tx) 121 } 122 blk := &block{num: 1, txs: trans} 123 124 if testValidator.db.IsBulkOptimizable() { 125 126 db := testValidator.db 127 bulkOptimizable, _ := db.VersionedDB.(statedb.BulkOptimizable) 128 129 // Clear cache loaded during ApplyPrivacyAwareUpdates() 130 testValidator.db.ClearCachedVersions() 131 132 testValidator.preLoadCommittedVersionOfRSet(blk) 133 134 // pubKV1 should be found in cache 135 version, keyFound := bulkOptimizable.GetCachedVersion(pubKV1.namespace, pubKV1.key) 136 require.True(t, keyFound) 137 require.Equal(t, pubKV1.version, version) 138 139 // pubKV2 should be found in cache 140 version, keyFound = bulkOptimizable.GetCachedVersion(pubKV2.namespace, pubKV2.key) 141 require.True(t, keyFound) 142 require.Equal(t, pubKV2.version, version) 143 144 // [ns3, key1] should be found in cache as it was in the readset of transaction 1 though it is 145 // not in the state db but the version would be nil 146 version, keyFound = bulkOptimizable.GetCachedVersion("ns3", "key1") 147 require.True(t, keyFound) 148 require.Nil(t, version) 149 150 // [ns4, key1] should not be found in cache as it was not loaded 151 version, keyFound = bulkOptimizable.GetCachedVersion("ns4", "key1") 152 require.False(t, keyFound) 153 require.Nil(t, version) 154 155 // hashedKV1 should be found in cache 156 version, keyFound = testValidator.db.GetCachedKeyHashVersion(hashedKV1.namespace, 157 hashedKV1.collection, hashedKV1.keyHash) 158 require.True(t, keyFound) 159 require.Equal(t, hashedKV1.version, version) 160 161 // hashedKV2 should be found in cache 162 version, keyFound = testValidator.db.GetCachedKeyHashVersion(hashedKV2.namespace, 163 hashedKV2.collection, hashedKV2.keyHash) 164 require.True(t, keyFound) 165 require.Equal(t, hashedKV2.version, version) 166 167 // [ns3, col1, hashedPvtKey1] should be found in cache as it was in the readset of transaction 2 though it is 168 // not in the state db 169 version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns3", "col1", util.ComputeStringHash("hashedPvtKey1")) 170 require.True(t, keyFound) 171 require.Nil(t, version) 172 173 // [ns4, col, key1] should not be found in cache as it was not loaded 174 version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns4", "col1", util.ComputeStringHash("key1")) 175 require.False(t, keyFound) 176 require.Nil(t, version) 177 178 // Clear cache 179 testValidator.db.ClearCachedVersions() 180 181 // pubKV1 should not be found in cache as cahce got emptied 182 version, keyFound = bulkOptimizable.GetCachedVersion(pubKV1.namespace, pubKV1.key) 183 require.False(t, keyFound) 184 require.Nil(t, version) 185 186 // [ns3, col1, key1] should not be found in cache as cahce got emptied 187 version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns3", "col1", util.ComputeStringHash("hashedPvtKey1")) 188 require.False(t, keyFound) 189 require.Nil(t, version) 190 } 191 } 192 193 func TestValidator(t *testing.T) { 194 testDBEnv := testEnvs[levelDBtestEnvName] 195 testDBEnv.Init(t) 196 defer testDBEnv.Cleanup() 197 db := testDBEnv.GetDBHandle("TestDB") 198 199 //populate db with initial data 200 batch := privacyenabledstate.NewUpdateBatch() 201 batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0)) 202 batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1)) 203 batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2)) 204 batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3)) 205 batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4)) 206 db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4)) 207 208 testValidator := &validator{db: db, hashFunc: testHashFunc} 209 210 //rwset1 should be valid 211 rwsetBuilder1 := rwsetutil.NewRWSetBuilder() 212 rwsetBuilder1.AddToReadSet("ns1", "key1", version.NewHeight(1, 0)) 213 rwsetBuilder1.AddToReadSet("ns2", "key2", nil) 214 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{}) 215 216 //rwset2 should not be valid 217 rwsetBuilder2 := rwsetutil.NewRWSetBuilder() 218 rwsetBuilder2.AddToReadSet("ns1", "key1", version.NewHeight(1, 1)) 219 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0}) 220 221 //rwset3 should not be valid 222 rwsetBuilder3 := rwsetutil.NewRWSetBuilder() 223 rwsetBuilder3.AddToReadSet("ns1", "key1", nil) 224 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder3), []int{0}) 225 226 // rwset4 and rwset5 within same block - rwset4 should be valid and makes rwset5 as invalid 227 rwsetBuilder4 := rwsetutil.NewRWSetBuilder() 228 rwsetBuilder4.AddToReadSet("ns1", "key1", version.NewHeight(1, 0)) 229 rwsetBuilder4.AddToWriteSet("ns1", "key1", []byte("value1_new")) 230 231 rwsetBuilder5 := rwsetutil.NewRWSetBuilder() 232 rwsetBuilder5.AddToReadSet("ns1", "key1", version.NewHeight(1, 0)) 233 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder4, rwsetBuilder5), []int{1}) 234 } 235 236 func TestPhantomValidation(t *testing.T) { 237 testDBEnv := testEnvs[levelDBtestEnvName] 238 testDBEnv.Init(t) 239 defer testDBEnv.Cleanup() 240 db := testDBEnv.GetDBHandle("TestDB") 241 242 //populate db with initial data 243 batch := privacyenabledstate.NewUpdateBatch() 244 batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0)) 245 batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1)) 246 batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2)) 247 batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3)) 248 batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4)) 249 db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4)) 250 251 testValidator := &validator{db: db, hashFunc: testHashFunc} 252 253 //rwset1 should be valid 254 rwsetBuilder1 := rwsetutil.NewRWSetBuilder() 255 rqi1 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: true} 256 rwsetutil.SetRawReads(rqi1, []*kvrwset.KVRead{ 257 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 258 rwsetutil.NewKVRead("key3", version.NewHeight(1, 2))}) 259 rwsetBuilder1.AddToRangeQuerySet("ns1", rqi1) 260 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{}) 261 262 //rwset2 should not be valid - Version of key4 changed 263 rwsetBuilder2 := rwsetutil.NewRWSetBuilder() 264 rqi2 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false} 265 rwsetutil.SetRawReads(rqi2, []*kvrwset.KVRead{ 266 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 267 rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)), 268 rwsetutil.NewKVRead("key4", version.NewHeight(1, 2))}) 269 rwsetBuilder2.AddToRangeQuerySet("ns1", rqi2) 270 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0}) 271 272 //rwset3 should not be valid - simulate key3 got committed to db 273 rwsetBuilder3 := rwsetutil.NewRWSetBuilder() 274 rqi3 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false} 275 rwsetutil.SetRawReads(rqi3, []*kvrwset.KVRead{ 276 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 277 rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))}) 278 rwsetBuilder3.AddToRangeQuerySet("ns1", rqi3) 279 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder3), []int{0}) 280 281 // //Remove a key in rwset4 and rwset5 should become invalid 282 rwsetBuilder4 := rwsetutil.NewRWSetBuilder() 283 rwsetBuilder4.AddToWriteSet("ns1", "key3", nil) 284 rwsetBuilder5 := rwsetutil.NewRWSetBuilder() 285 rqi5 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false} 286 rwsetutil.SetRawReads(rqi5, []*kvrwset.KVRead{ 287 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 288 rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)), 289 rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))}) 290 rwsetBuilder5.AddToRangeQuerySet("ns1", rqi5) 291 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder4, rwsetBuilder5), []int{1}) 292 293 //Add a key in rwset6 and rwset7 should become invalid 294 rwsetBuilder6 := rwsetutil.NewRWSetBuilder() 295 rwsetBuilder6.AddToWriteSet("ns1", "key2_1", []byte("value2_1")) 296 297 rwsetBuilder7 := rwsetutil.NewRWSetBuilder() 298 rqi7 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false} 299 rwsetutil.SetRawReads(rqi7, []*kvrwset.KVRead{ 300 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 301 rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)), 302 rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))}) 303 rwsetBuilder7.AddToRangeQuerySet("ns1", rqi7) 304 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder6, rwsetBuilder7), []int{1}) 305 } 306 307 func TestPhantomHashBasedValidation(t *testing.T) { 308 testDBEnv := testEnvs[levelDBtestEnvName] 309 testDBEnv.Init(t) 310 defer testDBEnv.Cleanup() 311 db := testDBEnv.GetDBHandle("TestDB") 312 313 //populate db with initial data 314 batch := privacyenabledstate.NewUpdateBatch() 315 batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0)) 316 batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1)) 317 batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2)) 318 batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3)) 319 batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4)) 320 batch.PubUpdates.Put("ns1", "key6", []byte("value6"), version.NewHeight(1, 5)) 321 batch.PubUpdates.Put("ns1", "key7", []byte("value7"), version.NewHeight(1, 6)) 322 batch.PubUpdates.Put("ns1", "key8", []byte("value8"), version.NewHeight(1, 7)) 323 batch.PubUpdates.Put("ns1", "key9", []byte("value9"), version.NewHeight(1, 8)) 324 db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 8)) 325 326 testValidator := &validator{db: db, hashFunc: testHashFunc} 327 328 rwsetBuilder1 := rwsetutil.NewRWSetBuilder() 329 rqi1 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key9", ItrExhausted: true} 330 kvReadsDuringSimulation1 := []*kvrwset.KVRead{ 331 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 332 rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)), 333 rwsetutil.NewKVRead("key4", version.NewHeight(1, 3)), 334 rwsetutil.NewKVRead("key5", version.NewHeight(1, 4)), 335 rwsetutil.NewKVRead("key6", version.NewHeight(1, 5)), 336 rwsetutil.NewKVRead("key7", version.NewHeight(1, 6)), 337 rwsetutil.NewKVRead("key8", version.NewHeight(1, 7)), 338 } 339 rwsetutil.SetMerkelSummary(rqi1, buildTestHashResults(t, 2, kvReadsDuringSimulation1)) 340 rwsetBuilder1.AddToRangeQuerySet("ns1", rqi1) 341 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{}) 342 343 rwsetBuilder2 := rwsetutil.NewRWSetBuilder() 344 rqi2 := &kvrwset.RangeQueryInfo{StartKey: "key1", EndKey: "key9", ItrExhausted: false} 345 kvReadsDuringSimulation2 := []*kvrwset.KVRead{ 346 rwsetutil.NewKVRead("key1", version.NewHeight(1, 0)), 347 rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)), 348 rwsetutil.NewKVRead("key3", version.NewHeight(1, 1)), 349 rwsetutil.NewKVRead("key4", version.NewHeight(1, 3)), 350 rwsetutil.NewKVRead("key5", version.NewHeight(1, 4)), 351 rwsetutil.NewKVRead("key6", version.NewHeight(1, 5)), 352 rwsetutil.NewKVRead("key7", version.NewHeight(1, 6)), 353 rwsetutil.NewKVRead("key8", version.NewHeight(1, 7)), 354 rwsetutil.NewKVRead("key9", version.NewHeight(1, 8)), 355 } 356 rwsetutil.SetMerkelSummary(rqi2, buildTestHashResults(t, 2, kvReadsDuringSimulation2)) 357 rwsetBuilder2.AddToRangeQuerySet("ns1", rqi2) 358 checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0}) 359 } 360 361 func checkValidation(t *testing.T, val *validator, transRWSets []*rwsetutil.TxRwSet, expectedInvalidTxIndexes []int) { 362 var trans []*transaction 363 for i, tranRWSet := range transRWSets { 364 tx := &transaction{ 365 id: fmt.Sprintf("txid-%d", i), 366 indexInBlock: i, 367 validationCode: peer.TxValidationCode_VALID, 368 rwset: tranRWSet, 369 } 370 trans = append(trans, tx) 371 } 372 blk := &block{num: 1, txs: trans} 373 _, err := val.validateAndPrepareBatch(blk, true) 374 require.NoError(t, err) 375 t.Logf("block.Txs[0].ValidationCode = %d", blk.txs[0].validationCode) 376 var invalidTxs []int 377 for _, tx := range blk.txs { 378 if tx.validationCode != peer.TxValidationCode_VALID { 379 invalidTxs = append(invalidTxs, tx.indexInBlock) 380 } 381 } 382 require.Equal(t, len(expectedInvalidTxIndexes), len(invalidTxs)) 383 require.ElementsMatch(t, invalidTxs, expectedInvalidTxIndexes) 384 } 385 386 func buildTestHashResults(t *testing.T, maxDegree int, kvReads []*kvrwset.KVRead) *kvrwset.QueryReadsMerkleSummary { 387 if len(kvReads) <= maxDegree { 388 t.Fatal("This method should be called with number of KVReads more than maxDegree; Else, hashing won't be performedrwset") 389 } 390 helper, _ := rwsetutil.NewRangeQueryResultsHelper(true, uint32(maxDegree), testHashFunc) 391 for _, kvRead := range kvReads { 392 helper.AddResult(kvRead) 393 } 394 _, h, err := helper.Done() 395 require.NoError(t, err) 396 require.NotNil(t, h) 397 return h 398 } 399 400 func getTestPubSimulationRWSet(t *testing.T, builders ...*rwsetutil.RWSetBuilder) []*rwsetutil.TxRwSet { 401 var pubRWSets []*rwsetutil.TxRwSet 402 for _, b := range builders { 403 s, e := b.GetTxSimulationResults() 404 require.NoError(t, e) 405 sBytes, err := s.GetPubSimulationBytes() 406 require.NoError(t, err) 407 pubRWSet := &rwsetutil.TxRwSet{} 408 require.NoError(t, pubRWSet.FromProtoBytes(sBytes)) 409 pubRWSets = append(pubRWSets, pubRWSet) 410 } 411 return pubRWSets 412 }