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  }