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  }