github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/validation/validator_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  	"crypto/sha256"
    11  	"fmt"
    12  	"os"
    13  	"testing"
    14  
    15  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    16  	"github.com/hyperledger/fabric-protos-go/peer"
    17  	"github.com/osdi23p228/fabric/common/flogging"
    18  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    19  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate"
    20  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    21  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    22  	"github.com/osdi23p228/fabric/core/ledger/util"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  type keyValue struct {
    27  	namespace  string
    28  	collection string
    29  	key        string
    30  	keyHash    []byte
    31  	value      []byte
    32  	version    *version.Height
    33  }
    34  
    35  const (
    36  	levelDBtestEnvName = "levelDB_LockBasedTxMgr"
    37  	couchDBtestEnvName = "couchDB_LockBasedTxMgr"
    38  )
    39  
    40  var (
    41  	// Tests will be run against each environment in this array
    42  	// For example, to skip CouchDB tests, remove &couchDBLockBasedEnv{}
    43  	testEnvs = map[string]privacyenabledstate.TestEnv{
    44  		levelDBtestEnvName: &privacyenabledstate.LevelDBTestEnv{},
    45  		couchDBtestEnvName: &privacyenabledstate.CouchDBTestEnv{},
    46  	}
    47  
    48  	testHashFunc = func(data []byte) ([]byte, error) {
    49  		h := sha256.New()
    50  		if _, err := h.Write(data); err != nil {
    51  			return nil, err
    52  		}
    53  		return h.Sum(nil), nil
    54  	}
    55  )
    56  
    57  func TestMain(m *testing.M) {
    58  	flogging.ActivateSpec("statevalidator,statebasedval,statecouchdb=debug")
    59  	exitCode := m.Run()
    60  	for _, testEnv := range testEnvs {
    61  		testEnv.StopExternalResource()
    62  	}
    63  	os.Exit(exitCode)
    64  }
    65  
    66  func TestValidatorBulkLoadingOfCache(t *testing.T) {
    67  	testDBEnv := testEnvs[couchDBtestEnvName]
    68  	testDBEnv.Init(t)
    69  	defer testDBEnv.Cleanup()
    70  	db := testDBEnv.GetDBHandle("testdb")
    71  
    72  	testValidator := &validator{db: db, hashFunc: testHashFunc}
    73  
    74  	//populate db with initial data
    75  	batch := privacyenabledstate.NewUpdateBatch()
    76  
    77  	// Create two public KV pairs
    78  	pubKV1 := keyValue{namespace: "ns1", key: "key1", value: []byte("value1"), version: version.NewHeight(1, 0)}
    79  	pubKV2 := keyValue{namespace: "ns1", key: "key2", value: []byte("value2"), version: version.NewHeight(1, 1)}
    80  
    81  	// Create two hashed KV pairs
    82  	hashedKV1 := keyValue{namespace: "ns2", collection: "col1", key: "hashedPvtKey1",
    83  		keyHash: util.ComputeStringHash("hashedPvtKey1"), value: []byte("value1"),
    84  		version: version.NewHeight(1, 2)}
    85  	hashedKV2 := keyValue{namespace: "ns2", collection: "col2", key: "hashedPvtKey2",
    86  		keyHash: util.ComputeStringHash("hashedPvtKey2"), value: []byte("value2"),
    87  		version: version.NewHeight(1, 3)}
    88  
    89  	// Store the public and hashed KV pairs to DB
    90  	batch.PubUpdates.Put(pubKV1.namespace, pubKV1.key, pubKV1.value, pubKV1.version)
    91  	batch.PubUpdates.Put(pubKV2.namespace, pubKV2.key, pubKV2.value, pubKV2.version)
    92  	batch.HashUpdates.Put(hashedKV1.namespace, hashedKV1.collection, hashedKV1.keyHash, hashedKV1.value, hashedKV1.version)
    93  	batch.HashUpdates.Put(hashedKV2.namespace, hashedKV2.collection, hashedKV2.keyHash, hashedKV2.value, hashedKV2.version)
    94  
    95  	db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4))
    96  
    97  	// Construct read set for transaction 1. It contains two public KV pairs (pubKV1, pubKV2) and two
    98  	// hashed KV pairs (hashedKV1, hashedKV2).
    99  	rwsetBuilder1 := rwsetutil.NewRWSetBuilder()
   100  	rwsetBuilder1.AddToReadSet(pubKV1.namespace, pubKV1.key, pubKV1.version)
   101  	rwsetBuilder1.AddToReadSet(pubKV2.namespace, pubKV2.key, pubKV2.version)
   102  	rwsetBuilder1.AddToHashedReadSet(hashedKV1.namespace, hashedKV1.collection, hashedKV1.key, hashedKV1.version)
   103  	rwsetBuilder1.AddToHashedReadSet(hashedKV2.namespace, hashedKV2.collection, hashedKV2.key, hashedKV2.version)
   104  
   105  	// Construct read set for transaction 1. It contains KV pairs which are not in the state db.
   106  	rwsetBuilder2 := rwsetutil.NewRWSetBuilder()
   107  	rwsetBuilder2.AddToReadSet("ns3", "key1", nil)
   108  	rwsetBuilder2.AddToHashedReadSet("ns3", "col1", "hashedPvtKey1", nil)
   109  
   110  	// Construct internal block
   111  	transRWSets := getTestPubSimulationRWSet(t, rwsetBuilder1, rwsetBuilder2)
   112  	var trans []*transaction
   113  	for i, tranRWSet := range transRWSets {
   114  		tx := &transaction{
   115  			id:             fmt.Sprintf("txid-%d", i),
   116  			indexInBlock:   i,
   117  			validationCode: peer.TxValidationCode_VALID,
   118  			rwset:          tranRWSet,
   119  		}
   120  		trans = append(trans, tx)
   121  	}
   122  	blk := &block{num: 1, txs: trans}
   123  
   124  	if testValidator.db.IsBulkOptimizable() {
   125  
   126  		db := testValidator.db
   127  		bulkOptimizable, _ := db.VersionedDB.(statedb.BulkOptimizable)
   128  
   129  		// Clear cache loaded during ApplyPrivacyAwareUpdates()
   130  		testValidator.db.ClearCachedVersions()
   131  
   132  		testValidator.preLoadCommittedVersionOfRSet(blk)
   133  
   134  		// pubKV1 should be found in cache
   135  		version, keyFound := bulkOptimizable.GetCachedVersion(pubKV1.namespace, pubKV1.key)
   136  		require.True(t, keyFound)
   137  		require.Equal(t, pubKV1.version, version)
   138  
   139  		// pubKV2 should be found in cache
   140  		version, keyFound = bulkOptimizable.GetCachedVersion(pubKV2.namespace, pubKV2.key)
   141  		require.True(t, keyFound)
   142  		require.Equal(t, pubKV2.version, version)
   143  
   144  		// [ns3, key1] should be found in cache as it was in the readset of transaction 1 though it is
   145  		// not in the state db but the version would be nil
   146  		version, keyFound = bulkOptimizable.GetCachedVersion("ns3", "key1")
   147  		require.True(t, keyFound)
   148  		require.Nil(t, version)
   149  
   150  		// [ns4, key1] should not be found in cache as it was not loaded
   151  		version, keyFound = bulkOptimizable.GetCachedVersion("ns4", "key1")
   152  		require.False(t, keyFound)
   153  		require.Nil(t, version)
   154  
   155  		// hashedKV1 should be found in cache
   156  		version, keyFound = testValidator.db.GetCachedKeyHashVersion(hashedKV1.namespace,
   157  			hashedKV1.collection, hashedKV1.keyHash)
   158  		require.True(t, keyFound)
   159  		require.Equal(t, hashedKV1.version, version)
   160  
   161  		// hashedKV2 should be found in cache
   162  		version, keyFound = testValidator.db.GetCachedKeyHashVersion(hashedKV2.namespace,
   163  			hashedKV2.collection, hashedKV2.keyHash)
   164  		require.True(t, keyFound)
   165  		require.Equal(t, hashedKV2.version, version)
   166  
   167  		// [ns3, col1, hashedPvtKey1] should be found in cache as it was in the readset of transaction 2 though it is
   168  		// not in the state db
   169  		version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns3", "col1", util.ComputeStringHash("hashedPvtKey1"))
   170  		require.True(t, keyFound)
   171  		require.Nil(t, version)
   172  
   173  		// [ns4, col, key1] should not be found in cache as it was not loaded
   174  		version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns4", "col1", util.ComputeStringHash("key1"))
   175  		require.False(t, keyFound)
   176  		require.Nil(t, version)
   177  
   178  		// Clear cache
   179  		testValidator.db.ClearCachedVersions()
   180  
   181  		// pubKV1 should not be found in cache as cahce got emptied
   182  		version, keyFound = bulkOptimizable.GetCachedVersion(pubKV1.namespace, pubKV1.key)
   183  		require.False(t, keyFound)
   184  		require.Nil(t, version)
   185  
   186  		// [ns3, col1, key1] should not be found in cache as cahce got emptied
   187  		version, keyFound = testValidator.db.GetCachedKeyHashVersion("ns3", "col1", util.ComputeStringHash("hashedPvtKey1"))
   188  		require.False(t, keyFound)
   189  		require.Nil(t, version)
   190  	}
   191  }
   192  
   193  func TestValidator(t *testing.T) {
   194  	testDBEnv := testEnvs[levelDBtestEnvName]
   195  	testDBEnv.Init(t)
   196  	defer testDBEnv.Cleanup()
   197  	db := testDBEnv.GetDBHandle("TestDB")
   198  
   199  	//populate db with initial data
   200  	batch := privacyenabledstate.NewUpdateBatch()
   201  	batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0))
   202  	batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1))
   203  	batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2))
   204  	batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3))
   205  	batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4))
   206  	db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4))
   207  
   208  	testValidator := &validator{db: db, hashFunc: testHashFunc}
   209  
   210  	//rwset1 should be valid
   211  	rwsetBuilder1 := rwsetutil.NewRWSetBuilder()
   212  	rwsetBuilder1.AddToReadSet("ns1", "key1", version.NewHeight(1, 0))
   213  	rwsetBuilder1.AddToReadSet("ns2", "key2", nil)
   214  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{})
   215  
   216  	//rwset2 should not be valid
   217  	rwsetBuilder2 := rwsetutil.NewRWSetBuilder()
   218  	rwsetBuilder2.AddToReadSet("ns1", "key1", version.NewHeight(1, 1))
   219  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0})
   220  
   221  	//rwset3 should not be valid
   222  	rwsetBuilder3 := rwsetutil.NewRWSetBuilder()
   223  	rwsetBuilder3.AddToReadSet("ns1", "key1", nil)
   224  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder3), []int{0})
   225  
   226  	// rwset4 and rwset5 within same block - rwset4 should be valid and makes rwset5 as invalid
   227  	rwsetBuilder4 := rwsetutil.NewRWSetBuilder()
   228  	rwsetBuilder4.AddToReadSet("ns1", "key1", version.NewHeight(1, 0))
   229  	rwsetBuilder4.AddToWriteSet("ns1", "key1", []byte("value1_new"))
   230  
   231  	rwsetBuilder5 := rwsetutil.NewRWSetBuilder()
   232  	rwsetBuilder5.AddToReadSet("ns1", "key1", version.NewHeight(1, 0))
   233  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder4, rwsetBuilder5), []int{1})
   234  }
   235  
   236  func TestPhantomValidation(t *testing.T) {
   237  	testDBEnv := testEnvs[levelDBtestEnvName]
   238  	testDBEnv.Init(t)
   239  	defer testDBEnv.Cleanup()
   240  	db := testDBEnv.GetDBHandle("TestDB")
   241  
   242  	//populate db with initial data
   243  	batch := privacyenabledstate.NewUpdateBatch()
   244  	batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0))
   245  	batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1))
   246  	batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2))
   247  	batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3))
   248  	batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4))
   249  	db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 4))
   250  
   251  	testValidator := &validator{db: db, hashFunc: testHashFunc}
   252  
   253  	//rwset1 should be valid
   254  	rwsetBuilder1 := rwsetutil.NewRWSetBuilder()
   255  	rqi1 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: true}
   256  	rwsetutil.SetRawReads(rqi1, []*kvrwset.KVRead{
   257  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   258  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 2))})
   259  	rwsetBuilder1.AddToRangeQuerySet("ns1", rqi1)
   260  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{})
   261  
   262  	//rwset2 should not be valid - Version of key4 changed
   263  	rwsetBuilder2 := rwsetutil.NewRWSetBuilder()
   264  	rqi2 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false}
   265  	rwsetutil.SetRawReads(rqi2, []*kvrwset.KVRead{
   266  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   267  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)),
   268  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 2))})
   269  	rwsetBuilder2.AddToRangeQuerySet("ns1", rqi2)
   270  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0})
   271  
   272  	//rwset3 should not be valid - simulate key3 got committed to db
   273  	rwsetBuilder3 := rwsetutil.NewRWSetBuilder()
   274  	rqi3 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false}
   275  	rwsetutil.SetRawReads(rqi3, []*kvrwset.KVRead{
   276  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   277  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))})
   278  	rwsetBuilder3.AddToRangeQuerySet("ns1", rqi3)
   279  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder3), []int{0})
   280  
   281  	// //Remove a key in rwset4 and rwset5 should become invalid
   282  	rwsetBuilder4 := rwsetutil.NewRWSetBuilder()
   283  	rwsetBuilder4.AddToWriteSet("ns1", "key3", nil)
   284  	rwsetBuilder5 := rwsetutil.NewRWSetBuilder()
   285  	rqi5 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false}
   286  	rwsetutil.SetRawReads(rqi5, []*kvrwset.KVRead{
   287  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   288  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)),
   289  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))})
   290  	rwsetBuilder5.AddToRangeQuerySet("ns1", rqi5)
   291  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder4, rwsetBuilder5), []int{1})
   292  
   293  	//Add a key in rwset6 and rwset7 should become invalid
   294  	rwsetBuilder6 := rwsetutil.NewRWSetBuilder()
   295  	rwsetBuilder6.AddToWriteSet("ns1", "key2_1", []byte("value2_1"))
   296  
   297  	rwsetBuilder7 := rwsetutil.NewRWSetBuilder()
   298  	rqi7 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key4", ItrExhausted: false}
   299  	rwsetutil.SetRawReads(rqi7, []*kvrwset.KVRead{
   300  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   301  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)),
   302  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 3))})
   303  	rwsetBuilder7.AddToRangeQuerySet("ns1", rqi7)
   304  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder6, rwsetBuilder7), []int{1})
   305  }
   306  
   307  func TestPhantomHashBasedValidation(t *testing.T) {
   308  	testDBEnv := testEnvs[levelDBtestEnvName]
   309  	testDBEnv.Init(t)
   310  	defer testDBEnv.Cleanup()
   311  	db := testDBEnv.GetDBHandle("TestDB")
   312  
   313  	//populate db with initial data
   314  	batch := privacyenabledstate.NewUpdateBatch()
   315  	batch.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 0))
   316  	batch.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 1))
   317  	batch.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 2))
   318  	batch.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 3))
   319  	batch.PubUpdates.Put("ns1", "key5", []byte("value5"), version.NewHeight(1, 4))
   320  	batch.PubUpdates.Put("ns1", "key6", []byte("value6"), version.NewHeight(1, 5))
   321  	batch.PubUpdates.Put("ns1", "key7", []byte("value7"), version.NewHeight(1, 6))
   322  	batch.PubUpdates.Put("ns1", "key8", []byte("value8"), version.NewHeight(1, 7))
   323  	batch.PubUpdates.Put("ns1", "key9", []byte("value9"), version.NewHeight(1, 8))
   324  	db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 8))
   325  
   326  	testValidator := &validator{db: db, hashFunc: testHashFunc}
   327  
   328  	rwsetBuilder1 := rwsetutil.NewRWSetBuilder()
   329  	rqi1 := &kvrwset.RangeQueryInfo{StartKey: "key2", EndKey: "key9", ItrExhausted: true}
   330  	kvReadsDuringSimulation1 := []*kvrwset.KVRead{
   331  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   332  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 2)),
   333  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 3)),
   334  		rwsetutil.NewKVRead("key5", version.NewHeight(1, 4)),
   335  		rwsetutil.NewKVRead("key6", version.NewHeight(1, 5)),
   336  		rwsetutil.NewKVRead("key7", version.NewHeight(1, 6)),
   337  		rwsetutil.NewKVRead("key8", version.NewHeight(1, 7)),
   338  	}
   339  	rwsetutil.SetMerkelSummary(rqi1, buildTestHashResults(t, 2, kvReadsDuringSimulation1))
   340  	rwsetBuilder1.AddToRangeQuerySet("ns1", rqi1)
   341  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder1), []int{})
   342  
   343  	rwsetBuilder2 := rwsetutil.NewRWSetBuilder()
   344  	rqi2 := &kvrwset.RangeQueryInfo{StartKey: "key1", EndKey: "key9", ItrExhausted: false}
   345  	kvReadsDuringSimulation2 := []*kvrwset.KVRead{
   346  		rwsetutil.NewKVRead("key1", version.NewHeight(1, 0)),
   347  		rwsetutil.NewKVRead("key2", version.NewHeight(1, 1)),
   348  		rwsetutil.NewKVRead("key3", version.NewHeight(1, 1)),
   349  		rwsetutil.NewKVRead("key4", version.NewHeight(1, 3)),
   350  		rwsetutil.NewKVRead("key5", version.NewHeight(1, 4)),
   351  		rwsetutil.NewKVRead("key6", version.NewHeight(1, 5)),
   352  		rwsetutil.NewKVRead("key7", version.NewHeight(1, 6)),
   353  		rwsetutil.NewKVRead("key8", version.NewHeight(1, 7)),
   354  		rwsetutil.NewKVRead("key9", version.NewHeight(1, 8)),
   355  	}
   356  	rwsetutil.SetMerkelSummary(rqi2, buildTestHashResults(t, 2, kvReadsDuringSimulation2))
   357  	rwsetBuilder2.AddToRangeQuerySet("ns1", rqi2)
   358  	checkValidation(t, testValidator, getTestPubSimulationRWSet(t, rwsetBuilder2), []int{0})
   359  }
   360  
   361  func checkValidation(t *testing.T, val *validator, transRWSets []*rwsetutil.TxRwSet, expectedInvalidTxIndexes []int) {
   362  	var trans []*transaction
   363  	for i, tranRWSet := range transRWSets {
   364  		tx := &transaction{
   365  			id:             fmt.Sprintf("txid-%d", i),
   366  			indexInBlock:   i,
   367  			validationCode: peer.TxValidationCode_VALID,
   368  			rwset:          tranRWSet,
   369  		}
   370  		trans = append(trans, tx)
   371  	}
   372  	blk := &block{num: 1, txs: trans}
   373  	_, err := val.validateAndPrepareBatch(blk, true)
   374  	require.NoError(t, err)
   375  	t.Logf("block.Txs[0].ValidationCode = %d", blk.txs[0].validationCode)
   376  	var invalidTxs []int
   377  	for _, tx := range blk.txs {
   378  		if tx.validationCode != peer.TxValidationCode_VALID {
   379  			invalidTxs = append(invalidTxs, tx.indexInBlock)
   380  		}
   381  	}
   382  	require.Equal(t, len(expectedInvalidTxIndexes), len(invalidTxs))
   383  	require.ElementsMatch(t, invalidTxs, expectedInvalidTxIndexes)
   384  }
   385  
   386  func buildTestHashResults(t *testing.T, maxDegree int, kvReads []*kvrwset.KVRead) *kvrwset.QueryReadsMerkleSummary {
   387  	if len(kvReads) <= maxDegree {
   388  		t.Fatal("This method should be called with number of KVReads more than maxDegree; Else, hashing won't be performedrwset")
   389  	}
   390  	helper, _ := rwsetutil.NewRangeQueryResultsHelper(true, uint32(maxDegree), testHashFunc)
   391  	for _, kvRead := range kvReads {
   392  		helper.AddResult(kvRead)
   393  	}
   394  	_, h, err := helper.Done()
   395  	require.NoError(t, err)
   396  	require.NotNil(t, h)
   397  	return h
   398  }
   399  
   400  func getTestPubSimulationRWSet(t *testing.T, builders ...*rwsetutil.RWSetBuilder) []*rwsetutil.TxRwSet {
   401  	var pubRWSets []*rwsetutil.TxRwSet
   402  	for _, b := range builders {
   403  		s, e := b.GetTxSimulationResults()
   404  		require.NoError(t, e)
   405  		sBytes, err := s.GetPubSimulationBytes()
   406  		require.NoError(t, err)
   407  		pubRWSet := &rwsetutil.TxRwSet{}
   408  		require.NoError(t, pubRWSet.FromProtoBytes(sBytes))
   409  		pubRWSets = append(pubRWSets, pubRWSet)
   410  	}
   411  	return pubRWSets
   412  }