github.com/yimialmonte/fabric@v2.1.1+incompatible/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  	"fmt"
    11  	"io/ioutil"
    12  	"math"
    13  	"os"
    14  	"testing"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hyperledger/fabric-protos-go/common"
    18  	"github.com/hyperledger/fabric-protos-go/peer"
    19  	"github.com/hyperledger/fabric/common/flogging"
    20  	"github.com/hyperledger/fabric/core/ledger"
    21  	"github.com/hyperledger/fabric/core/ledger/mock"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func TestMain(m *testing.M) {
    27  	flogging.ActivateSpec("confighistory=debug")
    28  	os.Exit(m.Run())
    29  }
    30  
    31  func TestWithNoCollectionConfig(t *testing.T) {
    32  	dbPath, err := ioutil.TempDir("", "confighistory")
    33  	if err != nil {
    34  		t.Fatalf("Failed to create config history directory: %s", err)
    35  	}
    36  	defer os.RemoveAll(dbPath)
    37  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    38  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
    39  	assert.NoError(t, err)
    40  	testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, "chaincode1", nil)
    41  	err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
    42  		LedgerID:           "ledger1",
    43  		CommittingBlockNum: 50},
    44  	)
    45  	assert.NoError(t, err)
    46  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
    47  		info: &common.BlockchainInfo{Height: 100},
    48  		qe:   &mock.QueryExecutor{},
    49  	}
    50  	retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
    51  	collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1")
    52  	assert.NoError(t, err)
    53  	assert.Nil(t, collConfig)
    54  }
    55  
    56  func TestWithEmptyCollectionConfig(t *testing.T) {
    57  	dbPath, err := ioutil.TempDir("", "confighistory")
    58  	if err != nil {
    59  		t.Fatalf("Failed to create config history directory: %s", err)
    60  	}
    61  	defer os.RemoveAll(dbPath)
    62  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    63  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
    64  	assert.NoError(t, err)
    65  	testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(
    66  		mockCCInfoProvider,
    67  		"chaincode1",
    68  		&peer.CollectionConfigPackage{},
    69  	)
    70  	err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
    71  		LedgerID:           "ledger1",
    72  		CommittingBlockNum: 50},
    73  	)
    74  	assert.NoError(t, err)
    75  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
    76  		info: &common.BlockchainInfo{Height: 100},
    77  		qe:   &mock.QueryExecutor{},
    78  	}
    79  	retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
    80  	collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1")
    81  	assert.NoError(t, err)
    82  	assert.Nil(t, collConfig)
    83  }
    84  
    85  func TestMgr(t *testing.T) {
    86  	dbPath, err := ioutil.TempDir("", "confighistory")
    87  	if err != nil {
    88  		t.Fatalf("Failed to create config history directory: %s", err)
    89  	}
    90  	defer os.RemoveAll(dbPath)
    91  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    92  	mgr, err := NewMgr(dbPath, mockCCInfoProvider)
    93  	assert.NoError(t, err)
    94  	chaincodeName := "chaincode1"
    95  	maxBlockNumberInLedger := uint64(2000)
    96  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
    97  		info: &common.BlockchainInfo{Height: maxBlockNumberInLedger + 1},
    98  		qe:   &mock.QueryExecutor{},
    99  	}
   100  	configCommittingBlockNums := []uint64{5, 10, 15, 100}
   101  	ledgerIds := []string{"ledgerid1", "ledger2"}
   102  
   103  	// Populate collection config versions
   104  	for _, ledgerid := range ledgerIds {
   105  		for _, committingBlockNum := range configCommittingBlockNums {
   106  			// for each ledgerid and commitHeight combination, construct a unique collConfigPackage and induce a stateUpdate
   107  			collConfigPackage := sampleCollectionConfigPackage(ledgerid, committingBlockNum)
   108  			testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, chaincodeName, collConfigPackage)
   109  			mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{
   110  				LedgerID:           ledgerid,
   111  				CommittingBlockNum: committingBlockNum},
   112  			)
   113  		}
   114  	}
   115  
   116  	t.Run("test-api-MostRecentCollectionConfigBelow()", func(t *testing.T) {
   117  		// A map that contains entries such that for each of the entries of type <K, V>,
   118  		// we retrieve the 'MostRecentCollectionConfigBelow' for 'K' and the expected value
   119  		// should be configuration committed at 'V'
   120  		m := map[uint64]uint64{math.MaxUint64: 100, 1000: 100, 50: 15, 12: 10, 7: 5}
   121  		for _, ledgerid := range ledgerIds {
   122  			retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever)
   123  			for testHeight, expectedHeight := range m {
   124  				retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(testHeight, chaincodeName)
   125  				assert.NoError(t, err)
   126  				expectedConfig := sampleCollectionConfigPackage(ledgerid, expectedHeight)
   127  				assert.Equal(t, expectedConfig, retrievedConfig.CollectionConfig)
   128  				assert.Equal(t, expectedHeight, retrievedConfig.CommittingBlockNum)
   129  			}
   130  
   131  			retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(5, chaincodeName)
   132  			assert.NoError(t, err)
   133  			assert.Nil(t, retrievedConfig)
   134  		}
   135  	})
   136  
   137  	t.Run("test-api-CollectionConfigAt()", func(t *testing.T) {
   138  		for _, ledgerid := range ledgerIds {
   139  			retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever)
   140  			for _, commitHeight := range configCommittingBlockNums {
   141  				retrievedConfig, err := retriever.CollectionConfigAt(commitHeight, chaincodeName)
   142  				assert.NoError(t, err)
   143  				expectedConfig := sampleCollectionConfigPackage(ledgerid, commitHeight)
   144  				assert.Equal(t, expectedConfig, retrievedConfig.CollectionConfig)
   145  				assert.Equal(t, commitHeight, retrievedConfig.CommittingBlockNum)
   146  			}
   147  		}
   148  	})
   149  
   150  	t.Run("test-api-CollectionConfigAt-BoundaryCases()", func(t *testing.T) {
   151  		retriever := mgr.GetRetriever("ledgerid1", dummyLedgerInfoRetriever)
   152  		retrievedConfig, err := retriever.CollectionConfigAt(4, chaincodeName)
   153  		assert.NoError(t, err)
   154  		assert.Nil(t, retrievedConfig)
   155  
   156  		retrievedConfig, err = retriever.CollectionConfigAt(5000, chaincodeName)
   157  		typedErr, ok := err.(*ledger.ErrCollectionConfigNotYetAvailable)
   158  		assert.True(t, ok)
   159  		assert.Equal(t, maxBlockNumberInLedger, typedErr.MaxBlockNumCommitted)
   160  	})
   161  }
   162  
   163  func TestWithImplicitColls(t *testing.T) {
   164  	dbPath, err := ioutil.TempDir("", "confighistory")
   165  	if err != nil {
   166  		t.Fatalf("Failed to create config history directory: %s", err)
   167  	}
   168  	defer os.RemoveAll(dbPath)
   169  	collConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-coll-1", "Explicit-coll-2"})
   170  	mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
   171  	mockCCInfoProvider.ImplicitCollectionsReturns(
   172  		[]*peer.StaticCollectionConfig{
   173  			{
   174  				Name: "Implicit-coll-1",
   175  			},
   176  			{
   177  				Name: "Implicit-coll-2",
   178  			},
   179  		},
   180  		nil,
   181  	)
   182  	p, err := newDBProvider(dbPath)
   183  	require.NoError(t, err)
   184  
   185  	mgr := &mgr{
   186  		ccInfoProvider: mockCCInfoProvider,
   187  		dbProvider:     p,
   188  	}
   189  
   190  	// add explicit collections at height 20
   191  	batch, err := prepareDBBatch(
   192  		map[string]*peer.CollectionConfigPackage{
   193  			"chaincode1": collConfigPackage,
   194  		},
   195  		20,
   196  	)
   197  	assert.NoError(t, err)
   198  	dbHandle := mgr.dbProvider.getDB("ledger1")
   199  	assert.NoError(t, dbHandle.writeBatch(batch, true))
   200  
   201  	onlyImplicitCollections := testutilCreateCollConfigPkg(
   202  		[]string{"Implicit-coll-1", "Implicit-coll-2"},
   203  	)
   204  
   205  	explicitAndImplicitCollections := testutilCreateCollConfigPkg(
   206  		[]string{"Explicit-coll-1", "Explicit-coll-2", "Implicit-coll-1", "Implicit-coll-2"},
   207  	)
   208  
   209  	dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{
   210  		info: &common.BlockchainInfo{Height: 1000},
   211  		qe:   &mock.QueryExecutor{},
   212  	}
   213  
   214  	t.Run("CheckQueryExecutorCalls", func(t *testing.T) {
   215  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   216  		// function MostRecentCollectionConfigBelow calls Done on query executor
   217  		_, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1")
   218  		assert.NoError(t, err)
   219  		assert.Equal(t, 1, dummyLedgerInfoRetriever.qe.DoneCallCount())
   220  		// function CollectionConfigAt calls Done on query executor
   221  		_, err = retriever.CollectionConfigAt(50, "chaincode1")
   222  		assert.NoError(t, err)
   223  		assert.Equal(t, 2, dummyLedgerInfoRetriever.qe.DoneCallCount())
   224  	})
   225  
   226  	t.Run("MostRecentCollectionConfigBelow50", func(t *testing.T) {
   227  		// explicit collections added at height 20 should be merged with the implicit collections
   228  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   229  		retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1")
   230  		assert.NoError(t, err)
   231  		assert.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections))
   232  	})
   233  
   234  	t.Run("MostRecentCollectionConfigBelow10", func(t *testing.T) {
   235  		// No explicit collections below height 10, should return only implicit collections
   236  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   237  		retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(10, "chaincode1")
   238  		assert.NoError(t, err)
   239  		assert.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections))
   240  	})
   241  
   242  	t.Run("CollectionConfigAt50", func(t *testing.T) {
   243  		// No explicit collections at height 50, should return only implicit collections
   244  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   245  		retrievedConfig, err := retriever.CollectionConfigAt(50, "chaincode1")
   246  		assert.NoError(t, err)
   247  		assert.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections))
   248  	})
   249  
   250  	t.Run("CollectionConfigAt20", func(t *testing.T) {
   251  		// Explicit collections at height 20, should be merged with implicit collections
   252  		retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever)
   253  		retrievedConfig, err := retriever.CollectionConfigAt(20, "chaincode1")
   254  		assert.NoError(t, err)
   255  		assert.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections))
   256  	})
   257  
   258  }
   259  
   260  func sampleCollectionConfigPackage(collNamePart1 string, collNamePart2 uint64) *peer.CollectionConfigPackage {
   261  	collName := fmt.Sprintf("%s-%d", collNamePart1, collNamePart2)
   262  	return testutilCreateCollConfigPkg([]string{collName})
   263  }
   264  
   265  func testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(
   266  	mockCCInfoProvider *mock.DeployedChaincodeInfoProvider,
   267  	chaincodeName string,
   268  	collConfigPackage *peer.CollectionConfigPackage) {
   269  	mockCCInfoProvider.UpdatedChaincodesReturns(
   270  		[]*ledger.ChaincodeLifecycleInfo{
   271  			{Name: chaincodeName},
   272  		},
   273  		nil,
   274  	)
   275  	mockCCInfoProvider.ChaincodeInfoReturns(
   276  		&ledger.DeployedChaincodeInfo{Name: chaincodeName, ExplicitCollectionConfigPkg: collConfigPackage},
   277  		nil,
   278  	)
   279  }
   280  
   281  func testutilCreateCollConfigPkg(collNames []string) *peer.CollectionConfigPackage {
   282  	pkg := &peer.CollectionConfigPackage{
   283  		Config: []*peer.CollectionConfig{},
   284  	}
   285  	for _, collName := range collNames {
   286  		pkg.Config = append(pkg.Config,
   287  			&peer.CollectionConfig{
   288  				Payload: &peer.CollectionConfig_StaticCollectionConfig{
   289  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   290  						Name: collName,
   291  					},
   292  				},
   293  			},
   294  		)
   295  	}
   296  	return pkg
   297  }
   298  
   299  type dummyLedgerInfoRetriever struct {
   300  	info *common.BlockchainInfo
   301  	qe   *mock.QueryExecutor
   302  }
   303  
   304  func (d *dummyLedgerInfoRetriever) GetBlockchainInfo() (*common.BlockchainInfo, error) {
   305  	return d.info, nil
   306  }
   307  
   308  func (d *dummyLedgerInfoRetriever) NewQueryExecutor() (ledger.QueryExecutor, error) {
   309  	return d.qe, nil
   310  }