github.com/Hnampk/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/validator/valimpl/helper_test.go (about)

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