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