github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/snapshot_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"math"
    15  	"os"
    16  	"path/filepath"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/golang/protobuf/proto"
    21  	"github.com/hechain20/hechain/bccsp/sw"
    22  	"github.com/hechain20/hechain/common/ledger/testutil"
    23  	"github.com/hechain20/hechain/common/metrics/disabled"
    24  	"github.com/hechain20/hechain/common/util"
    25  	"github.com/hechain20/hechain/core/chaincode/implicitcollection"
    26  	"github.com/hechain20/hechain/core/ledger"
    27  	"github.com/hechain20/hechain/core/ledger/cceventmgmt"
    28  	"github.com/hechain20/hechain/core/ledger/confighistory/confighistorytest"
    29  	"github.com/hechain20/hechain/core/ledger/internal/version"
    30  	kvledgermock "github.com/hechain20/hechain/core/ledger/kvledger/mock"
    31  	"github.com/hechain20/hechain/core/ledger/kvledger/msgs"
    32  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
    33  	"github.com/hechain20/hechain/core/ledger/mock"
    34  	"github.com/hechain20/hechain/internal/fileutil"
    35  	"github.com/hechain20/hechain/protoutil"
    36  	"github.com/hyperledger/fabric-protos-go/common"
    37  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    38  	"github.com/hyperledger/fabric-protos-go/peer"
    39  	"github.com/stretchr/testify/require"
    40  )
    41  
    42  func TestSnapshotGenerationAndNewLedgerCreation(t *testing.T) {
    43  	conf, cleanup := testConfig(t)
    44  	defer cleanup()
    45  	snapshotRootDir := conf.SnapshotsConfig.RootDir
    46  	nsCollBtlConfs := []*nsCollBtlConfig{
    47  		{
    48  			namespace: "ns",
    49  			btlConfig: map[string]uint64{"coll": 0},
    50  		},
    51  	}
    52  	provider := testutilNewProviderWithCollectionConfig(
    53  		t,
    54  		nsCollBtlConfs,
    55  		conf,
    56  	)
    57  	defer provider.Close()
    58  
    59  	// add the genesis block and generate the snapshot
    60  	blkGenerator, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false)
    61  	lgr, err := provider.CreateFromGenesisBlock(genesisBlk)
    62  	require.NoError(t, err)
    63  	defer lgr.Close()
    64  	kvlgr := lgr.(*kvLedger)
    65  	require.NoError(t, kvlgr.generateSnapshot())
    66  	verifySnapshotOutput(t,
    67  		&expectedSnapshotOutput{
    68  			snapshotRootDir:   snapshotRootDir,
    69  			ledgerID:          kvlgr.ledgerID,
    70  			lastBlockNumber:   0,
    71  			lastBlockHash:     protoutil.BlockHeaderHash(genesisBlk.Header),
    72  			previousBlockHash: genesisBlk.Header.PreviousHash,
    73  			lastCommitHash:    kvlgr.commitHash,
    74  			stateDBType:       simpleKeyValueDB,
    75  			expectedBinaryFiles: []string{
    76  				"txids.data", "txids.metadata",
    77  			},
    78  		},
    79  	)
    80  
    81  	// add block-1 only with public state data and generate the snapshot
    82  	blockAndPvtdata1 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk1",
    83  		map[string]string{
    84  			"key1": "value1.1",
    85  			"key2": "value2.1",
    86  			"key3": "value3.1",
    87  		},
    88  		nil,
    89  	)
    90  	require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata1, &ledger.CommitOptions{}))
    91  	require.NoError(t, kvlgr.generateSnapshot())
    92  	verifySnapshotOutput(t,
    93  		&expectedSnapshotOutput{
    94  			snapshotRootDir:   snapshotRootDir,
    95  			ledgerID:          kvlgr.ledgerID,
    96  			lastBlockNumber:   1,
    97  			lastBlockHash:     protoutil.BlockHeaderHash(blockAndPvtdata1.Block.Header),
    98  			previousBlockHash: blockAndPvtdata1.Block.Header.PreviousHash,
    99  			lastCommitHash:    kvlgr.commitHash,
   100  			stateDBType:       simpleKeyValueDB,
   101  			expectedBinaryFiles: []string{
   102  				"txids.data", "txids.metadata",
   103  				"public_state.data", "public_state.metadata",
   104  			},
   105  		},
   106  	)
   107  
   108  	// add dummy entry in collection config history and commit block-2 and generate the snapshot
   109  	addDummyEntryInCollectionConfigHistory(t, provider, kvlgr.ledgerID, "ns", 1, []*peer.StaticCollectionConfig{{Name: "coll"}})
   110  
   111  	// add block-2 only with public and private data and generate the snapshot
   112  	blockAndPvtdata2 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk2",
   113  		map[string]string{
   114  			"key1": "value1.2",
   115  			"key2": "value2.2",
   116  			"key3": "value3.2",
   117  		},
   118  		map[string]string{
   119  			"key1": "pvtValue1.2",
   120  			"key2": "pvtValue2.2",
   121  			"key3": "pvtValue3.2",
   122  		},
   123  	)
   124  	require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata2, &ledger.CommitOptions{}))
   125  	require.NoError(t, kvlgr.generateSnapshot())
   126  	verifySnapshotOutput(t,
   127  		&expectedSnapshotOutput{
   128  			snapshotRootDir:   snapshotRootDir,
   129  			ledgerID:          kvlgr.ledgerID,
   130  			lastBlockNumber:   2,
   131  			lastBlockHash:     protoutil.BlockHeaderHash(blockAndPvtdata2.Block.Header),
   132  			previousBlockHash: blockAndPvtdata2.Block.Header.PreviousHash,
   133  			lastCommitHash:    kvlgr.commitHash,
   134  			stateDBType:       simpleKeyValueDB,
   135  			expectedBinaryFiles: []string{
   136  				"txids.data", "txids.metadata",
   137  				"public_state.data", "public_state.metadata",
   138  				"private_state_hashes.data", "private_state_hashes.metadata",
   139  				"confighistory.data", "confighistory.metadata",
   140  			},
   141  		},
   142  	)
   143  
   144  	blockAndPvtdata3 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk3",
   145  		map[string]string{
   146  			"key1": "value1.3",
   147  			"key2": "value2.3",
   148  			"key3": "value3.3",
   149  		},
   150  		nil,
   151  	)
   152  	require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata3, &ledger.CommitOptions{}))
   153  	require.NoError(t, kvlgr.generateSnapshot())
   154  	verifySnapshotOutput(t,
   155  		&expectedSnapshotOutput{
   156  			snapshotRootDir:   snapshotRootDir,
   157  			ledgerID:          kvlgr.ledgerID,
   158  			lastBlockNumber:   3,
   159  			lastBlockHash:     protoutil.BlockHeaderHash(blockAndPvtdata3.Block.Header),
   160  			previousBlockHash: blockAndPvtdata3.Block.Header.PreviousHash,
   161  			lastCommitHash:    kvlgr.commitHash,
   162  			stateDBType:       simpleKeyValueDB,
   163  			expectedBinaryFiles: []string{
   164  				"txids.data", "txids.metadata",
   165  				"public_state.data", "public_state.metadata",
   166  				"private_state_hashes.data", "private_state_hashes.metadata",
   167  				"confighistory.data", "confighistory.metadata",
   168  			},
   169  		},
   170  	)
   171  
   172  	snapshotDir := SnapshotDirForLedgerBlockNum(snapshotRootDir, kvlgr.ledgerID, 3)
   173  
   174  	t.Run("create-ledger-from-snapshot", func(t *testing.T) {
   175  		createdLedger := testCreateLedgerFromSnapshot(t, snapshotDir, kvlgr.ledgerID)
   176  		verifyCreatedLedger(t,
   177  			provider,
   178  			createdLedger,
   179  			&expectedLegderState{
   180  				lastBlockNumber:   3,
   181  				lastBlockHash:     protoutil.BlockHeaderHash(blockAndPvtdata3.Block.Header),
   182  				previousBlockHash: blockAndPvtdata3.Block.Header.PreviousHash,
   183  				lastCommitHash:    kvlgr.commitHash,
   184  				namespace:         "ns",
   185  				publicState: map[string]string{
   186  					"key1": "value1.3",
   187  					"key2": "value2.3",
   188  					"key3": "value3.3",
   189  				},
   190  				collectionConfig: map[uint64]*peer.CollectionConfigPackage{
   191  					1: {
   192  						Config: []*peer.CollectionConfig{
   193  							{
   194  								Payload: &peer.CollectionConfig_StaticCollectionConfig{
   195  									StaticCollectionConfig: &peer.StaticCollectionConfig{
   196  										Name: "coll",
   197  									},
   198  								},
   199  							},
   200  						},
   201  					},
   202  				},
   203  			},
   204  		)
   205  	})
   206  
   207  	t.Run("create-ledger-from-snapshot-error-paths", func(t *testing.T) {
   208  		testCreateLedgerFromSnapshotErrorPaths(t, snapshotDir)
   209  	})
   210  }
   211  
   212  func TestSnapshotDBTypeCouchDB(t *testing.T) {
   213  	conf, cleanup := testConfig(t)
   214  	fmt.Printf("snapshotRootDir %s\n", conf.SnapshotsConfig.RootDir)
   215  	defer cleanup()
   216  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   217  	defer provider.Close()
   218  
   219  	_, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false)
   220  	lgr, err := provider.CreateFromGenesisBlock(genesisBlk)
   221  	require.NoError(t, err)
   222  	defer lgr.Close()
   223  	kvlgr := lgr.(*kvLedger)
   224  
   225  	// artificially set the db type
   226  	kvlgr.config.StateDBConfig.StateDatabase = ledger.CouchDB
   227  	require.NoError(t, kvlgr.generateSnapshot())
   228  	verifySnapshotOutput(t,
   229  		&expectedSnapshotOutput{
   230  			snapshotRootDir: conf.SnapshotsConfig.RootDir,
   231  			ledgerID:        kvlgr.ledgerID,
   232  			stateDBType:     ledger.CouchDB,
   233  			lastBlockHash:   protoutil.BlockHeaderHash(genesisBlk.Header),
   234  			expectedBinaryFiles: []string{
   235  				"txids.data", "txids.metadata",
   236  			},
   237  		},
   238  	)
   239  }
   240  
   241  func TestSnapshotCouchDBIndexCreation(t *testing.T) {
   242  	setup := func() (string, *ledger.CouchDBConfig, *Provider) {
   243  		conf, cleanup := testConfig(t)
   244  		t.Cleanup(cleanup)
   245  
   246  		snapshotRootDir := conf.SnapshotsConfig.RootDir
   247  		provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   248  		t.Cleanup(provider.Close)
   249  
   250  		// add the genesis block and generate the snapshot
   251  		blkGenerator, genesisBlk := testutil.NewBlockGenerator(t, "test_ledger", false)
   252  		lgr, err := provider.CreateFromGenesisBlock(genesisBlk)
   253  		require.NoError(t, err)
   254  		t.Cleanup(lgr.Close)
   255  		kvlgr := lgr.(*kvLedger)
   256  
   257  		blockAndPvtdata := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk1",
   258  			map[string]string{
   259  				"key1": `{"asset_name": "marble1", "color": "blue", "size": 1, "owner": "tom"}`,
   260  				"key2": `{"asset_name": "marble2", "color": "red", "size": 2, "owner": "jerry"}`,
   261  			},
   262  			nil,
   263  		)
   264  		require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata, &ledger.CommitOptions{}))
   265  		require.NoError(t, kvlgr.generateSnapshot())
   266  		snapshotDir := SnapshotDirForLedgerBlockNum(snapshotRootDir, kvlgr.ledgerID, 1)
   267  
   268  		cceventmgmt.Initialize(nil)
   269  		if couchDBAddress == "" {
   270  			couchDBAddress, stopCouchDBFunc = statecouchdb.StartCouchDB(t, nil)
   271  		}
   272  		couchDBConfig := &ledger.CouchDBConfig{
   273  			Address:             couchDBAddress,
   274  			Username:            "admin",
   275  			Password:            "adminpw",
   276  			MaxRetries:          3,
   277  			MaxRetriesOnStartup: 3,
   278  			RequestTimeout:      10 * time.Second,
   279  			InternalQueryLimit:  1000,
   280  			RedoLogPath:         filepath.Join(conf.RootFSPath, "couchdbRedoLogs"),
   281  		}
   282  
   283  		destConf, destCleanup := testConfig(t)
   284  		t.Cleanup(destCleanup)
   285  		destConf.StateDBConfig = &ledger.StateDBConfig{
   286  			StateDatabase: ledger.CouchDB,
   287  			CouchDB:       couchDBConfig,
   288  		}
   289  		destinationProvider := testutilNewProvider(destConf, t, &mock.DeployedChaincodeInfoProvider{})
   290  		return snapshotDir, couchDBConfig, destinationProvider
   291  	}
   292  
   293  	dbArtifactsBytes := testutil.CreateTarBytesForTest(
   294  		[]*testutil.TarFileEntry{
   295  			{
   296  				Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json",
   297  				Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`,
   298  			},
   299  		},
   300  	)
   301  
   302  	verifyIndexCreatedOnMarbleSize := func(lgr ledger.PeerLedger) {
   303  		qe, err := lgr.NewQueryExecutor()
   304  		require.NoError(t, err)
   305  		defer qe.Done()
   306  		iter, err := qe.ExecuteQuery("ns", `{"selector":{"owner":"tom"}, "sort": [{"size": "desc"}]}`)
   307  		require.NoError(t, err)
   308  		defer iter.Close()
   309  		actualResults := []*queryresult.KV{}
   310  		for {
   311  			queryResults, err := iter.Next()
   312  			require.NoError(t, err)
   313  			if queryResults == nil {
   314  				break
   315  			}
   316  			actualResults = append(actualResults, queryResults.(*queryresult.KV))
   317  		}
   318  		require.Len(t, actualResults, 1)
   319  		_, err = qe.ExecuteQuery("ns", `{"selector":{"owner":"tom"}, "sort": [{"color": "desc"}]}`)
   320  		require.Contains(t, err.Error(), "No index exists for this sort")
   321  	}
   322  
   323  	t.Run("create_indexes_on_couchdb_for_new_lifecycle", func(t *testing.T) {
   324  		snapshotDir, couchDBConfig, provider := setup()
   325  		defer func() {
   326  			require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig))
   327  		}()
   328  
   329  		// mimic new lifecycle chaincode "ns" installed and defiend and the package contains an index definition "sort index"
   330  		ccLifecycleEventProvider := provider.initializer.ChaincodeLifecycleEventProvider.(*mock.ChaincodeLifecycleEventProvider)
   331  		ccLifecycleEventProvider.RegisterListenerStub =
   332  			func(
   333  				channelID string,
   334  				listener ledger.ChaincodeLifecycleEventListener,
   335  				callback bool,
   336  			) error {
   337  				if callback {
   338  					err := listener.HandleChaincodeDeploy(
   339  						&ledger.ChaincodeDefinition{
   340  							Name: "ns",
   341  						},
   342  						dbArtifactsBytes,
   343  					)
   344  					require.NoError(t, err)
   345  				}
   346  				return nil
   347  			}
   348  
   349  		lgr, _, err := provider.CreateFromSnapshot(snapshotDir)
   350  		require.NoError(t, err)
   351  		verifyIndexCreatedOnMarbleSize(lgr)
   352  	})
   353  
   354  	t.Run("create_indexes_on_couchdb_for_legacy_lifecycle", func(t *testing.T) {
   355  		snapshotDir, couchDBConfig, provider := setup()
   356  		defer func() {
   357  			require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig))
   358  		}()
   359  
   360  		// mimic legacy chaincode "ns" installed and defiend and the package contains an index definition "sort index"
   361  		deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider)
   362  		deployedCCInfoProvider.AllChaincodesInfoReturns(
   363  			map[string]*ledger.DeployedChaincodeInfo{
   364  				"ns": {
   365  					Name:     "ns",
   366  					Version:  "version",
   367  					Hash:     []byte("hash"),
   368  					IsLegacy: true,
   369  				},
   370  				"anotherNs": {
   371  					Name:     "anotherNs",
   372  					Version:  "version",
   373  					Hash:     []byte("hash"),
   374  					IsLegacy: false,
   375  				},
   376  			},
   377  			nil,
   378  		)
   379  
   380  		installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{}
   381  		installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns(
   382  			true, dbArtifactsBytes, nil,
   383  		)
   384  		cceventmgmt.Initialize(installedChaincodeInfoProvider)
   385  		lgr, _, err := provider.CreateFromSnapshot(snapshotDir)
   386  		require.NoError(t, err)
   387  
   388  		require.Equal(t, 1, installedChaincodeInfoProvider.RetrieveChaincodeArtifactsCallCount())
   389  		require.Equal(t,
   390  			&cceventmgmt.ChaincodeDefinition{
   391  				Name:    "ns",
   392  				Version: "version",
   393  				Hash:    []byte("hash"),
   394  			},
   395  			installedChaincodeInfoProvider.RetrieveChaincodeArtifactsArgsForCall(0),
   396  		)
   397  		verifyIndexCreatedOnMarbleSize(lgr)
   398  	})
   399  
   400  	t.Run("errors-propagation", func(t *testing.T) {
   401  		snapshotDir, couchDBConfig, provider := setup()
   402  		defer func() {
   403  			require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig))
   404  		}()
   405  
   406  		t.Run("deployedChaincodeInfoProvider-returns-error", func(t *testing.T) {
   407  			deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider)
   408  			deployedCCInfoProvider.AllChaincodesInfoReturns(nil, fmt.Errorf("error-retrieving-all-defined-chaincodes"))
   409  
   410  			installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{}
   411  			installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns(
   412  				true, dbArtifactsBytes, nil,
   413  			)
   414  			cceventmgmt.Initialize(installedChaincodeInfoProvider)
   415  			_, _, err := provider.CreateFromSnapshot(snapshotDir)
   416  			require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-retrieving-all-defined-chaincodes")
   417  		})
   418  
   419  		t.Run("legacychaincodes-dbartifacts-retriever-returns-error", func(t *testing.T) {
   420  			deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider)
   421  			deployedCCInfoProvider.AllChaincodesInfoReturns(
   422  				map[string]*ledger.DeployedChaincodeInfo{
   423  					"ns": {
   424  						Name:     "ns",
   425  						Version:  "version",
   426  						Hash:     []byte("hash"),
   427  						IsLegacy: true,
   428  					},
   429  				},
   430  				nil,
   431  			)
   432  
   433  			installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{}
   434  			installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns(false, nil, fmt.Errorf("error-retrieving-db-artifacts"))
   435  			cceventmgmt.Initialize(installedChaincodeInfoProvider)
   436  			_, _, err := provider.CreateFromSnapshot(snapshotDir)
   437  			require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-retrieving-db-artifacts")
   438  		})
   439  
   440  		t.Run("chaincodeLifecycleEventProvider-returns-error", func(t *testing.T) {
   441  			chaincodeLifecycleEventProvider := provider.initializer.ChaincodeLifecycleEventProvider.(*mock.ChaincodeLifecycleEventProvider)
   442  			chaincodeLifecycleEventProvider.RegisterListenerReturns(fmt.Errorf("error-calling-back"))
   443  			cceventmgmt.Initialize(nil)
   444  			_, _, err := provider.CreateFromSnapshot(snapshotDir)
   445  			require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-calling-back")
   446  		})
   447  	})
   448  }
   449  
   450  func TestSnapshotDirPaths(t *testing.T) {
   451  	require.Equal(t, "/peerFSPath/snapshotRootDir/temp", SnapshotsTempDirPath("/peerFSPath/snapshotRootDir"))
   452  	require.Equal(t, "/peerFSPath/snapshotRootDir/completed", CompletedSnapshotsPath("/peerFSPath/snapshotRootDir"))
   453  	require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger", SnapshotsDirForLedger("/peerFSPath/snapshotRootDir", "myLedger"))
   454  	require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger/2000", SnapshotDirForLedgerBlockNum("/peerFSPath/snapshotRootDir", "myLedger", 2000))
   455  }
   456  
   457  func TestSnapshotDirPathsCreation(t *testing.T) {
   458  	conf, cleanup := testConfig(t)
   459  	defer cleanup()
   460  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   461  	defer func() {
   462  		provider.Close()
   463  	}()
   464  
   465  	inProgressSnapshotsPath := SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir)
   466  	completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir)
   467  
   468  	// verify that upon first time start, kvledgerProvider creates an empty temp dir and an empty final dir for the snapshots
   469  	for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} {
   470  		f, err := ioutil.ReadDir(dir)
   471  		require.NoError(t, err)
   472  		require.Len(t, f, 0)
   473  	}
   474  
   475  	// add a file in each of the above folders
   476  	for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} {
   477  		err := ioutil.WriteFile(filepath.Join(dir, "testFile"), []byte("some junk data"), 0o644)
   478  		require.NoError(t, err)
   479  		f, err := ioutil.ReadDir(dir)
   480  		require.NoError(t, err)
   481  		require.Len(t, f, 1)
   482  	}
   483  
   484  	// verify that upon subsequent opening, kvledgerProvider removes any under-processing snapshots,
   485  	// potentially from a previous crash, from the temp dir but it does not remove any files from the final dir
   486  	provider.Close()
   487  	provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   488  	f, err := ioutil.ReadDir(inProgressSnapshotsPath)
   489  	require.NoError(t, err)
   490  	require.Len(t, f, 0)
   491  	f, err = ioutil.ReadDir(completedSnapshotsPath)
   492  	require.NoError(t, err)
   493  	require.Len(t, f, 1)
   494  }
   495  
   496  func TestSnapshotsDirInitializingErrors(t *testing.T) {
   497  	initKVLedgerProvider := func(conf *ledger.Config) error {
   498  		cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   499  		require.NoError(t, err)
   500  		_, err = NewProvider(
   501  			&ledger.Initializer{
   502  				DeployedChaincodeInfoProvider: &mock.DeployedChaincodeInfoProvider{},
   503  				MetricsProvider:               &disabled.Provider{},
   504  				Config:                        conf,
   505  				HashProvider:                  cryptoProvider,
   506  			},
   507  		)
   508  		return err
   509  	}
   510  
   511  	t.Run("invalid-path", func(t *testing.T) {
   512  		conf, cleanup := testConfig(t)
   513  		defer cleanup()
   514  		conf.SnapshotsConfig.RootDir = "./a-relative-path"
   515  		err := initKVLedgerProvider(conf)
   516  		require.EqualError(t, err, "invalid path: ./a-relative-path. The path for the snapshot dir is expected to be an absolute path")
   517  	})
   518  
   519  	t.Run("snapshots final dir creation returns error", func(t *testing.T) {
   520  		conf, cleanup := testConfig(t)
   521  		defer cleanup()
   522  
   523  		completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir)
   524  		require.NoError(t, os.MkdirAll(filepath.Dir(completedSnapshotsPath), 0o755))
   525  		require.NoError(t, ioutil.WriteFile(completedSnapshotsPath, []byte("some data"), 0o644))
   526  		err := initKVLedgerProvider(conf)
   527  		require.Error(t, err)
   528  		require.Contains(t, err.Error(), "while creating the dir: "+completedSnapshotsPath)
   529  	})
   530  }
   531  
   532  func TestGenerateSnapshotErrors(t *testing.T) {
   533  	conf, cleanup := testConfig(t)
   534  	defer cleanup()
   535  	provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   536  	defer func() {
   537  		provider.Close()
   538  	}()
   539  
   540  	// create a ledger
   541  	_, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false)
   542  	lgr, err := provider.CreateFromGenesisBlock(genesisBlk)
   543  	require.NoError(t, err)
   544  	kvlgr := lgr.(*kvLedger)
   545  
   546  	closeAndReopenLedgerProvider := func() {
   547  		provider.Close()
   548  		provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   549  		lgr, err = provider.Open("testLedgerid")
   550  		require.NoError(t, err)
   551  		kvlgr = lgr.(*kvLedger)
   552  	}
   553  
   554  	t.Run("snapshot tmp dir creation returns error", func(t *testing.T) {
   555  		closeAndReopenLedgerProvider()
   556  		require.NoError(t, os.RemoveAll( // remove the base tempdir so that the snapshot tempdir creation fails
   557  			SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir),
   558  		))
   559  		err := kvlgr.generateSnapshot()
   560  		require.Error(t, err)
   561  		require.Contains(t, err.Error(), "error while creating temp dir")
   562  	})
   563  
   564  	t.Run("block store returns error", func(t *testing.T) {
   565  		closeAndReopenLedgerProvider()
   566  		provider.blkStoreProvider.Close() // close the blockstore provider to trigger the error
   567  		err := kvlgr.generateSnapshot()
   568  		require.Error(t, err)
   569  		errStackTrace := fmt.Sprintf("%+v", err)
   570  		require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator")
   571  		require.Contains(t, errStackTrace, "github.com/hechain20/hechain/common/ledger/blkstorage")
   572  	})
   573  
   574  	t.Run("config history mgr returns error", func(t *testing.T) {
   575  		closeAndReopenLedgerProvider()
   576  		provider.configHistoryMgr.Close() // close the configHistoryMgr to trigger the error
   577  		err := kvlgr.generateSnapshot()
   578  		require.Error(t, err)
   579  		errStackTrace := fmt.Sprintf("%+v", err)
   580  		require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator")
   581  		require.Contains(t, errStackTrace, "github.com/hechain20/hechain/core/ledger/confighistory")
   582  	})
   583  
   584  	t.Run("statedb returns error", func(t *testing.T) {
   585  		closeAndReopenLedgerProvider()
   586  		provider.dbProvider.Close() // close the dbProvider to trigger the error
   587  		err := kvlgr.generateSnapshot()
   588  		require.Error(t, err)
   589  		errStackTrace := fmt.Sprintf("%+v", err)
   590  		require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator")
   591  		require.Contains(t, errStackTrace, "statedb/stateleveldb/stateleveldb.go")
   592  	})
   593  
   594  	t.Run("renaming to the final snapshot dir returns error", func(t *testing.T) {
   595  		closeAndReopenLedgerProvider()
   596  		snapshotFinalDir := SnapshotDirForLedgerBlockNum(conf.SnapshotsConfig.RootDir, "testLedgerid", 0)
   597  		require.NoError(t, os.MkdirAll(snapshotFinalDir, 0o744))
   598  		defer os.RemoveAll(snapshotFinalDir)
   599  		require.NoError(t, ioutil.WriteFile( // make a non-empty snapshotFinalDir to trigger failure on rename
   600  			filepath.Join(snapshotFinalDir, "dummyFile"),
   601  			[]byte("dummy file"), 0o444),
   602  		)
   603  		err := kvlgr.generateSnapshot()
   604  		require.Contains(t, err.Error(), "error while renaming dir")
   605  	})
   606  
   607  	t.Run("deletes the temp folder upon error", func(t *testing.T) {
   608  		closeAndReopenLedgerProvider()
   609  		provider.blkStoreProvider.Close() // close the blockstore provider to trigger an error
   610  		err := kvlgr.generateSnapshot()
   611  		require.Error(t, err)
   612  
   613  		empty, err := fileutil.DirEmpty(SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir))
   614  		require.NoError(t, err)
   615  		require.True(t, empty)
   616  	})
   617  }
   618  
   619  func testCreateLedgerFromSnapshotErrorPaths(t *testing.T, originalSnapshotDir string) {
   620  	var provider *Provider
   621  	var snapshotDirForTest string
   622  	var cleanup func()
   623  
   624  	var metadata *SnapshotMetadata
   625  	var signableMetadataFile string
   626  	var additionalMetadataFile string
   627  
   628  	init := func(t *testing.T) {
   629  		conf, cleanupFunc := testConfig(t)
   630  		// make a copy of originalSnapshotDir
   631  		snapshotDirForTest = filepath.Join(conf.RootFSPath, "snapshot")
   632  		require.NoError(t, os.MkdirAll(snapshotDirForTest, 0o700))
   633  		files, err := ioutil.ReadDir(originalSnapshotDir)
   634  		require.NoError(t, err)
   635  		for _, f := range files {
   636  			content, err := ioutil.ReadFile(filepath.Join(originalSnapshotDir, f.Name()))
   637  			require.NoError(t, err)
   638  			err = ioutil.WriteFile(filepath.Join(snapshotDirForTest, f.Name()), content, 0o600)
   639  			require.NoError(t, err)
   640  		}
   641  
   642  		metadataJSONs, err := loadSnapshotMetadataJSONs(snapshotDirForTest)
   643  		require.NoError(t, err)
   644  		metadata, err = metadataJSONs.ToMetadata()
   645  		require.NoError(t, err)
   646  
   647  		signableMetadataFile = filepath.Join(snapshotDirForTest, SnapshotSignableMetadataFileName)
   648  		additionalMetadataFile = filepath.Join(snapshotDirForTest, snapshotAdditionalMetadataFileName)
   649  
   650  		provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   651  		cleanup = func() {
   652  			provider.Close()
   653  			cleanupFunc()
   654  		}
   655  	}
   656  
   657  	overwriteModifiedSignableMetadata := func() {
   658  		signaleMetadataJSON, err := metadata.SnapshotSignableMetadata.ToJSON()
   659  		require.NoError(t, err)
   660  		require.NoError(t, ioutil.WriteFile(signableMetadataFile, signaleMetadataJSON, 0o600))
   661  
   662  		metadata.snapshotAdditionalMetadata.SnapshotHashInHex = computeHashForTest(t, provider, signaleMetadataJSON)
   663  		additionalMetadataJSON, err := metadata.snapshotAdditionalMetadata.ToJSON()
   664  		require.NoError(t, err)
   665  		require.NoError(t, ioutil.WriteFile(additionalMetadataFile, additionalMetadataJSON, 0o600))
   666  	}
   667  
   668  	overwriteDataFile := func(fileName string, content []byte) {
   669  		filePath := filepath.Join(snapshotDirForTest, fileName)
   670  		require.NoError(t, ioutil.WriteFile(filePath, content, 0o600))
   671  		metadata.SnapshotSignableMetadata.FilesAndHashes[fileName] = computeHashForTest(t, provider, content)
   672  		overwriteModifiedSignableMetadata()
   673  	}
   674  
   675  	t.Run("singable-metadata-file-missing", func(t *testing.T) {
   676  		init(t)
   677  		defer cleanup()
   678  
   679  		require.NoError(t, os.Remove(filepath.Join(snapshotDirForTest, SnapshotSignableMetadataFileName)))
   680  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   681  		require.EqualError(t,
   682  			err,
   683  			fmt.Sprintf(
   684  				"error while loading metadata: open %s/_snapshot_signable_metadata.json: no such file or directory",
   685  				snapshotDirForTest,
   686  			),
   687  		)
   688  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   689  	})
   690  
   691  	t.Run("additional-metadata-file-missing", func(t *testing.T) {
   692  		init(t)
   693  		defer cleanup()
   694  
   695  		require.NoError(t, os.Remove(filepath.Join(snapshotDirForTest, snapshotAdditionalMetadataFileName)))
   696  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   697  		require.EqualError(t,
   698  			err,
   699  			fmt.Sprintf("error while loading metadata: open %s/_snapshot_additional_metadata.json: no such file or directory", snapshotDirForTest),
   700  		)
   701  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   702  	})
   703  
   704  	t.Run("singable-metadata-file-invalid-json", func(t *testing.T) {
   705  		init(t)
   706  		defer cleanup()
   707  
   708  		require.NoError(t, ioutil.WriteFile(signableMetadataFile, []byte(""), 0o600))
   709  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   710  		require.EqualError(t,
   711  			err,
   712  			"error while unmarshalling metadata: error while unmarshalling signable metadata: unexpected end of JSON input",
   713  		)
   714  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   715  	})
   716  
   717  	t.Run("additional-metadata-file-invalid-json", func(t *testing.T) {
   718  		init(t)
   719  		defer cleanup()
   720  
   721  		require.NoError(t, ioutil.WriteFile(additionalMetadataFile, []byte(""), 0o600))
   722  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   723  		require.EqualError(t,
   724  			err,
   725  			"error while unmarshalling metadata: error while unmarshalling additional metadata: unexpected end of JSON input",
   726  		)
   727  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   728  	})
   729  
   730  	t.Run("snapshot-hash-mismatch", func(t *testing.T) {
   731  		init(t)
   732  		defer cleanup()
   733  
   734  		require.NoError(t, ioutil.WriteFile(signableMetadataFile, []byte("{}"), 0o600))
   735  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   736  		require.Contains(t,
   737  			err.Error(),
   738  			"error while verifying snapshot: hash mismatch for file [_snapshot_signable_metadata.json]",
   739  		)
   740  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   741  	})
   742  
   743  	t.Run("datafile-missing", func(t *testing.T) {
   744  		init(t)
   745  		defer cleanup()
   746  
   747  		err := os.Remove(filepath.Join(snapshotDirForTest, "txids.data"))
   748  		require.NoError(t, err)
   749  
   750  		_, _, err = provider.CreateFromSnapshot(snapshotDirForTest)
   751  		require.EqualError(t, err,
   752  			fmt.Sprintf(
   753  				"error while verifying snapshot: open %s/txids.data: no such file or directory",
   754  				snapshotDirForTest,
   755  			),
   756  		)
   757  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   758  	})
   759  
   760  	t.Run("datafile-hash-mismatch", func(t *testing.T) {
   761  		init(t)
   762  		defer cleanup()
   763  
   764  		err := ioutil.WriteFile(filepath.Join(snapshotDirForTest, "txids.data"), []byte("random content"), 0o600)
   765  		require.NoError(t, err)
   766  
   767  		_, _, err = provider.CreateFromSnapshot(snapshotDirForTest)
   768  		require.Contains(t, err.Error(), "error while verifying snapshot: hash mismatch for file [txids.data]")
   769  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   770  	})
   771  
   772  	t.Run("hex-decoding-error-for-lastBlkHash", func(t *testing.T) {
   773  		init(t)
   774  		defer cleanup()
   775  
   776  		metadata.SnapshotSignableMetadata.LastBlockHashInHex = "invalid-hex"
   777  		overwriteModifiedSignableMetadata()
   778  
   779  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   780  		require.Contains(t, err.Error(), "error while decoding last block hash")
   781  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   782  	})
   783  
   784  	t.Run("hex-decoding-error-for-previousBlkHash", func(t *testing.T) {
   785  		init(t)
   786  		defer cleanup()
   787  
   788  		metadata.SnapshotSignableMetadata.PreviousBlockHashInHex = "invalid-hex"
   789  		overwriteModifiedSignableMetadata()
   790  
   791  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   792  		require.Contains(t, err.Error(), "error while decoding previous block hash")
   793  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   794  	})
   795  
   796  	t.Run("idStore-returns-error", func(t *testing.T) {
   797  		init(t)
   798  		defer cleanup()
   799  
   800  		provider.idStore.close()
   801  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   802  		require.Contains(t, err.Error(), "error while creating ledger id")
   803  	})
   804  
   805  	t.Run("blkstore-provider-returns-error", func(t *testing.T) {
   806  		init(t)
   807  		defer cleanup()
   808  
   809  		overwriteDataFile("txids.data", []byte(""))
   810  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   811  		require.Contains(t, err.Error(), "error while importing data into block store")
   812  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   813  	})
   814  
   815  	t.Run("config-history-mgr-returns-error", func(t *testing.T) {
   816  		init(t)
   817  		defer cleanup()
   818  
   819  		overwriteDataFile("confighistory.data", []byte(""))
   820  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   821  		require.Contains(t, err.Error(), "error while importing data into config history Mgr")
   822  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   823  	})
   824  
   825  	t.Run("statedb-provider-returns-error", func(t *testing.T) {
   826  		init(t)
   827  		defer cleanup()
   828  
   829  		overwriteDataFile("public_state.data", []byte(""))
   830  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   831  		require.Contains(t, err.Error(), "error while importing data into state db")
   832  		verifyLedgerDoesNotExist(t, provider, metadata.ChannelName)
   833  	})
   834  
   835  	t.Run("error-while-deleting-partially-created-ledger", func(t *testing.T) {
   836  		init(t)
   837  		defer cleanup()
   838  
   839  		provider.historydbProvider.Close()
   840  
   841  		_, _, err := provider.CreateFromSnapshot(snapshotDirForTest)
   842  		require.Contains(t, err.Error(), "error while preparing history db")
   843  		require.Contains(t, err.Error(), "error while deleting data from ledger")
   844  		verifyLedgerIDExists(t, provider, metadata.ChannelName, msgs.Status_UNDER_CONSTRUCTION)
   845  	})
   846  }
   847  
   848  func computeHashForTest(t *testing.T, provider *Provider, content []byte) string {
   849  	hasher, err := provider.initializer.HashProvider.GetHash(snapshotHashOpts)
   850  	require.NoError(t, err)
   851  	_, err = hasher.Write(content)
   852  	require.NoError(t, err)
   853  	return hex.EncodeToString(hasher.Sum(nil))
   854  }
   855  
   856  type expectedSnapshotOutput struct {
   857  	snapshotRootDir     string
   858  	ledgerID            string
   859  	lastBlockNumber     uint64
   860  	lastBlockHash       []byte
   861  	previousBlockHash   []byte
   862  	lastCommitHash      []byte
   863  	stateDBType         string
   864  	expectedBinaryFiles []string
   865  }
   866  
   867  func verifySnapshotOutput(
   868  	t *testing.T,
   869  	o *expectedSnapshotOutput,
   870  ) {
   871  	inProgressSnapshotsPath := SnapshotsTempDirPath(o.snapshotRootDir)
   872  	f, err := ioutil.ReadDir(inProgressSnapshotsPath)
   873  	require.NoError(t, err)
   874  	require.Len(t, f, 0)
   875  
   876  	snapshotDir := SnapshotDirForLedgerBlockNum(o.snapshotRootDir, o.ledgerID, o.lastBlockNumber)
   877  	files, err := ioutil.ReadDir(snapshotDir)
   878  	require.NoError(t, err)
   879  	require.Len(t, files, len(o.expectedBinaryFiles)+2) // + 2 JSON files
   880  
   881  	filesAndHashes := map[string]string{}
   882  	for _, f := range o.expectedBinaryFiles {
   883  		c, err := ioutil.ReadFile(filepath.Join(snapshotDir, f))
   884  		require.NoError(t, err)
   885  		filesAndHashes[f] = hex.EncodeToString(util.ComputeSHA256(c))
   886  	}
   887  
   888  	// verify the contents of the file snapshot_metadata.json
   889  	m := &SnapshotSignableMetadata{}
   890  	mJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, SnapshotSignableMetadataFileName))
   891  	require.NoError(t, err)
   892  	require.NoError(t, json.Unmarshal(mJSON, m))
   893  
   894  	previousBlockHashHex := ""
   895  	if o.previousBlockHash != nil {
   896  		previousBlockHashHex = hex.EncodeToString(o.previousBlockHash)
   897  	}
   898  	require.Equal(t,
   899  		&SnapshotSignableMetadata{
   900  			ChannelName:            o.ledgerID,
   901  			LastBlockNumber:        o.lastBlockNumber,
   902  			LastBlockHashInHex:     hex.EncodeToString(o.lastBlockHash),
   903  			PreviousBlockHashInHex: previousBlockHashHex,
   904  			StateDBType:            o.stateDBType,
   905  			FilesAndHashes:         filesAndHashes,
   906  		},
   907  		m,
   908  	)
   909  
   910  	// verify the contents of the file snapshot_metadata_hash.json
   911  	mh := &snapshotAdditionalMetadata{}
   912  	mhJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, snapshotAdditionalMetadataFileName))
   913  	require.NoError(t, err)
   914  	require.NoError(t, json.Unmarshal(mhJSON, mh))
   915  	require.Equal(t,
   916  		&snapshotAdditionalMetadata{
   917  			SnapshotHashInHex:        hex.EncodeToString(util.ComputeSHA256(mJSON)),
   918  			LastBlockCommitHashInHex: hex.EncodeToString(o.lastCommitHash),
   919  		},
   920  		mh,
   921  	)
   922  }
   923  
   924  func testCreateLedgerFromSnapshot(t *testing.T, snapshotDir string, expectedChannelID string) *kvLedger {
   925  	conf, cleanup := testConfig(t)
   926  	defer cleanup()
   927  	p := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{})
   928  	destLedger, channelID, err := p.CreateFromSnapshot(snapshotDir)
   929  	require.NoError(t, err)
   930  	require.Equal(t, expectedChannelID, channelID)
   931  	return destLedger.(*kvLedger)
   932  }
   933  
   934  type expectedLegderState struct {
   935  	lastBlockNumber   uint64
   936  	lastBlockHash     []byte
   937  	previousBlockHash []byte
   938  	lastCommitHash    []byte
   939  	namespace         string
   940  	publicState       map[string]string
   941  	collectionConfig  map[uint64]*peer.CollectionConfigPackage
   942  }
   943  
   944  func verifyCreatedLedger(t *testing.T,
   945  	p *Provider,
   946  	l *kvLedger,
   947  	e *expectedLegderState,
   948  ) {
   949  	verifyLedgerIDExists(t, p, l.ledgerID, msgs.Status_ACTIVE)
   950  
   951  	destBCInfo, err := l.GetBlockchainInfo()
   952  	require.NoError(t, err)
   953  	require.Equal(t,
   954  		&common.BlockchainInfo{
   955  			Height:            e.lastBlockNumber + 1,
   956  			CurrentBlockHash:  e.lastBlockHash,
   957  			PreviousBlockHash: e.previousBlockHash,
   958  			BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   959  				LastBlockInSnapshot: e.lastBlockNumber,
   960  			},
   961  		},
   962  		destBCInfo,
   963  	)
   964  
   965  	statedbSavepoint, err := l.txmgr.GetLastSavepoint()
   966  	require.NoError(t, err)
   967  	require.Equal(t, version.NewHeight(e.lastBlockNumber, math.MaxUint64), statedbSavepoint)
   968  
   969  	historydbSavepoint, err := l.historyDB.GetLastSavepoint()
   970  	require.NoError(t, err)
   971  	require.Equal(t, version.NewHeight(e.lastBlockNumber, math.MaxUint64), historydbSavepoint)
   972  
   973  	qe, err := l.txmgr.NewQueryExecutor("dummyTxId")
   974  	require.NoError(t, err)
   975  	defer qe.Done()
   976  	for k, v := range e.publicState {
   977  		val, err := qe.GetState(e.namespace, k)
   978  		require.NoError(t, err)
   979  		require.Equal(t, v, string(val))
   980  	}
   981  	for committingBlock, collConfigPkg := range e.collectionConfig {
   982  		collConfigInfo, err := l.configHistoryRetriever.MostRecentCollectionConfigBelow(committingBlock+1, e.namespace)
   983  		require.NoError(t, err)
   984  		require.Equal(t, committingBlock, collConfigInfo.CommittingBlockNum)
   985  		require.True(t, proto.Equal(collConfigPkg, collConfigInfo.CollectionConfig))
   986  	}
   987  }
   988  
   989  func addDummyEntryInCollectionConfigHistory(
   990  	t *testing.T,
   991  	provider *Provider,
   992  	ledgerID string,
   993  	namespace string,
   994  	committingBlockNumber uint64,
   995  	collectionConfig []*peer.StaticCollectionConfig,
   996  ) {
   997  	configHistory := &confighistorytest.Mgr{
   998  		Mgr:                provider.configHistoryMgr,
   999  		MockCCInfoProvider: provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider),
  1000  	}
  1001  	require.NoError(t,
  1002  		configHistory.Setup(ledgerID, namespace,
  1003  			map[uint64][]*peer.StaticCollectionConfig{
  1004  				committingBlockNumber: collectionConfig,
  1005  			},
  1006  		),
  1007  	)
  1008  }
  1009  
  1010  func TestMostRecentCollectionConfigFetcher(t *testing.T) {
  1011  	conf, cleanup := testConfig(t)
  1012  	defer cleanup()
  1013  
  1014  	ledgerID := "test-ledger"
  1015  	chaincodeName := "test-chaincode"
  1016  
  1017  	implicitCollectionName := implicitcollection.NameForOrg("test-org")
  1018  	implicitCollection := &peer.StaticCollectionConfig{
  1019  		Name: implicitCollectionName,
  1020  	}
  1021  	mockDeployedCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
  1022  	mockDeployedCCInfoProvider.GenerateImplicitCollectionForOrgReturns(implicitCollection)
  1023  
  1024  	provider := testutilNewProvider(conf, t, mockDeployedCCInfoProvider)
  1025  	explicitCollectionName := "explicit-coll"
  1026  	explicitCollection := &peer.StaticCollectionConfig{
  1027  		Name: explicitCollectionName,
  1028  	}
  1029  	testutilPersistExplicitCollectionConfig(
  1030  		t,
  1031  		provider,
  1032  		mockDeployedCCInfoProvider,
  1033  		ledgerID,
  1034  		chaincodeName,
  1035  		testutilCollConfigPkg(
  1036  			[]*peer.StaticCollectionConfig{
  1037  				explicitCollection,
  1038  			},
  1039  		),
  1040  		10,
  1041  	)
  1042  
  1043  	fetcher := &mostRecentCollectionConfigFetcher{
  1044  		DeployedChaincodeInfoProvider: mockDeployedCCInfoProvider,
  1045  		Retriever:                     provider.configHistoryMgr.GetRetriever(ledgerID),
  1046  	}
  1047  
  1048  	testcases := []struct {
  1049  		name                 string
  1050  		lookupCollectionName string
  1051  		expectedOutput       *peer.StaticCollectionConfig
  1052  	}{
  1053  		{
  1054  			name:                 "lookup-implicit-collection",
  1055  			lookupCollectionName: implicitCollectionName,
  1056  			expectedOutput:       implicitCollection,
  1057  		},
  1058  
  1059  		{
  1060  			name:                 "lookup-explicit-collection",
  1061  			lookupCollectionName: explicitCollectionName,
  1062  			expectedOutput:       explicitCollection,
  1063  		},
  1064  
  1065  		{
  1066  			name:                 "lookup-non-existing-explicit-collection",
  1067  			lookupCollectionName: "non-existing-explicit-collection",
  1068  			expectedOutput:       nil,
  1069  		},
  1070  	}
  1071  
  1072  	for _, testcase := range testcases {
  1073  		t.Run(
  1074  			testcase.name,
  1075  			func(t *testing.T) {
  1076  				config, err := fetcher.CollectionInfo(chaincodeName, testcase.lookupCollectionName)
  1077  				require.NoError(t, err)
  1078  				require.True(t, proto.Equal(testcase.expectedOutput, config))
  1079  			},
  1080  		)
  1081  	}
  1082  
  1083  	t.Run("explicit-collection-lookup-causes-error", func(t *testing.T) {
  1084  		provider.configHistoryMgr.Close()
  1085  		_, err := fetcher.CollectionInfo(chaincodeName, explicitCollectionName)
  1086  		require.Contains(t, err.Error(), "error while fetching most recent collection config")
  1087  	})
  1088  }