github.com/Hnampk/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/validator/valimpl/helper_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package valimpl 8 9 import ( 10 "fmt" 11 "os" 12 "testing" 13 14 "github.com/davecgh/go-spew/spew" 15 "github.com/golang/protobuf/proto" 16 "github.com/hyperledger/fabric-protos-go/common" 17 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 18 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 19 "github.com/hyperledger/fabric-protos-go/peer" 20 "github.com/hyperledger/fabric/bccsp/sw" 21 "github.com/hyperledger/fabric/common/flogging" 22 "github.com/hyperledger/fabric/common/flogging/floggingtest" 23 "github.com/hyperledger/fabric/common/ledger/testutil" 24 "github.com/hyperledger/fabric/core/ledger" 25 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate" 26 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 27 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 28 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr" 29 mocktxmgr "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr/mock" 30 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/validator/internal" 31 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/validator/valimpl/mock" 32 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 33 mocklgr "github.com/hyperledger/fabric/core/ledger/mock" 34 lutils "github.com/hyperledger/fabric/core/ledger/util" 35 "github.com/hyperledger/fabric/protoutil" 36 "github.com/stretchr/testify/assert" 37 ) 38 39 func TestMain(m *testing.M) { 40 flogging.ActivateSpec("valimpl,statebasedval,internal=debug") 41 os.Exit(m.Run()) 42 } 43 44 func TestValidateAndPreparePvtBatch(t *testing.T) { 45 testDBEnv := &privacyenabledstate.LevelDBCommonStorageTestEnv{} 46 testDBEnv.Init(t) 47 defer testDBEnv.Cleanup() 48 testDB := testDBEnv.GetDBHandle("emptydb") 49 50 pubSimulationResults := [][]byte{} 51 pvtDataMap := make(map[uint64]*ledger.TxPvtData) 52 53 txids := []string{"tx1", "tx2", "tx3"} 54 55 // 1. Construct a block with three transactions and pre 56 // process the block by calling preprocessProtoBlock() 57 // and get a preprocessedBlock. 58 59 // Tx 1 60 // Get simulation results for tx1 61 tx1SimulationResults := testutilSampleTxSimulationResults(t, "key1") 62 res, err := tx1SimulationResults.GetPubSimulationBytes() 63 assert.NoError(t, err) 64 65 // Add tx1 public rwset to the set of results 66 pubSimulationResults = append(pubSimulationResults, res) 67 68 // Add tx1 private rwset to the private data map 69 tx1PvtData := &ledger.TxPvtData{SeqInBlock: 0, WriteSet: tx1SimulationResults.PvtSimulationResults} 70 pvtDataMap[uint64(0)] = tx1PvtData 71 72 // Tx 2 73 // Get simulation results for tx2 74 tx2SimulationResults := testutilSampleTxSimulationResults(t, "key2") 75 res, err = tx2SimulationResults.GetPubSimulationBytes() 76 assert.NoError(t, err) 77 78 // Add tx2 public rwset to the set of results 79 pubSimulationResults = append(pubSimulationResults, res) 80 81 // As tx2 private rwset does not belong to a collection owned by the current peer, 82 // the private rwset is not added to the private data map 83 84 // Tx 3 85 // Get simulation results for tx3 86 tx3SimulationResults := testutilSampleTxSimulationResults(t, "key3") 87 res, err = tx3SimulationResults.GetPubSimulationBytes() 88 assert.NoError(t, err) 89 90 // Add tx3 public rwset to the set of results 91 pubSimulationResults = append(pubSimulationResults, res) 92 93 // Add tx3 private rwset to the private data map 94 tx3PvtData := &ledger.TxPvtData{SeqInBlock: 2, WriteSet: tx3SimulationResults.PvtSimulationResults} 95 pvtDataMap[uint64(2)] = tx3PvtData 96 97 // Construct a block using all three transactions' simulation results 98 block := testutil.ConstructBlockWithTxid(t, 10, testutil.ConstructRandomBytes(t, 32), pubSimulationResults, txids, false) 99 100 // Construct the expected preprocessed block from preprocessProtoBlock() 101 expectedPerProcessedBlock := &internal.Block{Num: 10} 102 tx1TxRWSet, err := rwsetutil.TxRwSetFromProtoMsg(tx1SimulationResults.PubSimulationResults) 103 assert.NoError(t, err) 104 expectedPerProcessedBlock.Txs = append(expectedPerProcessedBlock.Txs, &internal.Transaction{IndexInBlock: 0, ID: "tx1", RWSet: tx1TxRWSet}) 105 106 tx2TxRWSet, err := rwsetutil.TxRwSetFromProtoMsg(tx2SimulationResults.PubSimulationResults) 107 assert.NoError(t, err) 108 expectedPerProcessedBlock.Txs = append(expectedPerProcessedBlock.Txs, &internal.Transaction{IndexInBlock: 1, ID: "tx2", RWSet: tx2TxRWSet}) 109 110 tx3TxRWSet, err := rwsetutil.TxRwSetFromProtoMsg(tx3SimulationResults.PubSimulationResults) 111 assert.NoError(t, err) 112 expectedPerProcessedBlock.Txs = append(expectedPerProcessedBlock.Txs, &internal.Transaction{IndexInBlock: 2, ID: "tx3", RWSet: tx3TxRWSet}) 113 alwaysValidKVFunc := func(key string, value []byte) error { 114 return nil 115 } 116 actualPreProcessedBlock, _, err := preprocessProtoBlock(nil, alwaysValidKVFunc, block, false, nil) 117 assert.NoError(t, err) 118 assert.Equal(t, expectedPerProcessedBlock, actualPreProcessedBlock) 119 120 // 2. Assuming that MVCC validation is performed on the preprocessedBlock, set the appropriate validation code 121 // for each transaction and then call validateAndPreparePvtBatch() to get a validated private update batch. 122 // Here, validate refers to comparison of hash of pvtRWSet in public rwset with the actual hash of pvtRWSet) 123 124 // Set validation code for all three transactions. One of the three transaction is marked invalid 125 mvccValidatedBlock := actualPreProcessedBlock 126 mvccValidatedBlock.Txs[0].ValidationCode = peer.TxValidationCode_VALID 127 mvccValidatedBlock.Txs[1].ValidationCode = peer.TxValidationCode_VALID 128 mvccValidatedBlock.Txs[2].ValidationCode = peer.TxValidationCode_INVALID_OTHER_REASON 129 130 // Construct the expected private updates 131 expectedPvtUpdates := privacyenabledstate.NewPvtUpdateBatch() 132 tx1TxPvtRWSet, err := rwsetutil.TxPvtRwSetFromProtoMsg(tx1SimulationResults.PvtSimulationResults) 133 assert.NoError(t, err) 134 addPvtRWSetToPvtUpdateBatch(tx1TxPvtRWSet, expectedPvtUpdates, version.NewHeight(uint64(10), uint64(0))) 135 136 actualPvtUpdates, err := validateAndPreparePvtBatch(mvccValidatedBlock, testDB, nil, pvtDataMap, nil) 137 assert.NoError(t, err) 138 assert.Equal(t, expectedPvtUpdates, actualPvtUpdates) 139 140 expectedtxsFilter := []uint8{uint8(peer.TxValidationCode_VALID), uint8(peer.TxValidationCode_VALID), uint8(peer.TxValidationCode_INVALID_OTHER_REASON)} 141 142 postprocessProtoBlock(block, mvccValidatedBlock) 143 assert.Equal(t, expectedtxsFilter, block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 144 } 145 146 func TestPreprocessProtoBlock(t *testing.T) { 147 allwaysValidKVfunc := func(key string, value []byte) error { 148 return nil 149 } 150 // good block 151 //_, gb := testutil.NewBlockGenerator(t, "testLedger", false) 152 gb := testutil.ConstructTestBlock(t, 10, 1, 1) 153 _, _, err := preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 154 assert.NoError(t, err) 155 // bad envelope 156 gb = testutil.ConstructTestBlock(t, 11, 1, 1) 157 gb.Data = &common.BlockData{Data: [][]byte{{123}}} 158 gb.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = 159 lutils.NewTxValidationFlagsSetValue(len(gb.Data.Data), peer.TxValidationCode_VALID) 160 _, _, err = preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 161 assert.Error(t, err) 162 t.Log(err) 163 // bad payload 164 gb = testutil.ConstructTestBlock(t, 12, 1, 1) 165 envBytes, _ := protoutil.GetBytesEnvelope(&common.Envelope{Payload: []byte{123}}) 166 gb.Data = &common.BlockData{Data: [][]byte{envBytes}} 167 _, _, err = preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 168 assert.Error(t, err) 169 t.Log(err) 170 // bad channel header 171 gb = testutil.ConstructTestBlock(t, 13, 1, 1) 172 payloadBytes, _ := protoutil.GetBytesPayload(&common.Payload{ 173 Header: &common.Header{ChannelHeader: []byte{123}}, 174 }) 175 envBytes, _ = protoutil.GetBytesEnvelope(&common.Envelope{Payload: payloadBytes}) 176 gb.Data = &common.BlockData{Data: [][]byte{envBytes}} 177 _, _, err = preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 178 assert.Error(t, err) 179 t.Log(err) 180 181 // bad channel header with invalid filter set 182 gb = testutil.ConstructTestBlock(t, 14, 1, 1) 183 payloadBytes, _ = protoutil.GetBytesPayload(&common.Payload{ 184 Header: &common.Header{ChannelHeader: []byte{123}}, 185 }) 186 envBytes, _ = protoutil.GetBytesEnvelope(&common.Envelope{Payload: payloadBytes}) 187 gb.Data = &common.BlockData{Data: [][]byte{envBytes}} 188 flags := lutils.NewTxValidationFlags(len(gb.Data.Data)) 189 flags.SetFlag(0, peer.TxValidationCode_BAD_CHANNEL_HEADER) 190 gb.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = flags 191 _, _, err = preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 192 assert.NoError(t, err) // invalid filter should take precedence 193 194 // new block 195 var blockNum uint64 = 15 196 txid := "testtxid1234" 197 gb = testutil.ConstructBlockWithTxid(t, blockNum, []byte{123}, 198 [][]byte{{123}}, []string{txid}, false) 199 flags = lutils.NewTxValidationFlags(len(gb.Data.Data)) 200 flags.SetFlag(0, peer.TxValidationCode_BAD_HEADER_EXTENSION) 201 gb.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = flags 202 203 // test logger 204 oldLogger := logger 205 defer func() { logger = oldLogger }() 206 l, recorder := floggingtest.NewTestLogger(t) 207 logger = l 208 209 _, _, err = preprocessProtoBlock(nil, allwaysValidKVfunc, gb, false, nil) 210 assert.NoError(t, err) 211 expected := fmt.Sprintf( 212 "Channel [%s]: Block [%d] Transaction index [%d] TxId [%s] marked as invalid by committer. Reason code [%s]", 213 "testchannelid", blockNum, 0, txid, peer.TxValidationCode_BAD_HEADER_EXTENSION, 214 ) 215 assert.NotEmpty(t, recorder.MessagesContaining(expected)) 216 } 217 218 func TestPreprocessProtoBlockInvalidWriteset(t *testing.T) { 219 kvValidationFunc := func(key string, value []byte) error { 220 if value[0] == '_' { 221 return fmt.Errorf("value [%s] found to be invalid by 'kvValidationFunc for testing'", value) 222 } 223 return nil 224 } 225 226 rwSetBuilder := rwsetutil.NewRWSetBuilder() 227 rwSetBuilder.AddToWriteSet("ns", "key", []byte("_invalidValue")) // bad value 228 simulation1, err := rwSetBuilder.GetTxSimulationResults() 229 assert.NoError(t, err) 230 simulation1Bytes, err := simulation1.GetPubSimulationBytes() 231 assert.NoError(t, err) 232 233 rwSetBuilder = rwsetutil.NewRWSetBuilder() 234 rwSetBuilder.AddToWriteSet("ns", "key", []byte("validValue")) // good value 235 simulation2, err := rwSetBuilder.GetTxSimulationResults() 236 assert.NoError(t, err) 237 simulation2Bytes, err := simulation2.GetPubSimulationBytes() 238 assert.NoError(t, err) 239 240 block := testutil.ConstructBlock(t, 1, testutil.ConstructRandomBytes(t, 32), 241 [][]byte{simulation1Bytes, simulation2Bytes}, false) // block with two txs 242 txfilter := lutils.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 243 assert.True(t, txfilter.IsValid(0)) 244 assert.True(t, txfilter.IsValid(1)) // both txs are valid initially at the time of block cutting 245 246 internalBlock, _, err := preprocessProtoBlock(nil, kvValidationFunc, block, false, nil) 247 assert.NoError(t, err) 248 assert.False(t, txfilter.IsValid(0)) // tx at index 0 should be marked as invalid 249 assert.True(t, txfilter.IsValid(1)) // tx at index 1 should be marked as valid 250 assert.Len(t, internalBlock.Txs, 1) 251 assert.Equal(t, internalBlock.Txs[0].IndexInBlock, 1) 252 } 253 254 func TestIncrementPvtdataVersionIfNeeded(t *testing.T) { 255 testDBEnv := &privacyenabledstate.LevelDBCommonStorageTestEnv{} 256 testDBEnv.Init(t) 257 defer testDBEnv.Cleanup() 258 testDB := testDBEnv.GetDBHandle("testdb") 259 updateBatch := privacyenabledstate.NewUpdateBatch() 260 // populate db with some pvt data 261 updateBatch.PvtUpdates.Put("ns", "coll1", "key1", []byte("value1"), version.NewHeight(1, 1)) 262 updateBatch.PvtUpdates.Put("ns", "coll2", "key2", []byte("value2"), version.NewHeight(1, 2)) 263 updateBatch.PvtUpdates.Put("ns", "coll3", "key3", []byte("value3"), version.NewHeight(1, 3)) 264 updateBatch.PvtUpdates.Put("ns", "col4", "key4", []byte("value4"), version.NewHeight(1, 4)) 265 testDB.ApplyPrivacyAwareUpdates(updateBatch, version.NewHeight(1, 4)) 266 267 // for the current block, mimic the resultant hashed updates 268 hashUpdates := privacyenabledstate.NewHashedUpdateBatch() 269 hashUpdates.PutValHashAndMetadata("ns", "coll1", lutils.ComputeStringHash("key1"), 270 lutils.ComputeStringHash("value1_set_by_tx1"), []byte("metadata1_set_by_tx2"), version.NewHeight(2, 2)) // mimics the situation - value set by tx1 and metadata by tx2 271 hashUpdates.PutValHashAndMetadata("ns", "coll2", lutils.ComputeStringHash("key2"), 272 lutils.ComputeStringHash("value2"), []byte("metadata2_set_by_tx4"), version.NewHeight(2, 4)) // only metadata set by tx4 273 hashUpdates.PutValHashAndMetadata("ns", "coll3", lutils.ComputeStringHash("key3"), 274 lutils.ComputeStringHash("value3_set_by_tx6"), []byte("metadata3"), version.NewHeight(2, 6)) // only value set by tx6 275 pubAndHashedUpdatesBatch := &internal.PubAndHashUpdates{HashUpdates: hashUpdates} 276 277 // for the current block, mimic the resultant pvt updates (without metadata taking into account). Assume that Tx6 pvt data is missing 278 pvtUpdateBatch := privacyenabledstate.NewPvtUpdateBatch() 279 pvtUpdateBatch.Put("ns", "coll1", "key1", []byte("value1_set_by_tx1"), version.NewHeight(2, 1)) 280 pvtUpdateBatch.Put("ns", "coll3", "key3", []byte("value3_set_by_tx5"), version.NewHeight(2, 5)) 281 // metadata updated for key1 and key3 282 metadataUpdates := metadataUpdates{collKey{"ns", "coll1", "key1"}: true, collKey{"ns", "coll2", "key2"}: true} 283 284 // invoke function and test results 285 err := incrementPvtdataVersionIfNeeded(metadataUpdates, pvtUpdateBatch, pubAndHashedUpdatesBatch, testDB) 286 assert.NoError(t, err) 287 288 assert.Equal(t, 289 &statedb.VersionedValue{Value: []byte("value1_set_by_tx1"), Version: version.NewHeight(2, 2)}, // key1 value should be same and version should be upgraded to (2,2) 290 pvtUpdateBatch.Get("ns", "coll1", "key1"), 291 ) 292 293 assert.Equal(t, 294 &statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(2, 4)}, // key2 entry should get added with value in the db and version (2,4) 295 pvtUpdateBatch.Get("ns", "coll2", "key2"), 296 ) 297 298 assert.Equal(t, 299 &statedb.VersionedValue{Value: []byte("value3_set_by_tx5"), Version: version.NewHeight(2, 5)}, // key3 should be unaffected because the tx6 was missing from pvt data 300 pvtUpdateBatch.Get("ns", "coll3", "key3"), 301 ) 302 } 303 304 func TestTxStatsInfoWithConfigTx(t *testing.T) { 305 testDBEnv := &privacyenabledstate.LevelDBCommonStorageTestEnv{} 306 testDBEnv.Init(t) 307 defer testDBEnv.Cleanup() 308 testDB := testDBEnv.GetDBHandle("emptydb") 309 310 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 311 assert.NoError(t, err) 312 v := NewStatebasedValidator(nil, testDB, nil, cryptoProvider) 313 314 gb := testutil.ConstructTestBlocks(t, 1)[0] 315 _, txStatsInfo, err := v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: gb}, true) 316 assert.NoError(t, err) 317 expectedTxStatInfo := []*txmgr.TxStatInfo{ 318 { 319 TxType: common.HeaderType_CONFIG, 320 ValidationCode: peer.TxValidationCode_VALID, 321 }, 322 } 323 t.Logf("txStatsInfo=%s\n", spew.Sdump(txStatsInfo)) 324 assert.Equal(t, expectedTxStatInfo, txStatsInfo) 325 } 326 327 func TestContainsPostOrderWrites(t *testing.T) { 328 testDBEnv := &privacyenabledstate.LevelDBCommonStorageTestEnv{} 329 testDBEnv.Init(t) 330 defer testDBEnv.Cleanup() 331 testDB := testDBEnv.GetDBHandle("emptydb") 332 mockSimulator := &mocklgr.TxSimulator{} 333 mockTxmgr := &mocktxmgr.TxMgr{} 334 mockTxmgr.NewTxSimulatorReturns(mockSimulator, nil) 335 336 fakeTxProcessor := &mock.Processor{} 337 customTxProcessors := map[common.HeaderType]ledger.CustomTxProcessor{ 338 common.HeaderType_CONFIG: fakeTxProcessor, 339 } 340 341 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 342 assert.NoError(t, err) 343 v := NewStatebasedValidator(mockTxmgr, testDB, customTxProcessors, cryptoProvider) 344 blocks := testutil.ConstructTestBlocks(t, 2) 345 346 // block with config tx that produces post order writes 347 fakeTxProcessor.GenerateSimulationResultsStub = 348 func(txEnvelop *common.Envelope, s ledger.TxSimulator, initializingLedger bool) error { 349 rwSetBuilder := rwsetutil.NewRWSetBuilder() 350 rwSetBuilder.AddToWriteSet("ns1", "key1", []byte("value1")) 351 rwSetBuilder.GetTxSimulationResults() 352 s.(*mocklgr.TxSimulator).GetTxSimulationResultsReturns( 353 rwSetBuilder.GetTxSimulationResults()) 354 return nil 355 } 356 batch, _, err := v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[0]}, true) 357 assert.NoError(t, err) 358 assert.True(t, batch.PubUpdates.ContainsPostOrderWrites) 359 360 // block with endorser txs 361 batch, _, err = v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[1]}, true) 362 assert.NoError(t, err) 363 assert.False(t, batch.PubUpdates.ContainsPostOrderWrites) 364 365 // test with block with invalid config tx 366 fakeTxProcessor.GenerateSimulationResultsStub = 367 func(txEnvelop *common.Envelope, s ledger.TxSimulator, initializingLedger bool) error { 368 s.(*mocklgr.TxSimulator).GetTxSimulationResultsReturns(nil, nil) 369 return &ledger.InvalidTxError{Msg: "fake-message"} 370 } 371 batch, _, err = v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[0]}, true) 372 assert.NoError(t, err) 373 assert.False(t, batch.PubUpdates.ContainsPostOrderWrites) 374 } 375 376 func TestTxStatsInfo(t *testing.T) { 377 testDBEnv := &privacyenabledstate.LevelDBCommonStorageTestEnv{} 378 testDBEnv.Init(t) 379 defer testDBEnv.Cleanup() 380 testDB := testDBEnv.GetDBHandle("emptydb") 381 382 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 383 assert.NoError(t, err) 384 v := NewStatebasedValidator(nil, testDB, nil, cryptoProvider) 385 386 // create a block with 4 endorser transactions 387 tx1SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 388 &testRwset{ 389 writes: []*testKeyWrite{ 390 {ns: "ns1", key: "key1", val: "val1"}, 391 }, 392 }, 393 ) 394 tx2SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 395 &testRwset{ 396 reads: []*testKeyRead{ 397 {ns: "ns1", key: "key1", version: nil}, // should cause mvcc read-conflict with tx1 398 }, 399 }, 400 ) 401 tx3SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 402 &testRwset{ 403 writes: []*testKeyWrite{ 404 {ns: "ns1", key: "key2", val: "val2"}, 405 }, 406 }, 407 ) 408 tx4SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 409 &testRwset{ 410 writes: []*testKeyWrite{ 411 {ns: "ns1", coll: "coll1", key: "key1", val: "val1"}, 412 {ns: "ns1", coll: "coll2", key: "key1", val: "val1"}, 413 }, 414 }, 415 ) 416 417 blockDetails := &testutil.BlockDetails{ 418 BlockNum: 5, 419 PreviousHash: []byte("previousHash"), 420 Txs: []*testutil.TxDetails{ 421 { 422 TxID: "tx_1", 423 ChaincodeName: "cc_1", 424 ChaincodeVersion: "cc_1_v1", 425 SimulationResults: tx1SimulationResults, 426 Type: common.HeaderType_ENDORSER_TRANSACTION, 427 }, 428 { 429 TxID: "tx_2", 430 ChaincodeName: "cc_2", 431 ChaincodeVersion: "cc_2_v1", 432 SimulationResults: tx2SimulationResults, 433 Type: common.HeaderType_ENDORSER_TRANSACTION, 434 }, 435 { 436 TxID: "tx_3", 437 ChaincodeName: "cc_3", 438 ChaincodeVersion: "cc_3_v1", 439 SimulationResults: tx3SimulationResults, 440 Type: common.HeaderType_ENDORSER_TRANSACTION, 441 }, 442 { 443 TxID: "tx_4", 444 ChaincodeName: "cc_4", 445 ChaincodeVersion: "cc_4_v1", 446 SimulationResults: tx4SimulationResults, 447 Type: common.HeaderType_ENDORSER_TRANSACTION, 448 }, 449 }, 450 } 451 452 block := testutil.ConstructBlockFromBlockDetails(t, blockDetails, false) 453 txsFilter := lutils.NewTxValidationFlags(4) 454 txsFilter.SetFlag(0, peer.TxValidationCode_VALID) 455 txsFilter.SetFlag(1, peer.TxValidationCode_VALID) 456 txsFilter.SetFlag(2, peer.TxValidationCode_BAD_PAYLOAD) 457 txsFilter.SetFlag(3, peer.TxValidationCode_VALID) 458 block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter 459 460 // collect the validation stats for the block and check against the expected stats 461 _, txStatsInfo, err := v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: block}, true) 462 assert.NoError(t, err) 463 expectedTxStatInfo := []*txmgr.TxStatInfo{ 464 { 465 TxType: common.HeaderType_ENDORSER_TRANSACTION, 466 ValidationCode: peer.TxValidationCode_VALID, 467 ChaincodeID: &peer.ChaincodeID{Name: "cc_1", Version: "cc_1_v1"}, 468 }, 469 { 470 TxType: common.HeaderType_ENDORSER_TRANSACTION, 471 ValidationCode: peer.TxValidationCode_MVCC_READ_CONFLICT, 472 ChaincodeID: &peer.ChaincodeID{Name: "cc_2", Version: "cc_2_v1"}, 473 }, 474 { 475 TxType: -1, 476 ValidationCode: peer.TxValidationCode_BAD_PAYLOAD, 477 }, 478 { 479 TxType: common.HeaderType_ENDORSER_TRANSACTION, 480 ValidationCode: peer.TxValidationCode_VALID, 481 ChaincodeID: &peer.ChaincodeID{Name: "cc_4", Version: "cc_4_v1"}, 482 NumCollections: 2, 483 }, 484 } 485 t.Logf("txStatsInfo=%s\n", spew.Sdump(txStatsInfo)) 486 assert.Equal(t, expectedTxStatInfo, txStatsInfo) 487 } 488 489 func testutilSampleTxSimulationResults(t *testing.T, key string) *ledger.TxSimulationResults { 490 rwSetBuilder := rwsetutil.NewRWSetBuilder() 491 // public rws ns1 + ns2 492 rwSetBuilder.AddToReadSet("ns1", key, version.NewHeight(1, 1)) 493 rwSetBuilder.AddToReadSet("ns2", key, version.NewHeight(1, 1)) 494 rwSetBuilder.AddToWriteSet("ns2", key, []byte("ns2-key1-value")) 495 496 // pvt rwset ns1 497 rwSetBuilder.AddToHashedReadSet("ns1", "coll1", key, version.NewHeight(1, 1)) 498 rwSetBuilder.AddToHashedReadSet("ns1", "coll2", key, version.NewHeight(1, 1)) 499 rwSetBuilder.AddToPvtAndHashedWriteSet("ns1", "coll2", key, []byte("pvt-ns1-coll2-key1-value")) 500 501 // pvt rwset ns2 502 rwSetBuilder.AddToHashedReadSet("ns2", "coll1", key, version.NewHeight(1, 1)) 503 rwSetBuilder.AddToHashedReadSet("ns2", "coll2", key, version.NewHeight(1, 1)) 504 rwSetBuilder.AddToPvtAndHashedWriteSet("ns2", "coll2", key, []byte("pvt-ns2-coll2-key1-value")) 505 rwSetBuilder.AddToPvtAndHashedWriteSet("ns2", "coll3", key, nil) 506 507 rwSetBuilder.AddToHashedReadSet("ns3", "coll1", key, version.NewHeight(1, 1)) 508 509 pubAndPvtSimulationResults, err := rwSetBuilder.GetTxSimulationResults() 510 if err != nil { 511 t.Fatalf("ConstructSimulationResultsWithPvtData failed while getting simulation results, err %s", err) 512 } 513 514 return pubAndPvtSimulationResults 515 } 516 517 type testKeyRead struct { 518 ns, coll, key string 519 version *version.Height 520 } 521 type testKeyWrite struct { 522 ns, coll, key string 523 val string 524 } 525 type testRwset struct { 526 reads []*testKeyRead 527 writes []*testKeyWrite 528 } 529 530 func testutilGenerateTxSimulationResults(t *testing.T, rwsetInfo *testRwset) *ledger.TxSimulationResults { 531 rwSetBuilder := rwsetutil.NewRWSetBuilder() 532 for _, r := range rwsetInfo.reads { 533 if r.coll == "" { 534 rwSetBuilder.AddToReadSet(r.ns, r.key, r.version) 535 } else { 536 rwSetBuilder.AddToHashedReadSet(r.ns, r.coll, r.key, r.version) 537 } 538 } 539 540 for _, w := range rwsetInfo.writes { 541 if w.coll == "" { 542 rwSetBuilder.AddToWriteSet(w.ns, w.key, []byte(w.val)) 543 } else { 544 rwSetBuilder.AddToPvtAndHashedWriteSet(w.ns, w.coll, w.key, []byte(w.val)) 545 } 546 } 547 simulationResults, err := rwSetBuilder.GetTxSimulationResults() 548 assert.NoError(t, err) 549 return simulationResults 550 } 551 552 func testutilGenerateTxSimulationResultsAsBytes( 553 t *testing.T, rwsetInfo *testRwset) ( 554 publicSimulationRes []byte, pvtWS []byte, 555 ) { 556 simulationRes := testutilGenerateTxSimulationResults(t, rwsetInfo) 557 pub, err := simulationRes.GetPubSimulationBytes() 558 assert.NoError(t, err) 559 pvt, err := simulationRes.GetPvtSimulationBytes() 560 assert.NoError(t, err) 561 return pub, pvt 562 } 563 564 //go:generate counterfeiter -o mock/txsim.go --fake-name TxSimulator . txSimulator 565 type txSimulator interface { 566 ledger.TxSimulator 567 } 568 569 //go:generate counterfeiter -o mock/processor.go --fake-name Processor . processor 570 type processor interface { 571 ledger.CustomTxProcessor 572 } 573 574 //go:generate counterfeiter -o mock/txmgr.go --fake-name TxMgr . txMgr 575 type txMgr interface { 576 txmgr.TxMgr 577 } 578 579 // Test for txType != common.HeaderType_ENDORSER_TRANSACTION 580 func Test_preprocessProtoBlock_processNonEndorserTx(t *testing.T) { 581 // Register customtx processor 582 mockTxProcessor := new(mock.Processor) 583 mockTxProcessor.GenerateSimulationResultsReturns(nil) 584 customTxProcessors := map[common.HeaderType]ledger.CustomTxProcessor{ 585 100: mockTxProcessor, 586 } 587 588 // Prepare param1: txmgr.TxMgr 589 kvw := &kvrwset.KVWrite{Key: "key1", IsDelete: false, Value: []byte{0xde, 0xad, 0xbe, 0xef}} 590 kvrw := &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{kvw}} 591 mkvrw, _ := proto.Marshal(kvrw) 592 nrws := rwset.NsReadWriteSet{ 593 Namespace: "ns1", 594 Rwset: mkvrw, 595 } 596 pubsimresults := rwset.TxReadWriteSet{ 597 DataModel: -1, 598 NsRwset: []*rwset.NsReadWriteSet{&nrws}, 599 } 600 txsimres := &ledger.TxSimulationResults{ 601 PubSimulationResults: &pubsimresults, 602 PvtSimulationResults: nil, 603 } 604 txsim_ := new(mock.TxSimulator) 605 txsim_.GetTxSimulationResultsReturns(txsimres, nil) 606 txmgr_ := new(mock.TxMgr) 607 txmgr_.NewTxSimulatorReturns(txsim_, nil) 608 609 // Prepare param2: validateKVFunc 610 alwaysValidKVFunc := func(key string, value []byte) error { 611 return nil 612 } 613 614 // Prepare param3: *common.Block 615 pubSimulationResults := [][]byte{} 616 txids := []string{"tx1"} 617 // Get simulation results for tx1 618 rwSetBuilder := rwsetutil.NewRWSetBuilder() 619 tx1SimulationResults, err := rwSetBuilder.GetTxSimulationResults() 620 assert.NoError(t, err) 621 // Add tx1 public rwset to the set of results 622 res, err := tx1SimulationResults.GetPubSimulationBytes() 623 assert.NoError(t, err) 624 pubSimulationResults = append(pubSimulationResults, res) 625 // Construct a block using a transaction simulation result 626 block := testutil.ConstructBlockWithTxidHeaderType( 627 t, 628 10, 629 testutil.ConstructRandomBytes(t, 32), 630 pubSimulationResults, 631 txids, 632 false, 633 100, 634 ) 635 636 // Call 637 internalBlock, txsStatInfo, err2 := preprocessProtoBlock(txmgr_, alwaysValidKVFunc, block, false, customTxProcessors) 638 639 // Prepare expected value 640 expectedPreprocessedBlock := &internal.Block{ 641 Num: 10, 642 } 643 value1 := []byte{0xde, 0xad, 0xbe, 0xef} 644 expKVWrite := &kvrwset.KVWrite{ 645 Key: "key1", 646 IsDelete: false, 647 Value: value1, 648 } 649 expKVRWSet := &kvrwset.KVRWSet{ 650 Writes: []*kvrwset.KVWrite{expKVWrite}, 651 } 652 expNsRwSet := &rwsetutil.NsRwSet{ 653 NameSpace: "ns1", 654 KvRwSet: expKVRWSet, 655 } 656 expTxRwSet := &rwsetutil.TxRwSet{ 657 NsRwSets: []*rwsetutil.NsRwSet{expNsRwSet}, 658 } 659 expectedPreprocessedBlock.Txs = append( 660 expectedPreprocessedBlock.Txs, 661 &internal.Transaction{ 662 IndexInBlock: 0, 663 ID: "tx1", 664 RWSet: expTxRwSet, 665 ContainsPostOrderWrites: true, 666 }, 667 ) 668 expectedTxStatInfo := []*txmgr.TxStatInfo{ 669 { 670 TxType: 100, 671 }, 672 } 673 674 // Check result 675 assert.NoError(t, err2) 676 assert.Equal(t, expectedPreprocessedBlock, internalBlock) 677 assert.Equal(t, expectedTxStatInfo, txsStatInfo) 678 }