github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/validation/batch_preparer_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 "fmt" 11 "testing" 12 13 "github.com/davecgh/go-spew/spew" 14 "github.com/golang/protobuf/proto" 15 "github.com/hyperledger/fabric-protos-go/common" 16 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 17 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 18 "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/osdi23p228/fabric/common/flogging/floggingtest" 20 "github.com/osdi23p228/fabric/common/ledger/testutil" 21 "github.com/osdi23p228/fabric/core/ledger" 22 "github.com/osdi23p228/fabric/core/ledger/internal/version" 23 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate" 24 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 25 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb" 26 "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/validation/mock" 27 mocklgr "github.com/osdi23p228/fabric/core/ledger/mock" 28 lutils "github.com/osdi23p228/fabric/core/ledger/util" 29 "github.com/osdi23p228/fabric/internal/pkg/txflags" 30 "github.com/osdi23p228/fabric/protoutil" 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, nil) 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 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 expectedTxStatInfo := []*TxStatInfo{ 306 { 307 TxType: common.HeaderType_CONFIG, 308 ValidationCode: peer.TxValidationCode_VALID, 309 }, 310 } 311 t.Logf("txStatsInfo=%s\n", spew.Sdump(txStatsInfo)) 312 require.Equal(t, expectedTxStatInfo, txStatsInfo) 313 } 314 315 func TestTXMgrContainsPostOrderWrites(t *testing.T) { 316 testDBEnv := &privacyenabledstate.LevelDBTestEnv{} 317 testDBEnv.Init(t) 318 defer testDBEnv.Cleanup() 319 testDB := testDBEnv.GetDBHandle("emptydb") 320 mockSimulator := &mocklgr.TxSimulator{} 321 mockSimulatorProvider := &mock.PostOrderSimulatorProvider{} 322 mockSimulatorProvider.NewTxSimulatorReturns(mockSimulator, nil) 323 324 fakeTxProcessor := &mock.Processor{} 325 customTxProcessors := map[common.HeaderType]ledger.CustomTxProcessor{ 326 common.HeaderType_CONFIG: fakeTxProcessor, 327 } 328 329 v := NewCommitBatchPreparer(mockSimulatorProvider, testDB, customTxProcessors, testHashFunc) 330 blocks := testutil.ConstructTestBlocks(t, 2) 331 332 // block with config tx that produces post order writes 333 fakeTxProcessor.GenerateSimulationResultsStub = 334 func(txEnvelop *common.Envelope, s ledger.TxSimulator, initializingLedger bool) error { 335 rwSetBuilder := rwsetutil.NewRWSetBuilder() 336 rwSetBuilder.AddToWriteSet("ns1", "key1", []byte("value1")) 337 rwSetBuilder.GetTxSimulationResults() 338 s.(*mocklgr.TxSimulator).GetTxSimulationResultsReturns( 339 rwSetBuilder.GetTxSimulationResults()) 340 return nil 341 } 342 batch, _, err := v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[0]}, true) 343 require.NoError(t, err) 344 require.True(t, batch.PubUpdates.ContainsPostOrderWrites) 345 346 // block with endorser txs 347 batch, _, err = v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[1]}, true) 348 require.NoError(t, err) 349 require.False(t, batch.PubUpdates.ContainsPostOrderWrites) 350 351 // test with block with invalid config tx 352 fakeTxProcessor.GenerateSimulationResultsStub = 353 func(txEnvelop *common.Envelope, s ledger.TxSimulator, initializingLedger bool) error { 354 s.(*mocklgr.TxSimulator).GetTxSimulationResultsReturns(nil, nil) 355 return &ledger.InvalidTxError{Msg: "fake-message"} 356 } 357 batch, _, err = v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blocks[0]}, true) 358 require.NoError(t, err) 359 require.False(t, batch.PubUpdates.ContainsPostOrderWrites) 360 } 361 362 func TestTxStatsInfo(t *testing.T) { 363 testDBEnv := &privacyenabledstate.LevelDBTestEnv{} 364 testDBEnv.Init(t) 365 defer testDBEnv.Cleanup() 366 testDB := testDBEnv.GetDBHandle("emptydb") 367 368 v := NewCommitBatchPreparer(nil, testDB, nil, testHashFunc) 369 370 // create a block with 4 endorser transactions 371 tx1SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 372 &testRwset{ 373 writes: []*testKeyWrite{ 374 {ns: "ns1", key: "key1", val: "val1"}, 375 }, 376 }, 377 ) 378 tx2SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 379 &testRwset{ 380 reads: []*testKeyRead{ 381 {ns: "ns1", key: "key1", version: nil}, // should cause mvcc read-conflict with tx1 382 }, 383 }, 384 ) 385 tx3SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 386 &testRwset{ 387 writes: []*testKeyWrite{ 388 {ns: "ns1", key: "key2", val: "val2"}, 389 }, 390 }, 391 ) 392 tx4SimulationResults, _ := testutilGenerateTxSimulationResultsAsBytes(t, 393 &testRwset{ 394 writes: []*testKeyWrite{ 395 {ns: "ns1", coll: "coll1", key: "key1", val: "val1"}, 396 {ns: "ns1", coll: "coll2", key: "key1", val: "val1"}, 397 }, 398 }, 399 ) 400 401 blockDetails := &testutil.BlockDetails{ 402 BlockNum: 5, 403 PreviousHash: []byte("previousHash"), 404 Txs: []*testutil.TxDetails{ 405 { 406 TxID: "tx_1", 407 ChaincodeName: "cc_1", 408 ChaincodeVersion: "cc_1_v1", 409 SimulationResults: tx1SimulationResults, 410 Type: common.HeaderType_ENDORSER_TRANSACTION, 411 }, 412 { 413 TxID: "tx_2", 414 ChaincodeName: "cc_2", 415 ChaincodeVersion: "cc_2_v1", 416 SimulationResults: tx2SimulationResults, 417 Type: common.HeaderType_ENDORSER_TRANSACTION, 418 }, 419 { 420 TxID: "tx_3", 421 ChaincodeName: "cc_3", 422 ChaincodeVersion: "cc_3_v1", 423 SimulationResults: tx3SimulationResults, 424 Type: common.HeaderType_ENDORSER_TRANSACTION, 425 }, 426 { 427 TxID: "tx_4", 428 ChaincodeName: "cc_4", 429 ChaincodeVersion: "cc_4_v1", 430 SimulationResults: tx4SimulationResults, 431 Type: common.HeaderType_ENDORSER_TRANSACTION, 432 }, 433 }, 434 } 435 436 blk := testutil.ConstructBlockFromBlockDetails(t, blockDetails, false) 437 txsFilter := txflags.New(4) 438 txsFilter.SetFlag(0, peer.TxValidationCode_VALID) 439 txsFilter.SetFlag(1, peer.TxValidationCode_VALID) 440 txsFilter.SetFlag(2, peer.TxValidationCode_BAD_PAYLOAD) 441 txsFilter.SetFlag(3, peer.TxValidationCode_VALID) 442 blk.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter 443 444 // collect the validation stats for the block and check against the expected stats 445 _, txStatsInfo, err := v.ValidateAndPrepareBatch(&ledger.BlockAndPvtData{Block: blk}, true) 446 require.NoError(t, err) 447 expectedTxStatInfo := []*TxStatInfo{ 448 { 449 TxType: common.HeaderType_ENDORSER_TRANSACTION, 450 ValidationCode: peer.TxValidationCode_VALID, 451 ChaincodeID: &peer.ChaincodeID{Name: "cc_1", Version: "cc_1_v1"}, 452 }, 453 { 454 TxType: common.HeaderType_ENDORSER_TRANSACTION, 455 ValidationCode: peer.TxValidationCode_MVCC_READ_CONFLICT, 456 ChaincodeID: &peer.ChaincodeID{Name: "cc_2", Version: "cc_2_v1"}, 457 }, 458 { 459 TxType: -1, 460 ValidationCode: peer.TxValidationCode_BAD_PAYLOAD, 461 }, 462 { 463 TxType: common.HeaderType_ENDORSER_TRANSACTION, 464 ValidationCode: peer.TxValidationCode_VALID, 465 ChaincodeID: &peer.ChaincodeID{Name: "cc_4", Version: "cc_4_v1"}, 466 NumCollections: 2, 467 }, 468 } 469 t.Logf("txStatsInfo=%s\n", spew.Sdump(txStatsInfo)) 470 require.Equal(t, expectedTxStatInfo, txStatsInfo) 471 } 472 473 func testutilSampleTxSimulationResults(t *testing.T, key string) *ledger.TxSimulationResults { 474 rwSetBuilder := rwsetutil.NewRWSetBuilder() 475 // public rws ns1 + ns2 476 rwSetBuilder.AddToReadSet("ns1", key, version.NewHeight(1, 1)) 477 rwSetBuilder.AddToReadSet("ns2", key, version.NewHeight(1, 1)) 478 rwSetBuilder.AddToWriteSet("ns2", key, []byte("ns2-key1-value")) 479 480 // pvt rwset ns1 481 rwSetBuilder.AddToHashedReadSet("ns1", "coll1", key, version.NewHeight(1, 1)) 482 rwSetBuilder.AddToHashedReadSet("ns1", "coll2", key, version.NewHeight(1, 1)) 483 rwSetBuilder.AddToPvtAndHashedWriteSet("ns1", "coll2", key, []byte("pvt-ns1-coll2-key1-value")) 484 485 // pvt rwset ns2 486 rwSetBuilder.AddToHashedReadSet("ns2", "coll1", key, version.NewHeight(1, 1)) 487 rwSetBuilder.AddToHashedReadSet("ns2", "coll2", key, version.NewHeight(1, 1)) 488 rwSetBuilder.AddToPvtAndHashedWriteSet("ns2", "coll2", key, []byte("pvt-ns2-coll2-key1-value")) 489 rwSetBuilder.AddToPvtAndHashedWriteSet("ns2", "coll3", key, nil) 490 491 rwSetBuilder.AddToHashedReadSet("ns3", "coll1", key, version.NewHeight(1, 1)) 492 493 pubAndPvtSimulationResults, err := rwSetBuilder.GetTxSimulationResults() 494 if err != nil { 495 t.Fatalf("ConstructSimulationResultsWithPvtData failed while getting simulation results, err %s", err) 496 } 497 498 return pubAndPvtSimulationResults 499 } 500 501 type testKeyRead struct { 502 ns, coll, key string 503 version *version.Height 504 } 505 type testKeyWrite struct { 506 ns, coll, key string 507 val string 508 } 509 type testRwset struct { 510 reads []*testKeyRead 511 writes []*testKeyWrite 512 } 513 514 func testutilGenerateTxSimulationResults(t *testing.T, rwsetInfo *testRwset) *ledger.TxSimulationResults { 515 rwSetBuilder := rwsetutil.NewRWSetBuilder() 516 for _, r := range rwsetInfo.reads { 517 if r.coll == "" { 518 rwSetBuilder.AddToReadSet(r.ns, r.key, r.version) 519 } else { 520 rwSetBuilder.AddToHashedReadSet(r.ns, r.coll, r.key, r.version) 521 } 522 } 523 524 for _, w := range rwsetInfo.writes { 525 if w.coll == "" { 526 rwSetBuilder.AddToWriteSet(w.ns, w.key, []byte(w.val)) 527 } else { 528 rwSetBuilder.AddToPvtAndHashedWriteSet(w.ns, w.coll, w.key, []byte(w.val)) 529 } 530 } 531 simulationResults, err := rwSetBuilder.GetTxSimulationResults() 532 require.NoError(t, err) 533 return simulationResults 534 } 535 536 func testutilGenerateTxSimulationResultsAsBytes( 537 t *testing.T, rwsetInfo *testRwset) ( 538 publicSimulationRes []byte, pvtWS []byte, 539 ) { 540 simulationRes := testutilGenerateTxSimulationResults(t, rwsetInfo) 541 pub, err := simulationRes.GetPubSimulationBytes() 542 require.NoError(t, err) 543 pvt, err := simulationRes.GetPvtSimulationBytes() 544 require.NoError(t, err) 545 return pub, pvt 546 } 547 548 //go:generate counterfeiter -o mock/txsim.go --fake-name TxSimulator . txSimulator 549 type txSimulator interface { 550 ledger.TxSimulator 551 } 552 553 //go:generate counterfeiter -o mock/processor.go --fake-name Processor . processor 554 type processor interface { 555 ledger.CustomTxProcessor 556 } 557 558 //go:generate counterfeiter -o mock/postOrderSimulatorProvider.go --fake-name PostOrderSimulatorProvider . postOrderSimulatorProvider 559 type postOrderSimulatorProvider interface { 560 PostOrderSimulatorProvider 561 } 562 563 // Test for txType != common.HeaderType_ENDORSER_TRANSACTION 564 func Test_preprocessProtoBlock_processNonEndorserTx(t *testing.T) { 565 // Register customtx processor 566 mockTxProcessor := new(mock.Processor) 567 mockTxProcessor.GenerateSimulationResultsReturns(nil) 568 customTxProcessors := map[common.HeaderType]ledger.CustomTxProcessor{ 569 100: mockTxProcessor, 570 } 571 572 // Prepare param1: txmgr.TxMgr 573 kvw := &kvrwset.KVWrite{Key: "key1", IsDelete: false, Value: []byte{0xde, 0xad, 0xbe, 0xef}} 574 kvrw := &kvrwset.KVRWSet{Writes: []*kvrwset.KVWrite{kvw}} 575 mkvrw, _ := proto.Marshal(kvrw) 576 nrws := rwset.NsReadWriteSet{ 577 Namespace: "ns1", 578 Rwset: mkvrw, 579 } 580 pubsimresults := rwset.TxReadWriteSet{ 581 DataModel: -1, 582 NsRwset: []*rwset.NsReadWriteSet{&nrws}, 583 } 584 txsimres := &ledger.TxSimulationResults{ 585 PubSimulationResults: &pubsimresults, 586 PvtSimulationResults: nil, 587 } 588 txsim := new(mock.TxSimulator) 589 txsim.GetTxSimulationResultsReturns(txsimres, nil) 590 txsimProvider := new(mock.PostOrderSimulatorProvider) 591 txsimProvider.NewTxSimulatorReturns(txsim, nil) 592 593 // Prepare param2: validateKVFunc 594 alwaysValidKVFunc := func(key string, value []byte) error { 595 return nil 596 } 597 598 // Prepare param3: *common.Block 599 pubSimulationResults := [][]byte{} 600 txids := []string{"tx1"} 601 // Get simulation results for tx1 602 rwSetBuilder := rwsetutil.NewRWSetBuilder() 603 tx1SimulationResults, err := rwSetBuilder.GetTxSimulationResults() 604 require.NoError(t, err) 605 // Add tx1 public rwset to the set of results 606 res, err := tx1SimulationResults.GetPubSimulationBytes() 607 require.NoError(t, err) 608 pubSimulationResults = append(pubSimulationResults, res) 609 // Construct a block using a transaction simulation result 610 blk := testutil.ConstructBlockWithTxidHeaderType( 611 t, 612 10, 613 testutil.ConstructRandomBytes(t, 32), 614 pubSimulationResults, 615 txids, 616 false, 617 100, 618 ) 619 620 // Call 621 internalBlock, txsStatInfo, err2 := preprocessProtoBlock(txsimProvider, alwaysValidKVFunc, blk, false, customTxProcessors) 622 623 // Prepare expected value 624 expectedPreprocessedBlock := &block{ 625 num: 10, 626 } 627 value1 := []byte{0xde, 0xad, 0xbe, 0xef} 628 expKVWrite := &kvrwset.KVWrite{ 629 Key: "key1", 630 IsDelete: false, 631 Value: value1, 632 } 633 expKVRWSet := &kvrwset.KVRWSet{ 634 Writes: []*kvrwset.KVWrite{expKVWrite}, 635 } 636 expNsRwSet := &rwsetutil.NsRwSet{ 637 NameSpace: "ns1", 638 KvRwSet: expKVRWSet, 639 } 640 expTxRwSet := &rwsetutil.TxRwSet{ 641 NsRwSets: []*rwsetutil.NsRwSet{expNsRwSet}, 642 } 643 expectedPreprocessedBlock.txs = append( 644 expectedPreprocessedBlock.txs, 645 &transaction{ 646 indexInBlock: 0, 647 id: "tx1", 648 rwset: expTxRwSet, 649 containsPostOrderWrites: true, 650 }, 651 ) 652 expectedTxStatInfo := []*TxStatInfo{ 653 { 654 TxType: 100, 655 }, 656 } 657 658 // Check result 659 require.NoError(t, err2) 660 require.Equal(t, expectedPreprocessedBlock, internalBlock) 661 require.Equal(t, expectedTxStatInfo, txsStatInfo) 662 }