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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privacyenabledstate
     8  
     9  import (
    10  	"crypto/rand"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"io"
    14  	"testing"
    15  
    16  	"github.com/hyperledger/fabric-protos-go/peer"
    17  	"github.com/osdi23p228/fabric/common/ledger/testutil"
    18  	"github.com/osdi23p228/fabric/core/common/ccprovider"
    19  	"github.com/osdi23p228/fabric/core/ledger/cceventmgmt"
    20  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    21  	testmock "github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate/mock"
    22  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    23  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
    24  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb"
    25  	"github.com/osdi23p228/fabric/core/ledger/mock"
    26  	"github.com/osdi23p228/fabric/core/ledger/util"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestHealthCheckRegister(t *testing.T) {
    31  	fakeHealthCheckRegistry := &mock.HealthCheckRegistry{}
    32  	dbProvider := &DBProvider{
    33  		VersionedDBProvider: &stateleveldb.VersionedDBProvider{},
    34  		HealthCheckRegistry: fakeHealthCheckRegistry,
    35  	}
    36  
    37  	err := dbProvider.RegisterHealthChecker()
    38  	require.NoError(t, err)
    39  	require.Equal(t, 0, fakeHealthCheckRegistry.RegisterCheckerCallCount())
    40  
    41  	dbProvider.VersionedDBProvider = &statecouchdb.VersionedDBProvider{}
    42  	err = dbProvider.RegisterHealthChecker()
    43  	require.NoError(t, err)
    44  	require.Equal(t, 1, fakeHealthCheckRegistry.RegisterCheckerCallCount())
    45  
    46  	arg1, arg2 := fakeHealthCheckRegistry.RegisterCheckerArgsForCall(0)
    47  	require.Equal(t, "couchdb", arg1)
    48  	require.NotNil(t, arg2)
    49  }
    50  
    51  func TestGetIndexInfo(t *testing.T) {
    52  	chaincodeIndexPath := "META-INF/statedb/couchdb/indexes/indexColorSortName.json"
    53  	actualIndexInfo := getIndexInfo(chaincodeIndexPath)
    54  	expectedIndexInfo := &indexInfo{
    55  		hasIndexForChaincode:  true,
    56  		hasIndexForCollection: false,
    57  		collectionName:        "",
    58  	}
    59  	require.Equal(t, expectedIndexInfo, actualIndexInfo)
    60  
    61  	collectionIndexPath := "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json"
    62  	actualIndexInfo = getIndexInfo(collectionIndexPath)
    63  	expectedIndexInfo = &indexInfo{
    64  		hasIndexForChaincode:  false,
    65  		hasIndexForCollection: true,
    66  		collectionName:        "collectionMarbles",
    67  	}
    68  	require.Equal(t, expectedIndexInfo, actualIndexInfo)
    69  
    70  	incorrectChaincodeIndexPath := "META-INF/statedb/couchdb/indexColorSortName.json"
    71  	actualIndexInfo = getIndexInfo(incorrectChaincodeIndexPath)
    72  	expectedIndexInfo = &indexInfo{
    73  		hasIndexForChaincode:  false,
    74  		hasIndexForCollection: false,
    75  		collectionName:        "",
    76  	}
    77  	require.Equal(t, expectedIndexInfo, actualIndexInfo)
    78  
    79  	incorrectCollectionIndexPath := "META-INF/statedb/couchdb/collections/indexes/indexCollMarbles.json"
    80  	actualIndexInfo = getIndexInfo(incorrectCollectionIndexPath)
    81  	require.Equal(t, expectedIndexInfo, actualIndexInfo)
    82  
    83  	incorrectIndexPath := "META-INF/statedb/"
    84  	actualIndexInfo = getIndexInfo(incorrectIndexPath)
    85  	require.Equal(t, expectedIndexInfo, actualIndexInfo)
    86  }
    87  
    88  func TestDB(t *testing.T) {
    89  	for _, env := range testEnvs {
    90  		t.Run(env.GetName(), func(t *testing.T) {
    91  			testDB(t, env)
    92  		})
    93  	}
    94  }
    95  
    96  func testDB(t *testing.T, env TestEnv) {
    97  	env.Init(t)
    98  	defer env.Cleanup()
    99  	db := env.GetDBHandle(generateLedgerID(t))
   100  
   101  	updates := NewUpdateBatch()
   102  
   103  	updates.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   104  	updates.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 2))
   105  	updates.PubUpdates.Put("ns2", "key3", []byte("value3"), version.NewHeight(1, 3))
   106  
   107  	putPvtUpdates(t, updates, "ns1", "coll1", "key1", []byte("pvt_value1"), version.NewHeight(1, 4))
   108  	putPvtUpdates(t, updates, "ns1", "coll1", "key2", []byte("pvt_value2"), version.NewHeight(1, 5))
   109  	putPvtUpdates(t, updates, "ns2", "coll1", "key3", []byte("pvt_value3"), version.NewHeight(1, 6))
   110  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 6))
   111  	bulkOptimizable, ok := db.VersionedDB.(statedb.BulkOptimizable)
   112  	if ok {
   113  		bulkOptimizable.ClearCachedVersions()
   114  	}
   115  
   116  	vv, err := db.GetState("ns1", "key1")
   117  	require.NoError(t, err)
   118  	require.Equal(t, &statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)}, vv)
   119  
   120  	vv, err = db.GetPrivateData("ns1", "coll1", "key1")
   121  	require.NoError(t, err)
   122  	require.Equal(t, &statedb.VersionedValue{Value: []byte("pvt_value1"), Version: version.NewHeight(1, 4)}, vv)
   123  
   124  	vv, err = db.GetPrivateDataHash("ns1", "coll1", "key1")
   125  	require.NoError(t, err)
   126  	require.Equal(t, &statedb.VersionedValue{Value: util.ComputeStringHash("pvt_value1"), Version: version.NewHeight(1, 4)}, vv)
   127  
   128  	vv, err = db.GetValueHash("ns1", "coll1", util.ComputeStringHash("key1"))
   129  	require.NoError(t, err)
   130  	require.Equal(t, &statedb.VersionedValue{Value: util.ComputeStringHash("pvt_value1"), Version: version.NewHeight(1, 4)}, vv)
   131  
   132  	committedVersion, err := db.GetKeyHashVersion("ns1", "coll1", util.ComputeStringHash("key1"))
   133  	require.NoError(t, err)
   134  	require.Equal(t, version.NewHeight(1, 4), committedVersion)
   135  
   136  	updates = NewUpdateBatch()
   137  	updates.PubUpdates.Delete("ns1", "key1", version.NewHeight(2, 7))
   138  	deletePvtUpdates(t, updates, "ns1", "coll1", "key1", version.NewHeight(2, 7))
   139  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 7))
   140  
   141  	vv, err = db.GetState("ns1", "key1")
   142  	require.NoError(t, err)
   143  	require.Nil(t, vv)
   144  
   145  	vv, err = db.GetPrivateData("ns1", "coll1", "key1")
   146  	require.NoError(t, err)
   147  	require.Nil(t, vv)
   148  
   149  	vv, err = db.GetValueHash("ns1", "coll1", util.ComputeStringHash("key1"))
   150  	require.NoError(t, err)
   151  	require.Nil(t, vv)
   152  }
   153  
   154  func TestGetStateMultipleKeys(t *testing.T) {
   155  	for _, env := range testEnvs {
   156  		t.Run(env.GetName(), func(t *testing.T) {
   157  			testGetStateMultipleKeys(t, env)
   158  		})
   159  	}
   160  }
   161  
   162  func testGetStateMultipleKeys(t *testing.T, env TestEnv) {
   163  	env.Init(t)
   164  	defer env.Cleanup()
   165  	db := env.GetDBHandle(generateLedgerID(t))
   166  
   167  	updates := NewUpdateBatch()
   168  
   169  	updates.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   170  	updates.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 2))
   171  	updates.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 3))
   172  
   173  	putPvtUpdates(t, updates, "ns1", "coll1", "key1", []byte("pvt_value1"), version.NewHeight(1, 4))
   174  	putPvtUpdates(t, updates, "ns1", "coll1", "key2", []byte("pvt_value2"), version.NewHeight(1, 5))
   175  	putPvtUpdates(t, updates, "ns1", "coll1", "key3", []byte("pvt_value3"), version.NewHeight(1, 6))
   176  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 6))
   177  
   178  	versionedVals, err := db.GetStateMultipleKeys("ns1", []string{"key1", "key3"})
   179  	require.NoError(t, err)
   180  	require.Equal(t,
   181  		[]*statedb.VersionedValue{
   182  			{Value: []byte("value1"), Version: version.NewHeight(1, 1)},
   183  			{Value: []byte("value3"), Version: version.NewHeight(1, 3)},
   184  		},
   185  		versionedVals)
   186  
   187  	pvtVersionedVals, err := db.GetPrivateDataMultipleKeys("ns1", "coll1", []string{"key1", "key3"})
   188  	require.NoError(t, err)
   189  	require.Equal(t,
   190  		[]*statedb.VersionedValue{
   191  			{Value: []byte("pvt_value1"), Version: version.NewHeight(1, 4)},
   192  			{Value: []byte("pvt_value3"), Version: version.NewHeight(1, 6)},
   193  		},
   194  		pvtVersionedVals)
   195  }
   196  
   197  func TestGetStateRangeScanIterator(t *testing.T) {
   198  	for _, env := range testEnvs {
   199  		t.Run(env.GetName(), func(t *testing.T) {
   200  			testGetStateRangeScanIterator(t, env)
   201  		})
   202  	}
   203  }
   204  
   205  func testGetStateRangeScanIterator(t *testing.T, env TestEnv) {
   206  	env.Init(t)
   207  	defer env.Cleanup()
   208  	db := env.GetDBHandle(generateLedgerID(t))
   209  
   210  	updates := NewUpdateBatch()
   211  
   212  	updates.PubUpdates.Put("ns1", "key1", []byte("value1"), version.NewHeight(1, 1))
   213  	updates.PubUpdates.Put("ns1", "key2", []byte("value2"), version.NewHeight(1, 2))
   214  	updates.PubUpdates.Put("ns1", "key3", []byte("value3"), version.NewHeight(1, 3))
   215  	updates.PubUpdates.Put("ns1", "key4", []byte("value4"), version.NewHeight(1, 4))
   216  	updates.PubUpdates.Put("ns2", "key5", []byte("value5"), version.NewHeight(1, 5))
   217  	updates.PubUpdates.Put("ns2", "key6", []byte("value6"), version.NewHeight(1, 6))
   218  	updates.PubUpdates.Put("ns3", "key7", []byte("value7"), version.NewHeight(1, 7))
   219  
   220  	putPvtUpdates(t, updates, "ns1", "coll1", "key1", []byte("pvt_value1"), version.NewHeight(1, 1))
   221  	putPvtUpdates(t, updates, "ns1", "coll1", "key2", []byte("pvt_value2"), version.NewHeight(1, 2))
   222  	putPvtUpdates(t, updates, "ns1", "coll1", "key3", []byte("pvt_value3"), version.NewHeight(1, 3))
   223  	putPvtUpdates(t, updates, "ns1", "coll1", "key4", []byte("pvt_value4"), version.NewHeight(1, 4))
   224  	putPvtUpdates(t, updates, "ns2", "coll1", "key5", []byte("pvt_value5"), version.NewHeight(1, 5))
   225  	putPvtUpdates(t, updates, "ns2", "coll1", "key6", []byte("pvt_value6"), version.NewHeight(1, 6))
   226  	putPvtUpdates(t, updates, "ns3", "coll1", "key7", []byte("pvt_value7"), version.NewHeight(1, 7))
   227  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 7))
   228  
   229  	itr1, _ := db.GetStateRangeScanIterator("ns1", "key1", "")
   230  	testItr(t, itr1, []string{"key1", "key2", "key3", "key4"})
   231  
   232  	itr2, _ := db.GetStateRangeScanIterator("ns1", "key2", "key3")
   233  	testItr(t, itr2, []string{"key2"})
   234  
   235  	itr3, _ := db.GetStateRangeScanIterator("ns1", "", "")
   236  	testItr(t, itr3, []string{"key1", "key2", "key3", "key4"})
   237  
   238  	itr4, _ := db.GetStateRangeScanIterator("ns2", "", "")
   239  	testItr(t, itr4, []string{"key5", "key6"})
   240  
   241  	pvtItr1, _ := db.GetPrivateDataRangeScanIterator("ns1", "coll1", "key1", "")
   242  	testItr(t, pvtItr1, []string{"key1", "key2", "key3", "key4"})
   243  
   244  	pvtItr2, _ := db.GetPrivateDataRangeScanIterator("ns1", "coll1", "key2", "key3")
   245  	testItr(t, pvtItr2, []string{"key2"})
   246  
   247  	pvtItr3, _ := db.GetPrivateDataRangeScanIterator("ns1", "coll1", "", "")
   248  	testItr(t, pvtItr3, []string{"key1", "key2", "key3", "key4"})
   249  
   250  	pvtItr4, _ := db.GetPrivateDataRangeScanIterator("ns2", "coll1", "", "")
   251  	testItr(t, pvtItr4, []string{"key5", "key6"})
   252  }
   253  
   254  func TestQueryOnCouchDB(t *testing.T) {
   255  	for _, env := range testEnvs {
   256  		_, ok := env.(*CouchDBTestEnv)
   257  		if !ok {
   258  			continue
   259  		}
   260  		t.Run(env.GetName(), func(t *testing.T) {
   261  			testQueryOnCouchDB(t, env)
   262  		})
   263  	}
   264  }
   265  
   266  func testQueryOnCouchDB(t *testing.T, env TestEnv) {
   267  	env.Init(t)
   268  	defer env.Cleanup()
   269  	db := env.GetDBHandle(generateLedgerID(t))
   270  	updates := NewUpdateBatch()
   271  
   272  	jsonValues := []string{
   273  		`{"asset_name": "marble1", "color": "blue", "size": 1, "owner": "tom"}`,
   274  		`{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}`,
   275  		`{"asset_name": "marble3","color": "blue","size": 3,"owner": "fred"}`,
   276  		`{"asset_name": "marble4","color": "blue","size": 4,"owner": "martha"}`,
   277  		`{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}`,
   278  		`{"asset_name": "marble6","color": "blue","size": 6,"owner": "elaine"}`,
   279  		`{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}`,
   280  		`{"asset_name": "marble8","color": "blue","size": 8,"owner": "elaine"}`,
   281  		`{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}`,
   282  		`{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}`,
   283  		`{"asset_name": "marble11","color": "cyan","size": 1000007,"owner": "joe"}`,
   284  	}
   285  
   286  	for i, jsonValue := range jsonValues {
   287  		updates.PubUpdates.Put("ns1", testKey(i), []byte(jsonValue), version.NewHeight(1, uint64(i)))
   288  		updates.PubUpdates.Put("ns2", testKey(i), []byte(jsonValue), version.NewHeight(1, uint64(i)))
   289  		putPvtUpdates(t, updates, "ns1", "coll1", testKey(i), []byte(jsonValue), version.NewHeight(1, uint64(i)))
   290  		putPvtUpdates(t, updates, "ns2", "coll1", testKey(i), []byte(jsonValue), version.NewHeight(1, uint64(i)))
   291  	}
   292  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(1, 11))
   293  
   294  	// query for owner=jerry, use namespace "ns1"
   295  	itr, err := db.ExecuteQuery("ns1", `{"selector":{"owner":"jerry"}}`)
   296  	require.NoError(t, err)
   297  	testQueryItr(t, itr, []string{testKey(1)}, []string{"jerry"})
   298  
   299  	// query for owner=jerry, use namespace "ns2"
   300  	itr, err = db.ExecuteQuery("ns2", `{"selector":{"owner":"jerry"}}`)
   301  	require.NoError(t, err)
   302  	testQueryItr(t, itr, []string{testKey(1)}, []string{"jerry"})
   303  
   304  	// query for pvt data owner=jerry, use namespace "ns1"
   305  	itr, err = db.ExecuteQueryOnPrivateData("ns1", "coll1", `{"selector":{"owner":"jerry"}}`)
   306  	require.NoError(t, err)
   307  	testQueryItr(t, itr, []string{testKey(1)}, []string{"jerry"})
   308  
   309  	// query for pvt data owner=jerry, use namespace "ns2"
   310  	itr, err = db.ExecuteQueryOnPrivateData("ns2", "coll1", `{"selector":{"owner":"jerry"}}`)
   311  	require.NoError(t, err)
   312  	testQueryItr(t, itr, []string{testKey(1)}, []string{"jerry"})
   313  
   314  	// query using bad query string
   315  	_, err = db.ExecuteQueryOnPrivateData("ns1", "coll1", "this is an invalid query string")
   316  	require.Error(t, err, "Should have received an error for invalid query string")
   317  
   318  	// query returns 0 records
   319  	itr, err = db.ExecuteQueryOnPrivateData("ns1", "coll1", `{"selector":{"owner":"not_a_valid_name"}}`)
   320  	require.NoError(t, err)
   321  	testQueryItr(t, itr, []string{}, []string{})
   322  
   323  	// query with embedded implicit "AND" and explicit "OR", namespace "ns1"
   324  	itr, err = db.ExecuteQueryOnPrivateData("ns1", "coll1", `{"selector":{"color":"green","$or":[{"owner":"fred"},{"owner":"mary"}]}}`)
   325  	require.NoError(t, err)
   326  	testQueryItr(t, itr, []string{testKey(8), testKey(9)}, []string{"green"}, []string{"green"})
   327  
   328  	// query with integer with digit-count equals 7 and response received is also received
   329  	// with same digit-count and there is no float transformation
   330  	itr, err = db.ExecuteQueryOnPrivateData("ns2", "coll1", `{"selector":{"$and":[{"size":{"$eq": 1000007}}]}}`)
   331  	require.NoError(t, err)
   332  	testQueryItr(t, itr, []string{testKey(10)}, []string{"joe", "1000007"})
   333  }
   334  
   335  func TestLongDBNameOnCouchDB(t *testing.T) {
   336  	for _, env := range testEnvs {
   337  		_, ok := env.(*CouchDBTestEnv)
   338  		if !ok {
   339  			continue
   340  		}
   341  		t.Run(env.GetName(), func(t *testing.T) {
   342  			testLongDBNameOnCouchDB(t, env)
   343  		})
   344  	}
   345  }
   346  
   347  func testLongDBNameOnCouchDB(t *testing.T, env TestEnv) {
   348  	env.Init(t)
   349  	defer env.Cleanup()
   350  
   351  	// Creates metadataDB (i.e., chainDB)
   352  	// Allowed pattern for chainName: [a-z][a-z0-9.-]
   353  	db := env.GetDBHandle("w1coaii9ck3l8red6a5cf3rwbe1b4wvbzcrrfl7samu7px8b9gf-4hft7wrgdmzzjj9ure4cbffucaj78nbj9ej.kvl3bus1iq1qir9xlhb8a1wipuksgs3g621elzy1prr658087exwrhp-y4j55o9cld242v--oeh3br1g7m8d6l8jobn.y42cgjt1.u1ik8qxnv4ohh9kr2w2zc8hqir5u4ev23s7jygrg....s7.ohp-5bcxari8nji")
   354  
   355  	updates := NewUpdateBatch()
   356  
   357  	// Allowed pattern for namespace and collection: [a-zA-Z0-9_-]
   358  	ns := "wMCnSXiV9YoIqNQyNvFVTdM8XnUtvrOFFIWsKelmP5NEszmNLl8YhtOKbFu3P_NgwgsYF8PsfwjYCD8f1XRpANQLoErDHwLlweryqXeJ6vzT2x0pS_GwSx0m6tBI0zOmHQOq_2De8A87x6zUOPwufC2T6dkidFxiuq8Sey2-5vUo_iNKCij3WTeCnKx78PUIg_U1gp4_0KTvYVtRBRvH0kz5usizBxPaiFu3TPhB9XLviScvdUVSbSYJ0Z"
   359  	coll := "vWjtfSTXVK8WJus5s6zWoMIciXd7qHRZIusF9SkOS6m8XuHCiJDE9cCRuVerq22Na8qBL2ywDGFpVMIuzfyEXLjeJb0mMuH4cwewT6r1INOTOSYwrikwOLlT_fl0V1L7IQEwUBB8WCvRqSdj6j5-E5aGul_pv_0UeCdwWiyA_GrZmP7ocLzfj2vP8btigrajqdH-irLO2ydEjQUAvf8fiuxru9la402KmKRy457GgI98UHoUdqV3f3FCdR"
   360  
   361  	updates.PubUpdates.Put(ns, "key1", []byte("value1"), version.NewHeight(1, 1))
   362  	updates.PvtUpdates.Put(ns, coll, "key1", []byte("pvt_value"), version.NewHeight(1, 2))
   363  	updates.HashUpdates.Put(ns, coll, util.ComputeStringHash("key1"), util.ComputeHash([]byte("pvt_value")), version.NewHeight(1, 2))
   364  
   365  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 6))
   366  
   367  	vv, err := db.GetState(ns, "key1")
   368  	require.NoError(t, err)
   369  	require.Equal(t, &statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)}, vv)
   370  
   371  	vv, err = db.GetPrivateData(ns, coll, "key1")
   372  	require.NoError(t, err)
   373  	require.Equal(t, &statedb.VersionedValue{Value: []byte("pvt_value"), Version: version.NewHeight(1, 2)}, vv)
   374  }
   375  
   376  func testItr(t *testing.T, itr statedb.ResultsIterator, expectedKeys []string) {
   377  	defer itr.Close()
   378  	for _, expectedKey := range expectedKeys {
   379  		queryResult, _ := itr.Next()
   380  		vkv := queryResult.(*statedb.VersionedKV)
   381  		key := vkv.Key
   382  		require.Equal(t, expectedKey, key)
   383  	}
   384  	last, err := itr.Next()
   385  	require.NoError(t, err)
   386  	require.Nil(t, last)
   387  }
   388  
   389  func testQueryItr(t *testing.T, itr statedb.ResultsIterator, expectedKeys []string, expectedValStrs ...[]string) {
   390  	defer itr.Close()
   391  	for i, expectedKey := range expectedKeys {
   392  		queryResult, _ := itr.Next()
   393  		vkv := queryResult.(*statedb.VersionedKV)
   394  		key := vkv.Key
   395  		valStr := string(vkv.Value)
   396  		require.Equal(t, expectedKey, key)
   397  		for _, expectedValStr := range expectedValStrs[i] {
   398  			require.Contains(t, valStr, expectedValStr)
   399  		}
   400  	}
   401  	last, err := itr.Next()
   402  	require.NoError(t, err)
   403  	require.Nil(t, last)
   404  }
   405  
   406  func testKey(i int) string {
   407  	return fmt.Sprintf("key%d", i)
   408  }
   409  
   410  func TestHandleChainCodeDeployOnCouchDB(t *testing.T) {
   411  	for _, env := range testEnvs {
   412  		_, ok := env.(*CouchDBTestEnv)
   413  		if !ok {
   414  			continue
   415  		}
   416  		t.Run(env.GetName(), func(t *testing.T) {
   417  			testHandleChainCodeDeploy(t, env)
   418  		})
   419  	}
   420  }
   421  
   422  func createCollectionConfig(collectionName string) *peer.CollectionConfig {
   423  	return &peer.CollectionConfig{
   424  		Payload: &peer.CollectionConfig_StaticCollectionConfig{
   425  			StaticCollectionConfig: &peer.StaticCollectionConfig{
   426  				Name:              collectionName,
   427  				MemberOrgsPolicy:  nil,
   428  				RequiredPeerCount: 0,
   429  				MaximumPeerCount:  0,
   430  				BlockToLive:       0,
   431  			},
   432  		},
   433  	}
   434  }
   435  
   436  func testHandleChainCodeDeploy(t *testing.T, env TestEnv) {
   437  	env.Init(t)
   438  	defer env.Cleanup()
   439  	db := env.GetDBHandle(generateLedgerID(t))
   440  
   441  	coll1 := createCollectionConfig("collectionMarbles")
   442  	ccp := &peer.CollectionConfigPackage{Config: []*peer.CollectionConfig{coll1}}
   443  	chaincodeDef := &cceventmgmt.ChaincodeDefinition{Name: "ns1", Hash: nil, Version: "", CollectionConfigs: ccp}
   444  
   445  	// Test indexes for side databases
   446  	dbArtifactsTarBytes := testutil.CreateTarBytesForTest(
   447  		[]*testutil.TarFileEntry{
   448  			{Name: "META-INF/statedb/couchdb/indexes/indexColorSortName.json", Body: `{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexColorSortName","name":"indexColorSortName","type":"json"}`},
   449  			{Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`},
   450  			{Name: "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json", Body: `{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`},
   451  			{Name: "META-INF/statedb/couchdb/collections/collectionMarblesPrivateDetails/indexes/indexCollPrivDetails.json", Body: `{"index":{"fields":["docType","price"]},"ddoc":"indexPrivateDetails", "name":"indexPrivateDetails","type":"json"}`},
   452  		},
   453  	)
   454  
   455  	// Test the retrieveIndexArtifacts method
   456  	fileEntries, err := ccprovider.ExtractFileEntries(dbArtifactsTarBytes, "couchdb")
   457  	require.NoError(t, err)
   458  
   459  	// There should be 3 entries
   460  	require.Len(t, fileEntries, 3)
   461  
   462  	// There should be 2 entries for main
   463  	require.Len(t, fileEntries["META-INF/statedb/couchdb/indexes"], 2)
   464  
   465  	// There should be 1 entry for collectionMarbles
   466  	require.Len(t, fileEntries["META-INF/statedb/couchdb/collections/collectionMarbles/indexes"], 1)
   467  
   468  	// Verify the content of the array item
   469  	expectedJSON := []byte(`{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`)
   470  	actualJSON := fileEntries["META-INF/statedb/couchdb/collections/collectionMarbles/indexes"][0].FileContent
   471  	require.Equal(t, expectedJSON, actualJSON)
   472  
   473  	// The collection config is added to the chaincodeDef but missing collectionMarblesPrivateDetails.
   474  	// Hence, the index on collectionMarblesPrivateDetails cannot be created
   475  	err = db.HandleChaincodeDeploy(chaincodeDef, dbArtifactsTarBytes)
   476  	require.NoError(t, err)
   477  
   478  	coll2 := createCollectionConfig("collectionMarblesPrivateDetails")
   479  	ccp = &peer.CollectionConfigPackage{Config: []*peer.CollectionConfig{coll1, coll2}}
   480  	chaincodeDef = &cceventmgmt.ChaincodeDefinition{Name: "ns1", Hash: nil, Version: "", CollectionConfigs: ccp}
   481  
   482  	// The collection config is added to the chaincodeDef and it contains all collections
   483  	// including collectionMarblesPrivateDetails which was missing earlier.
   484  	// Hence, the existing indexes must be updated and the new index must be created for
   485  	// collectionMarblesPrivateDetails
   486  	err = db.HandleChaincodeDeploy(chaincodeDef, dbArtifactsTarBytes)
   487  	require.NoError(t, err)
   488  
   489  	chaincodeDef = &cceventmgmt.ChaincodeDefinition{Name: "ns1", Hash: nil, Version: "", CollectionConfigs: nil}
   490  
   491  	// The collection config is not added to the chaincodeDef. In this case, the index creation
   492  	// process reads the collection config from state db. However, the state db does not contain
   493  	// any collection config for this chaincode. Hence, index creation/update on all collections
   494  	// should fail
   495  	err = db.HandleChaincodeDeploy(chaincodeDef, dbArtifactsTarBytes)
   496  	require.NoError(t, err)
   497  
   498  	//Test HandleChaincodeDefinition with a nil tar file
   499  	err = db.HandleChaincodeDeploy(chaincodeDef, nil)
   500  	require.NoError(t, err)
   501  
   502  	//Test HandleChaincodeDefinition with a bad tar file
   503  	err = db.HandleChaincodeDeploy(chaincodeDef, []byte(`This is a really bad tar file`))
   504  	require.NoError(t, err, "Error should not have been thrown for a bad tar file")
   505  
   506  	//Test HandleChaincodeDefinition with a nil chaincodeDef
   507  	err = db.HandleChaincodeDeploy(nil, dbArtifactsTarBytes)
   508  	require.Error(t, err, "Error should have been thrown for a nil chaincodeDefinition")
   509  
   510  	// Create a tar file for test with 2 index definitions - one of them being errorneous
   511  	badSyntaxFileContent := `{"index":{"fields": This is a bad json}`
   512  	dbArtifactsTarBytes = testutil.CreateTarBytesForTest(
   513  		[]*testutil.TarFileEntry{
   514  			{Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`},
   515  			{Name: "META-INF/statedb/couchdb/indexes/badSyntax.json", Body: badSyntaxFileContent},
   516  		},
   517  	)
   518  
   519  	// Test the retrieveIndexArtifacts method
   520  	fileEntries, err = ccprovider.ExtractFileEntries(dbArtifactsTarBytes, "couchdb")
   521  	require.NoError(t, err)
   522  
   523  	// There should be 1 entry
   524  	require.Len(t, fileEntries, 1)
   525  
   526  	err = db.HandleChaincodeDeploy(chaincodeDef, dbArtifactsTarBytes)
   527  	require.NoError(t, err)
   528  
   529  }
   530  
   531  func TestMetadataRetrieval(t *testing.T) {
   532  	for _, env := range testEnvs {
   533  		t.Run(env.GetName(), func(t *testing.T) {
   534  			testMetadataRetrieval(t, env)
   535  		})
   536  	}
   537  }
   538  
   539  func testMetadataRetrieval(t *testing.T, env TestEnv) {
   540  	env.Init(t)
   541  	defer env.Cleanup()
   542  	db := env.GetDBHandle(generateLedgerID(t))
   543  
   544  	updates := NewUpdateBatch()
   545  	updates.PubUpdates.PutValAndMetadata("ns1", "key1", []byte("value1"), []byte("metadata1"), version.NewHeight(1, 1))
   546  	updates.PubUpdates.PutValAndMetadata("ns1", "key2", []byte("value2"), nil, version.NewHeight(1, 2))
   547  	updates.PubUpdates.PutValAndMetadata("ns2", "key3", []byte("value3"), nil, version.NewHeight(1, 3))
   548  
   549  	putPvtUpdatesWithMetadata(t, updates, "ns1", "coll1", "key1", []byte("pvt_value1"), []byte("metadata1"), version.NewHeight(1, 4))
   550  	putPvtUpdatesWithMetadata(t, updates, "ns1", "coll1", "key2", []byte("pvt_value2"), nil, version.NewHeight(1, 5))
   551  	putPvtUpdatesWithMetadata(t, updates, "ns2", "coll1", "key3", []byte("pvt_value3"), nil, version.NewHeight(1, 6))
   552  	db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 6))
   553  
   554  	vm, _ := db.GetStateMetadata("ns1", "key1")
   555  	require.Equal(t, vm, []byte("metadata1"))
   556  	vm, _ = db.GetStateMetadata("ns1", "key2")
   557  	require.Nil(t, vm)
   558  	vm, _ = db.GetStateMetadata("ns2", "key3")
   559  	require.Nil(t, vm)
   560  
   561  	vm, _ = db.GetPrivateDataMetadataByHash("ns1", "coll1", util.ComputeStringHash("key1"))
   562  	require.Equal(t, vm, []byte("metadata1"))
   563  	vm, _ = db.GetPrivateDataMetadataByHash("ns1", "coll1", util.ComputeStringHash("key2"))
   564  	require.Nil(t, vm)
   565  	vm, _ = db.GetPrivateDataMetadataByHash("ns2", "coll1", util.ComputeStringHash("key3"))
   566  	require.Nil(t, vm)
   567  }
   568  
   569  func putPvtUpdates(t *testing.T, updates *UpdateBatch, ns, coll, key string, value []byte, ver *version.Height) {
   570  	updates.PvtUpdates.Put(ns, coll, key, value, ver)
   571  	updates.HashUpdates.Put(ns, coll, util.ComputeStringHash(key), util.ComputeHash(value), ver)
   572  }
   573  
   574  func putPvtUpdatesWithMetadata(t *testing.T, updates *UpdateBatch, ns, coll, key string, value []byte, metadata []byte, ver *version.Height) {
   575  	updates.PvtUpdates.Put(ns, coll, key, value, ver)
   576  	updates.HashUpdates.PutValHashAndMetadata(ns, coll, util.ComputeStringHash(key), util.ComputeHash(value), metadata, ver)
   577  }
   578  
   579  func deletePvtUpdates(t *testing.T, updates *UpdateBatch, ns, coll, key string, ver *version.Height) {
   580  	updates.PvtUpdates.Delete(ns, coll, key, ver)
   581  	updates.HashUpdates.Delete(ns, coll, util.ComputeStringHash(key), ver)
   582  }
   583  
   584  func generateLedgerID(t *testing.T) string {
   585  	bytes := make([]byte, 8)
   586  	_, err := io.ReadFull(rand.Reader, bytes)
   587  	require.NoError(t, err)
   588  	return fmt.Sprintf("x%s", hex.EncodeToString(bytes))
   589  }
   590  
   591  //go:generate counterfeiter -o mock/channelinfo_provider.go -fake-name ChannelInfoProvider . channelInfoProviderWrapper
   592  
   593  // define this interface to break circular dependency
   594  type channelInfoProviderWrapper interface {
   595  	channelInfoProvider
   596  }
   597  
   598  func TestPossibleNamespaces(t *testing.T) {
   599  	namespacesAndCollections := map[string][]string{
   600  		"cc1":        {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP", "collectionA", "collectionB"},
   601  		"cc2":        {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP"},
   602  		"_lifecycle": {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP"},
   603  		"lscc":       {},
   604  		"":           {},
   605  	}
   606  	expectedNamespaces := []string{
   607  		"cc1",
   608  		"cc1$$p_implicit_org_Org1MSP",
   609  		"cc1$$h_implicit_org_Org1MSP",
   610  		"cc1$$p_implicit_org_Org2MSP",
   611  		"cc1$$h_implicit_org_Org2MSP",
   612  		"cc1$$pcollectionA",
   613  		"cc1$$hcollectionA",
   614  		"cc1$$pcollectionB",
   615  		"cc1$$hcollectionB",
   616  		"cc2",
   617  		"cc2$$p_implicit_org_Org1MSP",
   618  		"cc2$$h_implicit_org_Org1MSP",
   619  		"cc2$$p_implicit_org_Org2MSP",
   620  		"cc2$$h_implicit_org_Org2MSP",
   621  		"_lifecycle",
   622  		"_lifecycle$$p_implicit_org_Org1MSP",
   623  		"_lifecycle$$h_implicit_org_Org1MSP",
   624  		"_lifecycle$$p_implicit_org_Org2MSP",
   625  		"_lifecycle$$h_implicit_org_Org2MSP",
   626  		"lscc",
   627  		"",
   628  	}
   629  
   630  	fakeChannelInfoProvider := &testmock.ChannelInfoProvider{}
   631  	fakeChannelInfoProvider.NamespacesAndCollectionsReturns(namespacesAndCollections, nil)
   632  	nsProvider := &namespaceProvider{fakeChannelInfoProvider}
   633  	namespaces, err := nsProvider.PossibleNamespaces(&statecouchdb.VersionedDB{})
   634  	require.NoError(t, err)
   635  	require.ElementsMatch(t, expectedNamespaces, namespaces)
   636  }