github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/confighistory/mgr_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package confighistory
     8  
     9  import (
    10  	"crypto/sha256"
    11  	"fmt"
    12  	"hash"
    13  	"io/ioutil"
    14  	"math"
    15  	"os"
    16  	"path/filepath"
    17  	"testing"
    18  
    19  	"github.com/golang/protobuf/proto"
    20  	"github.com/hyperledger/fabric-protos-go/common"
    21  	"github.com/hyperledger/fabric-protos-go/peer"
    22  	"github.com/osdi23p228/fabric/common/flogging"
    23  	"github.com/osdi23p228/fabric/common/ledger/snapshot"
    24  	"github.com/osdi23p228/fabric/core/ledger"
    25  	"github.com/osdi23p228/fabric/core/ledger/mock"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  var (
    30  	testNewHashFunc = func() (hash.Hash, error) {
    31  		return sha256.New(), nil
    32  	}
    33  )
    34  
    35  func TestMain(m *testing.M) {
    36  	flogging.ActivateSpec("confighistory=debug")
    37  	os.Exit(m.Run())
    38  }
    39  
    40  func TestWithNoCollectionConfig(t *testing.T) {
    41  	dbPath, err := ioutil.TempDir("", "confighistory")
    42  	if err != nil {
    43  		t.Fatalf("Failed to create config history directory: %s", err)
    44  	}
    45  	defer os.RemoveAll(dbPath)
    46  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    47  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
    48  	require.NoError(t, err)
    49  	testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, "chaincode1", nil)
    50  	err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
    51  		LedgerID:           "ledger1",
    52  		CommittingBlockNum: 50},
    53  	)
    54  	require.NoError(t, err)
    55  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
    56  		info: &common.BlockchainInfo{Height: 100},
    57  		qe:   &mock.QueryExecutor{},
    58  	}
    59  	retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
    60  	collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1")
    61  	require.NoError(t, err)
    62  	require.Nil(t, collConfig)
    63  }
    64  
    65  func TestWithEmptyCollectionConfig(t *testing.T) {
    66  	dbPath, err := ioutil.TempDir("", "confighistory")
    67  	if err != nil {
    68  		t.Fatalf("Failed to create config history directory: %s", err)
    69  	}
    70  	defer os.RemoveAll(dbPath)
    71  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    72  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
    73  	require.NoError(t, err)
    74  	testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(
    75  		mockCCInfoProvider,
    76  		"chaincode1",
    77  		&peer.CollectionConfigPackage{},
    78  	)
    79  	err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
    80  		LedgerID:           "ledger1",
    81  		CommittingBlockNum: 50},
    82  	)
    83  	require.NoError(t, err)
    84  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
    85  		info: &common.BlockchainInfo{Height: 100},
    86  		qe:   &mock.QueryExecutor{},
    87  	}
    88  	retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
    89  	collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1")
    90  	require.NoError(t, err)
    91  	require.Nil(t, collConfig)
    92  }
    93  
    94  func TestMgr(t *testing.T) {
    95  	dbPath, err := ioutil.TempDir("", "confighistory")
    96  	if err != nil {
    97  		t.Fatalf("Failed to create config history directory: %s", err)
    98  	}
    99  	defer os.RemoveAll(dbPath)
   100  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
   101  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
   102  	require.NoError(t, err)
   103  	chaincodeName := "chaincode1"
   104  	maxBlockNumberInLedger := uint64(2000)
   105  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
   106  		info: &common.BlockchainInfo{Height: maxBlockNumberInLedger + 1},
   107  		qe:   &mock.QueryExecutor{},
   108  	}
   109  	configCommittingBlockNums := []uint64{5, 10, 15, 100}
   110  	ledgerIds := []string{"ledgerid1", "ledger2"}
   111  
   112  	// Populate collection config versions
   113  	for _, ledgerid := range ledgerIds {
   114  		for _, committingBlockNum := range configCommittingBlockNums {
   115  			// for each ledgerid and commitHeight combination, construct a unique collConfigPackage and induce a stateUpdate
   116  			collConfigPackage := sampleCollectionConfigPackage(ledgerid, committingBlockNum)
   117  			testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, chaincodeName, collConfigPackage)
   118  			mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
   119  				LedgerID:           ledgerid,
   120  				CommittingBlockNum: committingBlockNum},
   121  			)
   122  		}
   123  	}
   124  
   125  	t.Run("test-api-MostRecentCollectionConfigBelow()", func(t *testing.T) {
   126  		// A map that contains entries such that for each of the entries of type <K, V>,
   127  		// we retrieve the 'MostRecentCollectionConfigBelow' for 'K' and the expected value
   128  		// should be configuration committed at 'V'
   129  		m := map[uint64]uint64{math.MaxUint64: 100, 1000: 100, 50: 15, 12: 10, 7: 5}
   130  		for _, ledgerid := range ledgerIds {
   131  			retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever)
   132  			for testHeight, expectedHeight := range m {
   133  				retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(testHeight, chaincodeName)
   134  				require.NoError(t, err)
   135  				expectedConfig := sampleCollectionConfigPackage(ledgerid, expectedHeight)
   136  				require.Equal(t, expectedConfig, retrievedConfig.CollectionConfig)
   137  				require.Equal(t, expectedHeight, retrievedConfig.CommittingBlockNum)
   138  			}
   139  
   140  			retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(5, chaincodeName)
   141  			require.NoError(t, err)
   142  			require.Nil(t, retrievedConfig)
   143  		}
   144  	})
   145  
   146  	t.Run("test-api-CollectionConfigAt()", func(t *testing.T) {
   147  		for _, ledgerid := range ledgerIds {
   148  			retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever)
   149  			for _, commitHeight := range configCommittingBlockNums {
   150  				retrievedConfig, err := retriever.CollectionConfigAt(commitHeight, chaincodeName)
   151  				require.NoError(t, err)
   152  				expectedConfig := sampleCollectionConfigPackage(ledgerid, commitHeight)
   153  				require.Equal(t, expectedConfig, retrievedConfig.CollectionConfig)
   154  				require.Equal(t, commitHeight, retrievedConfig.CommittingBlockNum)
   155  			}
   156  		}
   157  	})
   158  
   159  	t.Run("test-api-CollectionConfigAt-BoundaryCases()", func(t *testing.T) {
   160  		retriever := mgr.GetRetriever("ledgerid1", dummyLedgerInfoRetriever)
   161  		retrievedConfig, err := retriever.CollectionConfigAt(4, chaincodeName)
   162  		require.NoError(t, err)
   163  		require.Nil(t, retrievedConfig)
   164  
   165  		_, err = retriever.CollectionConfigAt(5000, chaincodeName)
   166  		typedErr, ok := err.(*ledger.ErrCollectionConfigNotYetAvailable)
   167  		require.True(t, ok)
   168  		require.Equal(t, maxBlockNumberInLedger, typedErr.MaxBlockNumCommitted)
   169  	})
   170  }
   171  
   172  func TestWithImplicitColls(t *testing.T) {
   173  	dbPath, err := ioutil.TempDir("", "confighistory")
   174  	if err != nil {
   175  		t.Fatalf("Failed to create config history directory: %s", err)
   176  	}
   177  	defer os.RemoveAll(dbPath)
   178  	collConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-coll-1", "Explicit-coll-2"})
   179  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
   180  	mockCCInfoProvider.ImplicitCollectionsReturns(
   181  		[]*peer.StaticCollectionConfig{
   182  			{
   183  				Name: "Implicit-coll-1",
   184  			},
   185  			{
   186  				Name: "Implicit-coll-2",
   187  			},
   188  		},
   189  		nil,
   190  	)
   191  	p, err := newDBProvider(dbPath)
   192  	require.NoError(t, err)
   193  
   194  	mgr := &Mgr{
   195  		ccInfoProvider: mockCCInfoProvider,
   196  		dbProvider:     p,
   197  	}
   198  	dbHandle := mgr.dbProvider.getDB("ledger1")
   199  	batch := dbHandle.newBatch()
   200  	// add explicit collections at height 20
   201  	err = prepareDBBatch(
   202  		batch,
   203  		map[string]*peer.CollectionConfigPackage{
   204  			"chaincode1": collConfigPackage,
   205  		},
   206  		20,
   207  	)
   208  	require.NoError(t, err)
   209  	require.NoError(t, dbHandle.writeBatch(batch, true))
   210  
   211  	onlyImplicitCollections := testutilCreateCollConfigPkg(
   212  		[]string{"Implicit-coll-1", "Implicit-coll-2"},
   213  	)
   214  
   215  	explicitAndImplicitCollections := testutilCreateCollConfigPkg(
   216  		[]string{"Explicit-coll-1", "Explicit-coll-2", "Implicit-coll-1", "Implicit-coll-2"},
   217  	)
   218  
   219  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
   220  		info: &common.BlockchainInfo{Height: 1000},
   221  		qe:   &mock.QueryExecutor{},
   222  	}
   223  
   224  	t.Run("CheckQueryExecutorCalls", func(t *testing.T) {
   225  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   226  		// function MostRecentCollectionConfigBelow calls Done on query executor
   227  		_, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1")
   228  		require.NoError(t, err)
   229  		require.Equal(t, 1, dummyLedgerInfoRetriever.qe.DoneCallCount())
   230  		// function CollectionConfigAt calls Done on query executor
   231  		_, err = retriever.CollectionConfigAt(50, "chaincode1")
   232  		require.NoError(t, err)
   233  		require.Equal(t, 2, dummyLedgerInfoRetriever.qe.DoneCallCount())
   234  	})
   235  
   236  	t.Run("MostRecentCollectionConfigBelow50", func(t *testing.T) {
   237  		// explicit collections added at height 20 should be merged with the implicit collections
   238  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   239  		retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1")
   240  		require.NoError(t, err)
   241  		require.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections))
   242  	})
   243  
   244  	t.Run("MostRecentCollectionConfigBelow10", func(t *testing.T) {
   245  		// No explicit collections below height 10, should return only implicit collections
   246  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   247  		retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(10, "chaincode1")
   248  		require.NoError(t, err)
   249  		require.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections))
   250  	})
   251  
   252  	t.Run("CollectionConfigAt50", func(t *testing.T) {
   253  		// No explicit collections at height 50, should return only implicit collections
   254  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   255  		retrievedConfig, err := retriever.CollectionConfigAt(50, "chaincode1")
   256  		require.NoError(t, err)
   257  		require.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections))
   258  	})
   259  
   260  	t.Run("CollectionConfigAt20", func(t *testing.T) {
   261  		// Explicit collections at height 20, should be merged with implicit collections
   262  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   263  		retrievedConfig, err := retriever.CollectionConfigAt(20, "chaincode1")
   264  		require.NoError(t, err)
   265  		require.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections))
   266  	})
   267  
   268  }
   269  
   270  type testEnvForSnapshot struct {
   271  	mgr             *Mgr
   272  	testSnapshotDir string
   273  	cleanup         func()
   274  }
   275  
   276  func newTestEnvForSnapshot(t *testing.T) *testEnvForSnapshot {
   277  	dbPath, err := ioutil.TempDir("", "confighistory")
   278  	require.NoError(t, err)
   279  	mgr, err := NewMgr(dbPath, &mock.DeployedChaincodeInfoProvider{})
   280  	if err != nil {
   281  		os.RemoveAll(dbPath)
   282  		t.Fatalf("Failed to create new config history manager: %s", err)
   283  	}
   284  
   285  	testSnapshotDir, err := ioutil.TempDir("", "confighistorysnapshot")
   286  	if err != nil {
   287  		os.RemoveAll(dbPath)
   288  		t.Fatalf("Failed to create config history snapshot directory: %s", err)
   289  	}
   290  	return &testEnvForSnapshot{
   291  		mgr:             mgr,
   292  		testSnapshotDir: testSnapshotDir,
   293  		cleanup: func() {
   294  			os.RemoveAll(dbPath)
   295  			os.RemoveAll(testSnapshotDir)
   296  		},
   297  	}
   298  }
   299  
   300  func TestExportAndImportConfigHistory(t *testing.T) {
   301  	env := newTestEnvForSnapshot(t)
   302  	defer env.cleanup()
   303  
   304  	setup := func(ledgerID string) ([]*compositeKV, map[string][]*ledger.CollectionConfigInfo) {
   305  		cc1CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2"})
   306  		cc2CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc2-coll-1", "Explicit-cc2-coll-2"})
   307  		cc3CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc3-coll-1", "Explicit-cc3-coll-2"})
   308  		cc1CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2", "Explicit-cc1-coll-3"})
   309  		cc2CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc2-coll-1", "Explicit-cc2-coll-2", "Explicit-cc2-coll-3"})
   310  		cc3CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc3-coll-1", "Explicit-cc3-coll-2", "Explicit-cc3-coll-3"})
   311  
   312  		ccConfigInfo := map[string][]*ledger.CollectionConfigInfo{
   313  			"chaincode1": {
   314  				{
   315  					CollectionConfig:   cc1CollConfigPackage,
   316  					CommittingBlockNum: 50,
   317  				},
   318  				{
   319  					CollectionConfig:   cc1CollConfigPackageNew,
   320  					CommittingBlockNum: 100,
   321  				},
   322  			},
   323  			"chaincode2": {
   324  				{
   325  					CollectionConfig:   cc2CollConfigPackage,
   326  					CommittingBlockNum: 50,
   327  				},
   328  				{
   329  					CollectionConfig:   cc2CollConfigPackageNew,
   330  					CommittingBlockNum: 100,
   331  				},
   332  			},
   333  			"chaincode3": {
   334  				{
   335  					CollectionConfig:   cc3CollConfigPackage,
   336  					CommittingBlockNum: 50,
   337  				},
   338  				{
   339  					CollectionConfig:   cc3CollConfigPackageNew,
   340  					CommittingBlockNum: 100,
   341  				},
   342  			},
   343  		}
   344  
   345  		db := env.mgr.dbProvider.getDB(ledgerID)
   346  		batch := db.newBatch()
   347  		err := prepareDBBatch(
   348  			batch,
   349  			map[string]*peer.CollectionConfigPackage{
   350  				"chaincode1": cc1CollConfigPackage,
   351  				"chaincode2": cc2CollConfigPackage,
   352  				"chaincode3": cc3CollConfigPackage,
   353  			},
   354  			50,
   355  		)
   356  		require.NoError(t, err)
   357  		require.NoError(t, db.writeBatch(batch, true))
   358  
   359  		batch = db.newBatch()
   360  		err = prepareDBBatch(
   361  			batch,
   362  			map[string]*peer.CollectionConfigPackage{
   363  				"chaincode1": cc1CollConfigPackageNew,
   364  				"chaincode2": cc2CollConfigPackageNew,
   365  				"chaincode3": cc3CollConfigPackageNew,
   366  			},
   367  			100,
   368  		)
   369  		require.NoError(t, err)
   370  		require.NoError(t, db.writeBatch(batch, true))
   371  
   372  		cc1configBytes, err := proto.Marshal(cc1CollConfigPackage)
   373  		require.NoError(t, err)
   374  		cc2configBytes, err := proto.Marshal(cc2CollConfigPackage)
   375  		require.NoError(t, err)
   376  		cc3configBytes, err := proto.Marshal(cc3CollConfigPackage)
   377  		require.NoError(t, err)
   378  		cc1configBytesNew, err := proto.Marshal(cc1CollConfigPackageNew)
   379  		require.NoError(t, err)
   380  		cc2configBytesNew, err := proto.Marshal(cc2CollConfigPackageNew)
   381  		require.NoError(t, err)
   382  		cc3configBytesNew, err := proto.Marshal(cc3CollConfigPackageNew)
   383  		require.NoError(t, err)
   384  
   385  		storedKVs := []*compositeKV{
   386  			{&compositeKey{ns: "lscc", key: "chaincode1~collection", blockNum: 100}, cc1configBytesNew},
   387  			{&compositeKey{ns: "lscc", key: "chaincode1~collection", blockNum: 50}, cc1configBytes},
   388  			{&compositeKey{ns: "lscc", key: "chaincode2~collection", blockNum: 100}, cc2configBytesNew},
   389  			{&compositeKey{ns: "lscc", key: "chaincode2~collection", blockNum: 50}, cc2configBytes},
   390  			{&compositeKey{ns: "lscc", key: "chaincode3~collection", blockNum: 100}, cc3configBytesNew},
   391  			{&compositeKey{ns: "lscc", key: "chaincode3~collection", blockNum: 50}, cc3configBytes},
   392  		}
   393  
   394  		return storedKVs, ccConfigInfo
   395  	}
   396  
   397  	cleanup := func() {
   398  		require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotDataFileName)))
   399  		require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName)))
   400  	}
   401  
   402  	t.Run("confighistory is empty", func(t *testing.T) {
   403  		retriever := env.mgr.GetRetriever("ledger0", nil)
   404  		fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   405  		require.NoError(t, err)
   406  		require.Empty(t, fileHashes)
   407  		files, err := ioutil.ReadDir(env.testSnapshotDir)
   408  		require.NoError(t, err)
   409  		require.Len(t, files, 0)
   410  	})
   411  
   412  	t.Run("export confighistory", func(t *testing.T) {
   413  		// setup ledger1 => export ledger1
   414  		storedKVs, _ := setup("ledger1")
   415  		defer cleanup()
   416  		retriever := env.mgr.GetRetriever("ledger1", nil)
   417  		fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   418  		require.NoError(t, err)
   419  		verifyExportedConfigHistory(t, env.testSnapshotDir, fileHashes, storedKVs)
   420  	})
   421  
   422  	t.Run("import confighistory and verify queries", func(t *testing.T) {
   423  		// setup ledger2 => export ledger2 => import into ledger3
   424  		_, ccConfigInfo := setup("ledger2")
   425  		defer cleanup()
   426  		retriever := env.mgr.GetRetriever("ledger2", nil)
   427  		_, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   428  		require.NoError(t, err)
   429  
   430  		importConfigsBatchSize = 100
   431  		require.NoError(t, env.mgr.ImportConfigHistory("ledger3", env.testSnapshotDir))
   432  		dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
   433  			info: &common.BlockchainInfo{Height: 1000},
   434  			qe:   &mock.QueryExecutor{},
   435  		}
   436  
   437  		retriever = env.mgr.GetRetriever("ledger3", dummyLedgerInfoRetriever)
   438  		verifyImportedConfigHistory(t, retriever, ccConfigInfo)
   439  	})
   440  
   441  	t.Run("export from an imported confighistory", func(t *testing.T) {
   442  		// setup ledger4 => export ledger4 => import into ledger5 => export ledger5
   443  		storedKVs, _ := setup("ledger4")
   444  		defer cleanup()
   445  		retriever := env.mgr.GetRetriever("ledger4", nil)
   446  		_, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   447  		require.NoError(t, err)
   448  
   449  		importConfigsBatchSize = 100
   450  		require.NoError(t, env.mgr.ImportConfigHistory("ledger5", env.testSnapshotDir))
   451  		cleanup()
   452  
   453  		retriever = env.mgr.GetRetriever("ledger5", nil)
   454  		fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   455  		require.NoError(t, err)
   456  		verifyExportedConfigHistory(t, env.testSnapshotDir, fileHashes, storedKVs)
   457  	})
   458  
   459  	t.Run("import confighistory error cases", func(t *testing.T) {
   460  		setup("ledger6")
   461  		defer cleanup()
   462  		retriever := env.mgr.GetRetriever("ledger6", nil)
   463  		_, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   464  		require.NoError(t, err)
   465  
   466  		err = env.mgr.ImportConfigHistory("ledger6", env.testSnapshotDir)
   467  		expectedErrStr := "config history for ledger [ledger6] exists. Incremental import is not supported. Remove the existing ledger data before retry"
   468  		require.EqualError(t, err, expectedErrStr)
   469  
   470  		require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotDataFileName)))
   471  		err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir)
   472  		require.Contains(t, err.Error(), "confighistory.data: no such file or directory")
   473  
   474  		dataFileWriter, err := snapshot.CreateFile(filepath.Join(env.testSnapshotDir, snapshotDataFileName), snapshotFileFormat, testNewHashFunc)
   475  		require.NoError(t, err)
   476  		defer dataFileWriter.Close()
   477  		require.NoError(t, dataFileWriter.EncodeUVarint(1))
   478  		err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir)
   479  		require.Contains(t, err.Error(), "error while reading from the snapshot file")
   480  		require.Contains(t, err.Error(), "confighistory.data: EOF")
   481  
   482  		require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName)))
   483  		err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir)
   484  		require.Contains(t, err.Error(), "confighistory.metadata: no such file or directory")
   485  
   486  		dataFileWriter, err = snapshot.CreateFile(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName), snapshotFileFormat, testNewHashFunc)
   487  		require.NoError(t, err)
   488  		defer dataFileWriter.Close()
   489  		require.NoError(t, dataFileWriter.EncodeBytes([]byte("junk")))
   490  		err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir)
   491  		require.Contains(t, err.Error(), "error while reading from the snapshot file")
   492  		require.Contains(t, err.Error(), "confighistory.metadata: EOF")
   493  
   494  		env.mgr.dbProvider.Close()
   495  		err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir)
   496  		require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed")
   497  	})
   498  }
   499  
   500  func verifyExportedConfigHistory(t *testing.T, dir string, fileHashes map[string][]byte, expectedCollectionConfigs []*compositeKV) {
   501  	require.Len(t, fileHashes, 2)
   502  	require.Contains(t, fileHashes, snapshotDataFileName)
   503  	require.Contains(t, fileHashes, snapshotMetadataFileName)
   504  
   505  	dataFile := filepath.Join(dir, snapshotDataFileName)
   506  	dataFileContent, err := ioutil.ReadFile(dataFile)
   507  	require.NoError(t, err)
   508  	dataFileHash := sha256.Sum256(dataFileContent)
   509  	require.Equal(t, dataFileHash[:], fileHashes[snapshotDataFileName])
   510  
   511  	metadataFile := filepath.Join(dir, snapshotMetadataFileName)
   512  	metadataFileContent, err := ioutil.ReadFile(metadataFile)
   513  	require.NoError(t, err)
   514  	metadataFileHash := sha256.Sum256(metadataFileContent)
   515  	require.Equal(t, metadataFileHash[:], fileHashes[snapshotMetadataFileName])
   516  
   517  	metadataReader, err := snapshot.OpenFile(metadataFile, snapshotFileFormat)
   518  	require.NoError(t, err)
   519  	defer metadataReader.Close()
   520  
   521  	dataReader, err := snapshot.OpenFile(dataFile, snapshotFileFormat)
   522  	require.NoError(t, err)
   523  	defer dataReader.Close()
   524  
   525  	numCollectionConfigs, err := metadataReader.DecodeUVarInt()
   526  	require.NoError(t, err)
   527  
   528  	var retrievedCollectionConfigs []*compositeKV
   529  	for i := uint64(0); i < numCollectionConfigs; i++ {
   530  		key, err := dataReader.DecodeBytes()
   531  		require.NoError(t, err)
   532  		val, err := dataReader.DecodeBytes()
   533  		require.NoError(t, err)
   534  		retrievedCollectionConfigs = append(retrievedCollectionConfigs,
   535  			&compositeKV{decodeCompositeKey(key), val},
   536  		)
   537  	}
   538  	require.Equal(t, expectedCollectionConfigs, retrievedCollectionConfigs)
   539  }
   540  
   541  func verifyImportedConfigHistory(t *testing.T, retriever *Retriever, expectedCCConfigInfo map[string][]*ledger.CollectionConfigInfo) {
   542  	for chaincodeName, ccConfigInfos := range expectedCCConfigInfo {
   543  		for _, expectedCCConfig := range ccConfigInfos {
   544  			ccConfig, err := retriever.CollectionConfigAt(expectedCCConfig.CommittingBlockNum, chaincodeName)
   545  			require.NoError(t, err)
   546  			require.True(t, proto.Equal(expectedCCConfig.CollectionConfig, ccConfig.CollectionConfig))
   547  			require.Equal(t, expectedCCConfig.CommittingBlockNum, ccConfig.CommittingBlockNum)
   548  
   549  			ccConfig, err = retriever.MostRecentCollectionConfigBelow(expectedCCConfig.CommittingBlockNum+1, chaincodeName)
   550  			require.NoError(t, err)
   551  			require.True(t, proto.Equal(expectedCCConfig.CollectionConfig, ccConfig.CollectionConfig))
   552  			require.Equal(t, expectedCCConfig.CommittingBlockNum, ccConfig.CommittingBlockNum)
   553  		}
   554  	}
   555  }
   556  
   557  func TestExportConfigHistoryErrorCase(t *testing.T) {
   558  	env := newTestEnvForSnapshot(t)
   559  	defer env.cleanup()
   560  
   561  	db := env.mgr.dbProvider.getDB("ledger1")
   562  	cc1collConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2"})
   563  	batch := db.newBatch()
   564  	err := prepareDBBatch(
   565  		batch,
   566  		map[string]*peer.CollectionConfigPackage{
   567  			"chaincode1": cc1collConfigPackage,
   568  		},
   569  		50,
   570  	)
   571  	require.NoError(t, err)
   572  	require.NoError(t, db.writeBatch(batch, true))
   573  
   574  	// error during data file creation
   575  	dataFilePath := filepath.Join(env.testSnapshotDir, snapshotDataFileName)
   576  	_, err = os.Create(dataFilePath)
   577  	require.NoError(t, err)
   578  
   579  	retriever := env.mgr.GetRetriever("ledger1", nil)
   580  	_, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   581  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+dataFilePath)
   582  	os.RemoveAll(env.testSnapshotDir)
   583  
   584  	// error during metadata file creation
   585  	require.NoError(t, os.MkdirAll(env.testSnapshotDir, 0700))
   586  	metadataFilePath := filepath.Join(env.testSnapshotDir, snapshotMetadataFileName)
   587  	_, err = os.Create(metadataFilePath)
   588  	require.NoError(t, err)
   589  	_, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   590  	require.Contains(t, err.Error(), "error while creating the snapshot file: "+metadataFilePath)
   591  	os.RemoveAll(env.testSnapshotDir)
   592  
   593  	// error while reading from leveldb
   594  	require.NoError(t, os.MkdirAll(env.testSnapshotDir, 0700))
   595  	env.mgr.dbProvider.Close()
   596  	_, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc)
   597  	require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed")
   598  	os.RemoveAll(env.testSnapshotDir)
   599  }
   600  
   601  func sampleCollectionConfigPackage(collNamePart1 string, collNamePart2 uint64) *peer.CollectionConfigPackage {
   602  	collName := fmt.Sprintf("%s-%d", collNamePart1, collNamePart2)
   603  	return testutilCreateCollConfigPkg([]string{collName})
   604  }
   605  
   606  func testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(
   607  	mockCCInfoProvider *mock.DeployedChaincodeInfoProvider,
   608  	chaincodeName string,
   609  	collConfigPackage *peer.CollectionConfigPackage) {
   610  	mockCCInfoProvider.UpdatedChaincodesReturns(
   611  		[]*ledger.ChaincodeLifecycleInfo{
   612  			{Name: chaincodeName},
   613  		},
   614  		nil,
   615  	)
   616  	mockCCInfoProvider.ChaincodeInfoReturns(
   617  		&ledger.DeployedChaincodeInfo{Name: chaincodeName, ExplicitCollectionConfigPkg: collConfigPackage},
   618  		nil,
   619  	)
   620  }
   621  
   622  func testutilCreateCollConfigPkg(collNames []string) *peer.CollectionConfigPackage {
   623  	pkg := &peer.CollectionConfigPackage{
   624  		Config: []*peer.CollectionConfig{},
   625  	}
   626  	for _, collName := range collNames {
   627  		pkg.Config = append(pkg.Config,
   628  			&peer.CollectionConfig{
   629  				Payload: &peer.CollectionConfig_StaticCollectionConfig{
   630  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   631  						Name: collName,
   632  					},
   633  				},
   634  			},
   635  		)
   636  	}
   637  	return pkg
   638  }
   639  
   640  type dummyLedgerInfoRetriever struct {
   641  	info *common.BlockchainInfo
   642  	qe   *mock.QueryExecutor
   643  }
   644  
   645  func (d *dummyLedgerInfoRetriever) GetBlockchainInfo() (*common.BlockchainInfo, error) {
   646  	return d.info, nil
   647  }
   648  
   649  func (d *dummyLedgerInfoRetriever) NewQueryExecutor() (ledger.QueryExecutor, error) {
   650  	return d.qe, nil
   651  }