github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/history/db_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package history
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"os"
    13  	"strconv"
    14  	"testing"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hyperledger/fabric-protos-go/common"
    18  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    19  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    20  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    21  	"github.com/hyperledger/fabric-protos-go/peer"
    22  	configtxtest "github.com/osdi23p228/fabric/common/configtx/test"
    23  	"github.com/osdi23p228/fabric/common/flogging"
    24  	"github.com/osdi23p228/fabric/common/ledger/testutil"
    25  	util2 "github.com/osdi23p228/fabric/common/util"
    26  	"github.com/osdi23p228/fabric/core/ledger"
    27  	"github.com/osdi23p228/fabric/internal/pkg/txflags"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func TestMain(m *testing.M) {
    33  	flogging.ActivateSpec("leveldbhelper,history=debug")
    34  	os.Exit(m.Run())
    35  }
    36  
    37  //TestSavepoint tests that save points get written after each block and get returned via GetBlockNumfromSavepoint
    38  func TestSavepoint(t *testing.T) {
    39  	env := newTestHistoryEnv(t)
    40  	defer env.cleanup()
    41  
    42  	// read the savepoint, it should not exist and should return nil Height object
    43  	savepoint, err := env.testHistoryDB.GetLastSavepoint()
    44  	assert.NoError(t, err, "Error upon historyDatabase.GetLastSavepoint()")
    45  	assert.Nil(t, savepoint)
    46  
    47  	// ShouldRecover should return true when no savepoint is found and recovery from block 0
    48  	status, blockNum, err := env.testHistoryDB.ShouldRecover(0)
    49  	assert.NoError(t, err, "Error upon historyDatabase.ShouldRecover()")
    50  	assert.True(t, status)
    51  	assert.Equal(t, uint64(0), blockNum)
    52  
    53  	bg, gb := testutil.NewBlockGenerator(t, "testLedger", false)
    54  	assert.NoError(t, env.testHistoryDB.Commit(gb))
    55  	// read the savepoint, it should now exist and return a Height object with BlockNum 0
    56  	savepoint, err = env.testHistoryDB.GetLastSavepoint()
    57  	assert.NoError(t, err, "Error upon historyDatabase.GetLastSavepoint()")
    58  	assert.Equal(t, uint64(0), savepoint.BlockNum)
    59  
    60  	// create the next block (block 1)
    61  	txid := util2.GenerateUUID()
    62  	simulator, _ := env.txmgr.NewTxSimulator(txid)
    63  	simulator.SetState("ns1", "key1", []byte("value1"))
    64  	simulator.Done()
    65  	simRes, _ := simulator.GetTxSimulationResults()
    66  	pubSimResBytes, _ := simRes.GetPubSimulationBytes()
    67  	block1 := bg.NextBlock([][]byte{pubSimResBytes})
    68  	assert.NoError(t, env.testHistoryDB.Commit(block1))
    69  	savepoint, err = env.testHistoryDB.GetLastSavepoint()
    70  	assert.NoError(t, err, "Error upon historyDatabase.GetLastSavepoint()")
    71  	assert.Equal(t, uint64(1), savepoint.BlockNum)
    72  
    73  	// Should Recover should return false
    74  	status, blockNum, err = env.testHistoryDB.ShouldRecover(1)
    75  	assert.NoError(t, err, "Error upon historyDatabase.ShouldRecover()")
    76  	assert.False(t, status)
    77  	assert.Equal(t, uint64(2), blockNum)
    78  
    79  	// create the next block (block 2)
    80  	txid = util2.GenerateUUID()
    81  	simulator, _ = env.txmgr.NewTxSimulator(txid)
    82  	simulator.SetState("ns1", "key1", []byte("value2"))
    83  	simulator.Done()
    84  	simRes, _ = simulator.GetTxSimulationResults()
    85  	pubSimResBytes, _ = simRes.GetPubSimulationBytes()
    86  	block2 := bg.NextBlock([][]byte{pubSimResBytes})
    87  
    88  	// assume that the peer failed to commit this block to historyDB and is being recovered now
    89  	env.testHistoryDB.CommitLostBlock(&ledger.BlockAndPvtData{Block: block2})
    90  	savepoint, err = env.testHistoryDB.GetLastSavepoint()
    91  	assert.NoError(t, err, "Error upon historyDatabase.GetLastSavepoint()")
    92  	assert.Equal(t, uint64(2), savepoint.BlockNum)
    93  
    94  	//Pass high blockNum, ShouldRecover should return true with 3 as blocknum to recover from
    95  	status, blockNum, err = env.testHistoryDB.ShouldRecover(10)
    96  	assert.NoError(t, err, "Error upon historyDatabase.ShouldRecover()")
    97  	assert.True(t, status)
    98  	assert.Equal(t, uint64(3), blockNum)
    99  }
   100  
   101  func TestHistory(t *testing.T) {
   102  	env := newTestHistoryEnv(t)
   103  	defer env.cleanup()
   104  	provider := env.testBlockStorageEnv.provider
   105  	ledger1id := "ledger1"
   106  	store1, err := provider.Open(ledger1id)
   107  	assert.NoError(t, err, "Error upon provider.OpenBlockStore()")
   108  	defer store1.Shutdown()
   109  	assert.Equal(t, "history", env.testHistoryDB.Name())
   110  
   111  	bg, gb := testutil.NewBlockGenerator(t, ledger1id, false)
   112  	assert.NoError(t, store1.AddBlock(gb))
   113  	assert.NoError(t, env.testHistoryDB.Commit(gb))
   114  
   115  	//block1
   116  	txid := util2.GenerateUUID()
   117  	simulator, _ := env.txmgr.NewTxSimulator(txid)
   118  	value1 := []byte("value1")
   119  	simulator.SetState("ns1", "key7", value1)
   120  	simulator.Done()
   121  	simRes, _ := simulator.GetTxSimulationResults()
   122  	pubSimResBytes, _ := simRes.GetPubSimulationBytes()
   123  	block1 := bg.NextBlock([][]byte{pubSimResBytes})
   124  	err = store1.AddBlock(block1)
   125  	assert.NoError(t, err)
   126  	err = env.testHistoryDB.Commit(block1)
   127  	assert.NoError(t, err)
   128  
   129  	//block2 tran1
   130  	simulationResults := [][]byte{}
   131  	txid = util2.GenerateUUID()
   132  	simulator, _ = env.txmgr.NewTxSimulator(txid)
   133  	value2 := []byte("value2")
   134  	simulator.SetState("ns1", "key7", value2)
   135  	simulator.Done()
   136  	simRes, _ = simulator.GetTxSimulationResults()
   137  	pubSimResBytes, _ = simRes.GetPubSimulationBytes()
   138  	simulationResults = append(simulationResults, pubSimResBytes)
   139  	//block2 tran2
   140  	txid2 := util2.GenerateUUID()
   141  	simulator2, _ := env.txmgr.NewTxSimulator(txid2)
   142  	value3 := []byte("value3")
   143  	simulator2.SetState("ns1", "key7", value3)
   144  	simulator2.Done()
   145  	simRes2, _ := simulator2.GetTxSimulationResults()
   146  	pubSimResBytes2, _ := simRes2.GetPubSimulationBytes()
   147  	simulationResults = append(simulationResults, pubSimResBytes2)
   148  	block2 := bg.NextBlock(simulationResults)
   149  	err = store1.AddBlock(block2)
   150  	assert.NoError(t, err)
   151  	err = env.testHistoryDB.Commit(block2)
   152  	assert.NoError(t, err)
   153  
   154  	//block3
   155  	txid = util2.GenerateUUID()
   156  	simulator, _ = env.txmgr.NewTxSimulator(txid)
   157  	simulator.DeleteState("ns1", "key7")
   158  	simulator.Done()
   159  	simRes, _ = simulator.GetTxSimulationResults()
   160  	pubSimResBytes, _ = simRes.GetPubSimulationBytes()
   161  	block3 := bg.NextBlock([][]byte{pubSimResBytes})
   162  	err = store1.AddBlock(block3)
   163  	assert.NoError(t, err)
   164  	err = env.testHistoryDB.Commit(block3)
   165  	assert.NoError(t, err)
   166  	t.Logf("Inserted all 3 blocks")
   167  
   168  	qhistory, err := env.testHistoryDB.NewQueryExecutor(store1)
   169  	assert.NoError(t, err, "Error upon NewQueryExecutor")
   170  
   171  	itr, err2 := qhistory.GetHistoryForKey("ns1", "key7")
   172  	assert.NoError(t, err2, "Error upon GetHistoryForKey()")
   173  
   174  	count := 0
   175  	for {
   176  		// iterator will return entries in the order of newest to oldest
   177  		kmod, _ := itr.Next()
   178  		if kmod == nil {
   179  			break
   180  		}
   181  		txid = kmod.(*queryresult.KeyModification).TxId
   182  		retrievedValue := kmod.(*queryresult.KeyModification).Value
   183  		retrievedTimestamp := kmod.(*queryresult.KeyModification).Timestamp
   184  		retrievedIsDelete := kmod.(*queryresult.KeyModification).IsDelete
   185  		t.Logf("Retrieved history record for key=key7 at TxId=%s with value %v and timestamp %v",
   186  			txid, retrievedValue, retrievedTimestamp)
   187  		count++
   188  		if count != 1 {
   189  			// entries 2, 3, 4 are block2:tran2, block2:tran1 and block1:tran1
   190  			expectedValue := []byte("value" + strconv.Itoa(5-count))
   191  			assert.Equal(t, expectedValue, retrievedValue)
   192  			assert.NotNil(t, retrievedTimestamp)
   193  			assert.False(t, retrievedIsDelete)
   194  		} else {
   195  			// entry 1 is block3:tran1
   196  			assert.Equal(t, []uint8(nil), retrievedValue)
   197  			assert.NotNil(t, retrievedTimestamp)
   198  			assert.True(t, retrievedIsDelete)
   199  		}
   200  	}
   201  	assert.Equal(t, 4, count)
   202  
   203  	t.Run("test-iter-error-path", func(t *testing.T) {
   204  		env.testHistoryDBProvider.Close()
   205  		qhistory, err = env.testHistoryDB.NewQueryExecutor(store1)
   206  		itr, err = qhistory.GetHistoryForKey("ns1", "key7")
   207  		require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed")
   208  		require.Nil(t, itr)
   209  
   210  	})
   211  }
   212  
   213  func TestHistoryForInvalidTran(t *testing.T) {
   214  	env := newTestHistoryEnv(t)
   215  	defer env.cleanup()
   216  	provider := env.testBlockStorageEnv.provider
   217  	ledger1id := "ledger1"
   218  	store1, err := provider.Open(ledger1id)
   219  	assert.NoError(t, err, "Error upon provider.OpenBlockStore()")
   220  	defer store1.Shutdown()
   221  
   222  	bg, gb := testutil.NewBlockGenerator(t, ledger1id, false)
   223  	assert.NoError(t, store1.AddBlock(gb))
   224  	assert.NoError(t, env.testHistoryDB.Commit(gb))
   225  
   226  	//block1
   227  	txid := util2.GenerateUUID()
   228  	simulator, _ := env.txmgr.NewTxSimulator(txid)
   229  	value1 := []byte("value1")
   230  	simulator.SetState("ns1", "key7", value1)
   231  	simulator.Done()
   232  	simRes, _ := simulator.GetTxSimulationResults()
   233  	pubSimResBytes, _ := simRes.GetPubSimulationBytes()
   234  	block1 := bg.NextBlock([][]byte{pubSimResBytes})
   235  
   236  	//for this invalid tran test, set the transaction to invalid
   237  	txsFilter := txflags.ValidationFlags(block1.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
   238  	txsFilter.SetFlag(0, peer.TxValidationCode_INVALID_OTHER_REASON)
   239  	block1.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter
   240  
   241  	err = store1.AddBlock(block1)
   242  	assert.NoError(t, err)
   243  	err = env.testHistoryDB.Commit(block1)
   244  	assert.NoError(t, err)
   245  
   246  	qhistory, err := env.testHistoryDB.NewQueryExecutor(store1)
   247  	assert.NoError(t, err, "Error upon NewQueryExecutor")
   248  
   249  	itr, err2 := qhistory.GetHistoryForKey("ns1", "key7")
   250  	assert.NoError(t, err2, "Error upon GetHistoryForKey()")
   251  
   252  	// test that there are no history values, since the tran was marked as invalid
   253  	kmod, _ := itr.Next()
   254  	assert.Nil(t, kmod)
   255  }
   256  
   257  //TestGenesisBlockNoError tests that Genesis blocks are ignored by history processing
   258  // since we only persist history of chaincode key writes
   259  func TestGenesisBlockNoError(t *testing.T) {
   260  	env := newTestHistoryEnv(t)
   261  	defer env.cleanup()
   262  	block, err := configtxtest.MakeGenesisBlock("test_chainid")
   263  	assert.NoError(t, err)
   264  	err = env.testHistoryDB.Commit(block)
   265  	assert.NoError(t, err)
   266  }
   267  
   268  // TestHistoryWithKeyContainingNilBytes tests historydb when keys contains nil bytes (FAB-11244) -
   269  // which happens to be used as a separator in the composite keys that is formed for the entries in the historydb
   270  func TestHistoryWithKeyContainingNilBytes(t *testing.T) {
   271  	env := newTestHistoryEnv(t)
   272  	defer env.cleanup()
   273  	provider := env.testBlockStorageEnv.provider
   274  	ledger1id := "ledger1"
   275  	store1, err := provider.Open(ledger1id)
   276  	assert.NoError(t, err, "Error upon provider.OpenBlockStore()")
   277  	defer store1.Shutdown()
   278  
   279  	bg, gb := testutil.NewBlockGenerator(t, ledger1id, false)
   280  	assert.NoError(t, store1.AddBlock(gb))
   281  	assert.NoError(t, env.testHistoryDB.Commit(gb))
   282  
   283  	//block1
   284  	txid := util2.GenerateUUID()
   285  	simulator, _ := env.txmgr.NewTxSimulator(txid)
   286  	simulator.SetState("ns1", "key", []byte("value1")) // add a key <key> that contains no nil byte
   287  	simulator.Done()
   288  	simRes, _ := simulator.GetTxSimulationResults()
   289  	pubSimResBytes, _ := simRes.GetPubSimulationBytes()
   290  	block1 := bg.NextBlock([][]byte{pubSimResBytes})
   291  	err = store1.AddBlock(block1)
   292  	assert.NoError(t, err)
   293  	err = env.testHistoryDB.Commit(block1)
   294  	assert.NoError(t, err)
   295  
   296  	//block2 tran1
   297  	simulationResults := [][]byte{}
   298  	txid = util2.GenerateUUID()
   299  	simulator, _ = env.txmgr.NewTxSimulator(txid)
   300  	simulator.SetState("ns1", "key", []byte("value2")) // add another value for the key <key>
   301  	simulator.Done()
   302  	simRes, _ = simulator.GetTxSimulationResults()
   303  	pubSimResBytes, _ = simRes.GetPubSimulationBytes()
   304  	simulationResults = append(simulationResults, pubSimResBytes)
   305  
   306  	//block2 tran2
   307  	txid2 := util2.GenerateUUID()
   308  	simulator2, _ := env.txmgr.NewTxSimulator(txid2)
   309  
   310  	// key1 should not fall in the range
   311  	key1 := "\x00key\x00\x01\x01\x15"
   312  	simulator2.SetState("ns1", key1, []byte("dummyVal1"))
   313  
   314  	// add other keys that contain nil byte(s) - such that when a range query is formed, these keys fall in the range
   315  	// key2 is skipped due to tran num decoding error (decode size 21 > 8)
   316  	// blockNumTranNumBytes are 0x1, 0x1, 0x15, 0x0 (separator), 0x1, 0x2, 0x1, 0x1
   317  	key2 := "key\x00\x01\x01\x15" // \x15 is 21
   318  	simulator2.SetState("ns1", key2, []byte("dummyVal2"))
   319  
   320  	// key3 is skipped due to block num decoding error (decoded size 12 > 8)
   321  	// blockNumTranNumBytes are 0xc, 0x0 (separtor), 0x1, 0x2, 0x1, 0x1
   322  	key3 := "key\x00\x0c" // \x0c is 12
   323  	simulator2.SetState("ns1", key3, []byte("dummyVal3"))
   324  
   325  	// key4 is skipped because blockBytesConsumed (2) + tranBytesConsumed (2) != len(blockNumTranNum) (6)
   326  	// blockNumTranNumBytes are 0x1, 0x0 (separator), 0x1, 0x2, 0x1, 0x1
   327  	key4 := "key\x00\x01"
   328  	simulator2.SetState("ns1", key4, []byte("dummyVal4"))
   329  
   330  	// key5 is skipped due to ErrNotFoundInIndex, where history key is <ns, key\x00\x04\x01, 2, 1>, same as <ns, key, 16777474, 1>.
   331  	// blockNumTranNumBytes are 0x4, 0x1, 0x0 (separator), 0x1, 0x2, 0x1, 0x1
   332  	key5 := "key\x00\x04\x01"
   333  	simulator2.SetState("ns1", key5, []byte("dummyVal5"))
   334  
   335  	// commit block2
   336  	simulator2.Done()
   337  	simRes2, _ := simulator2.GetTxSimulationResults()
   338  	pubSimResBytes2, _ := simRes2.GetPubSimulationBytes()
   339  	simulationResults = append(simulationResults, pubSimResBytes2)
   340  	block2 := bg.NextBlock(simulationResults)
   341  	err = store1.AddBlock(block2)
   342  	assert.NoError(t, err)
   343  	err = env.testHistoryDB.Commit(block2)
   344  	assert.NoError(t, err)
   345  
   346  	qhistory, err := env.testHistoryDB.NewQueryExecutor(store1)
   347  	assert.NoError(t, err, "Error upon NewQueryExecutor")
   348  
   349  	// verify the results for each key, in the order of newest to oldest
   350  	testutilVerifyResults(t, qhistory, "ns1", "key", []string{"value2", "value1"})
   351  	testutilVerifyResults(t, qhistory, "ns1", key1, []string{"dummyVal1"})
   352  	testutilVerifyResults(t, qhistory, "ns1", key2, []string{"dummyVal2"})
   353  	testutilVerifyResults(t, qhistory, "ns1", key3, []string{"dummyVal3"})
   354  	testutilVerifyResults(t, qhistory, "ns1", key4, []string{"dummyVal4"})
   355  	testutilVerifyResults(t, qhistory, "ns1", key5, []string{"dummyVal5"})
   356  
   357  	// verify none of key1-key5 falls in the range of history query for "key"
   358  	testutilCheckKeyNotInRange(t, qhistory, "ns1", "key", key1)
   359  	testutilCheckKeyNotInRange(t, qhistory, "ns1", "key", key2)
   360  	testutilCheckKeyNotInRange(t, qhistory, "ns1", "key", key3)
   361  	testutilCheckKeyNotInRange(t, qhistory, "ns1", "key", key4)
   362  	testutilCheckKeyNotInRange(t, qhistory, "ns1", "key", key5)
   363  }
   364  
   365  // TestHistoryWithBlockNumber256 creates 256 blocks and then
   366  // queries historydb to verify that all 256 blocks are returned in the right orderer.
   367  // This test also verifies that block256 is returned correctly even if its key contains nil byte.
   368  func TestHistoryWithBlockNumber256(t *testing.T) {
   369  	env := newTestHistoryEnv(t)
   370  	defer env.cleanup()
   371  	provider := env.testBlockStorageEnv.provider
   372  	ledger1id := "ledger1"
   373  	store1, err := provider.Open(ledger1id)
   374  	assert.NoError(t, err, "Error upon provider.OpenBlockStore()")
   375  	defer store1.Shutdown()
   376  
   377  	bg, gb := testutil.NewBlockGenerator(t, ledger1id, false)
   378  	assert.NoError(t, store1.AddBlock(gb))
   379  	assert.NoError(t, env.testHistoryDB.Commit(gb))
   380  
   381  	// add 256 blocks, each block has 1 transaction setting state for "ns1" and "key", value is "value<blockNum>"
   382  	for i := 1; i <= 256; i++ {
   383  		txid := util2.GenerateUUID()
   384  		simulator, _ := env.txmgr.NewTxSimulator(txid)
   385  		value := fmt.Sprintf("value%d", i)
   386  		simulator.SetState("ns1", "key", []byte(value))
   387  		simulator.Done()
   388  		simRes, _ := simulator.GetTxSimulationResults()
   389  		pubSimResBytes, _ := simRes.GetPubSimulationBytes()
   390  		block := bg.NextBlock([][]byte{pubSimResBytes})
   391  		err = store1.AddBlock(block)
   392  		assert.NoError(t, err)
   393  		err = env.testHistoryDB.Commit(block)
   394  		assert.NoError(t, err)
   395  	}
   396  
   397  	// query history db for "ns1", "key"
   398  	qhistory, err := env.testHistoryDB.NewQueryExecutor(store1)
   399  	assert.NoError(t, err, "Error upon NewQueryExecutor")
   400  
   401  	// verify history query returns the expected results in the orderer of block256, 255, 254 .... 1.
   402  	expectedHistoryResults := make([]string, 0)
   403  	for i := 256; i >= 1; i-- {
   404  		expectedHistoryResults = append(expectedHistoryResults, fmt.Sprintf("value%d", i))
   405  	}
   406  	testutilVerifyResults(t, qhistory, "ns1", "key", expectedHistoryResults)
   407  }
   408  
   409  func TestName(t *testing.T) {
   410  	env := newTestHistoryEnv(t)
   411  	defer env.cleanup()
   412  	assert.Equal(t, "history", env.testHistoryDB.Name())
   413  }
   414  
   415  // TestHistoryWithKVWriteOfNilValue - See FAB-18386 for details
   416  func TestHistoryWithKVWriteOfNilValue(t *testing.T) {
   417  	env := newTestHistoryEnv(t)
   418  	defer env.cleanup()
   419  	provider := env.testBlockStorageEnv.provider
   420  	store, err := provider.Open("ledger1")
   421  	require.NoError(t, err)
   422  	defer store.Shutdown()
   423  
   424  	bg, gb := testutil.NewBlockGenerator(t, "ledger1", false)
   425  
   426  	kvRWSet := &kvrwset.KVRWSet{
   427  		Writes: []*kvrwset.KVWrite{
   428  			// explicitly set IsDelete to false while the value to nil. As this will never be generated by simulation
   429  			{Key: "key1", IsDelete: false, Value: nil},
   430  		},
   431  	}
   432  	kvRWsetBytes, err := proto.Marshal(kvRWSet)
   433  	require.NoError(t, err)
   434  
   435  	txRWSet := &rwset.TxReadWriteSet{
   436  		NsRwset: []*rwset.NsReadWriteSet{
   437  			{
   438  				Namespace: "ns1",
   439  				Rwset:     kvRWsetBytes,
   440  			},
   441  		},
   442  	}
   443  
   444  	txRWSetBytes, err := proto.Marshal(txRWSet)
   445  	require.NoError(t, err)
   446  
   447  	block1 := bg.NextBlockWithTxid([][]byte{txRWSetBytes}, []string{"txid1"})
   448  
   449  	historydb, err := env.testHistoryDBProvider.GetDBHandle("ledger1")
   450  	require.NoError(t, err)
   451  	require.NoError(t, store.AddBlock(gb))
   452  	require.NoError(t, historydb.Commit(gb))
   453  	require.NoError(t, store.AddBlock(block1))
   454  	require.NoError(t, historydb.Commit(block1))
   455  
   456  	historydbQE, err := historydb.NewQueryExecutor(store)
   457  	require.NoError(t, err)
   458  	itr, err := historydbQE.GetHistoryForKey("ns1", "key1")
   459  	require.NoError(t, err)
   460  	kmod, err := itr.Next()
   461  	require.NoError(t, err)
   462  	keyModification := kmod.(*queryresult.KeyModification)
   463  	// despite IsDelete set to "false" in the write-set, historydb results should set this to "true"
   464  	require.True(t, keyModification.IsDelete)
   465  
   466  	kmod, err = itr.Next()
   467  	require.NoError(t, err)
   468  	require.Nil(t, kmod)
   469  }
   470  
   471  // verify history results
   472  func testutilVerifyResults(t *testing.T, hqe ledger.HistoryQueryExecutor, ns, key string, expectedVals []string) {
   473  	itr, err := hqe.GetHistoryForKey(ns, key)
   474  	assert.NoError(t, err, "Error upon GetHistoryForKey()")
   475  	retrievedVals := []string{}
   476  	for {
   477  		kmod, _ := itr.Next()
   478  		if kmod == nil {
   479  			break
   480  		}
   481  		txid := kmod.(*queryresult.KeyModification).TxId
   482  		retrievedValue := string(kmod.(*queryresult.KeyModification).Value)
   483  		retrievedVals = append(retrievedVals, retrievedValue)
   484  		t.Logf("Retrieved history record at TxId=%s with value %s", txid, retrievedValue)
   485  	}
   486  	assert.Equal(t, expectedVals, retrievedVals)
   487  }
   488  
   489  // testutilCheckKeyNotInRange verifies that a (false) key is not returned in range query when searching for the desired key
   490  func testutilCheckKeyNotInRange(t *testing.T, hqe ledger.HistoryQueryExecutor, ns, desiredKey, falseKey string) {
   491  	itr, err := hqe.GetHistoryForKey(ns, desiredKey)
   492  	assert.NoError(t, err, "Error upon GetHistoryForKey()")
   493  	scanner := itr.(*historyScanner)
   494  	rangeScanKeys := constructRangeScan(ns, falseKey)
   495  	for {
   496  		if !scanner.dbItr.Next() {
   497  			break
   498  		}
   499  		historyKey := scanner.dbItr.Key()
   500  		if bytes.Contains(historyKey, rangeScanKeys.startKey) {
   501  			assert.Failf(t, "false key %s should not be returned in range query for key %s", falseKey, desiredKey)
   502  		}
   503  	}
   504  }