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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"testing"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hyperledger/fabric-config/protolator"
    18  	"github.com/hyperledger/fabric-protos-go/common"
    19  	cb "github.com/hyperledger/fabric-protos-go/common"
    20  	pb "github.com/hyperledger/fabric-protos-go/peer"
    21  	"github.com/osdi23p228/fabric/common/channelconfig"
    22  	"github.com/osdi23p228/fabric/common/configtx/test"
    23  	"github.com/osdi23p228/fabric/common/ledger/blkstorage"
    24  	"github.com/osdi23p228/fabric/common/ledger/testutil"
    25  	"github.com/osdi23p228/fabric/common/metrics/disabled"
    26  	"github.com/osdi23p228/fabric/core/ledger"
    27  	"github.com/osdi23p228/fabric/core/ledger/mock"
    28  	"github.com/osdi23p228/fabric/protoutil"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func TestNamespacesAndCollections(t *testing.T) {
    33  	channelName := "testnamespacesandcollections"
    34  	basePath, err := ioutil.TempDir("", "testchannelinfoprovider")
    35  	require.NoError(t, err)
    36  	defer os.RemoveAll(basePath)
    37  	blkStoreProvider, blkStore := openBlockStorage(t, channelName, basePath)
    38  	defer blkStoreProvider.Close()
    39  
    40  	// add genesis block and another config block so that we can retrieve MSPIDs
    41  	// 3 orgs/MSPIDs are added: SampleOrg/SampleOrg, org1/Org1MSP, org2/Org2MSP
    42  	genesisBlock, err := test.MakeGenesisBlock(channelName)
    43  	require.NoError(t, err)
    44  	require.NoError(t, blkStore.AddBlock(genesisBlock))
    45  	orgGroups := createTestOrgGroups(t)
    46  	config := getConfigFromBlock(genesisBlock)
    47  	config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups["org1"] = orgGroups["org1"]
    48  	config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups["org2"] = orgGroups["org2"]
    49  	configEnv := getEnvelopeFromConfig(channelName, config)
    50  	configBlock := newBlock([]*cb.Envelope{configEnv}, 1, 1, protoutil.BlockHeaderHash(genesisBlock.Header))
    51  	require.NoError(t, blkStore.AddBlock(configBlock))
    52  
    53  	// prepare fakeDeployedCCInfoProvider to create mocked test data
    54  	deployedccInfo := map[string]*ledger.DeployedChaincodeInfo{
    55  		"cc1": {
    56  			Name:                        "cc1",
    57  			Version:                     "version",
    58  			Hash:                        []byte("cc1-hash"),
    59  			ExplicitCollectionConfigPkg: prepareCollectionConfigPackage([]string{"collectionA", "collectionB"}),
    60  		},
    61  		"cc2": {
    62  			Name:    "cc2",
    63  			Version: "version",
    64  			Hash:    []byte("cc2-hash"),
    65  		},
    66  	}
    67  	fakeDeployedCCInfoProvider := &mock.DeployedChaincodeInfoProvider{}
    68  	fakeDeployedCCInfoProvider.NamespacesReturns([]string{"_lifecycle", "lscc"})
    69  	fakeDeployedCCInfoProvider.AllChaincodesInfoReturns(deployedccInfo, nil)
    70  	fakeDeployedCCInfoProvider.GenerateImplicitCollectionForOrgStub = func(mspID string) *pb.StaticCollectionConfig {
    71  		return &pb.StaticCollectionConfig{
    72  			Name: fmt.Sprintf("_implicit_org_%s", mspID),
    73  		}
    74  	}
    75  
    76  	// verify NamespacesAndCollections
    77  	channelInfoProvider := &channelInfoProvider{channelName, blkStore, fakeDeployedCCInfoProvider}
    78  	expectedNamespacesAndColls := map[string][]string{
    79  		"cc1":        {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP", "_implicit_org_SampleOrg", "collectionA", "collectionB"},
    80  		"cc2":        {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP", "_implicit_org_SampleOrg"},
    81  		"_lifecycle": {"_implicit_org_Org1MSP", "_implicit_org_Org2MSP", "_implicit_org_SampleOrg"},
    82  		"lscc":       {},
    83  		"":           {},
    84  	}
    85  	namespacesAndColls, err := channelInfoProvider.NamespacesAndCollections(nil)
    86  	require.NoError(t, err)
    87  	require.Equal(t, len(expectedNamespacesAndColls), len(namespacesAndColls))
    88  	for ns, colls := range expectedNamespacesAndColls {
    89  		require.ElementsMatch(t, colls, namespacesAndColls[ns])
    90  	}
    91  }
    92  
    93  // TestGetAllMSPIDs verifies getAllMSPIDs by adding and removing organizations to the channel config.
    94  func TestGetAllMSPIDs(t *testing.T) {
    95  	channelName := "testgetallmspids"
    96  	basePath, err := ioutil.TempDir("", "testchannelinfoprovider")
    97  	require.NoError(t, err)
    98  	defer os.RemoveAll(basePath)
    99  
   100  	blkStoreProvider, blkStore := openBlockStorage(t, channelName, basePath)
   101  	defer blkStoreProvider.Close()
   102  	channelInfoProvider := &channelInfoProvider{channelName, blkStore, nil}
   103  
   104  	var block *cb.Block
   105  	var configBlock *cb.Block
   106  	var lastBlockNum = uint64(0)
   107  	var lastConfigBlockNum = uint64(0)
   108  
   109  	// verify GetAllMSPIDs in a corner case where the channel has no block
   110  	verifyGetAllMSPIDs(t, channelInfoProvider, nil)
   111  
   112  	// add genesis block and verify GetAllMSPIDs when the channel has only genesis block
   113  	// the genesis block is created for org "SampleOrg" with MSPID "SampleOrg"
   114  	configBlock, err = test.MakeGenesisBlock(channelName)
   115  	require.NoError(t, err)
   116  	require.NoError(t, blkStore.AddBlock(configBlock))
   117  	verifyGetAllMSPIDs(t, channelInfoProvider, []string{"SampleOrg"})
   118  
   119  	// add some blocks and verify GetAllMSPIDs
   120  	block = configBlock
   121  	for i := 0; i < 3; i++ {
   122  		lastBlockNum++
   123  		block = newBlock([]*cb.Envelope{}, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(block.Header))
   124  		require.NoError(t, blkStore.AddBlock(block))
   125  	}
   126  	verifyGetAllMSPIDs(t, channelInfoProvider, []string{"SampleOrg"})
   127  
   128  	// create test org groups, update the config by adding org1 (Org1MSP) and org2 (Org2MSP)
   129  	orgGroups := createTestOrgGroups(t)
   130  	config := getConfigFromBlock(configBlock)
   131  	config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups["org1"] = orgGroups["org1"]
   132  	config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups["org2"] = orgGroups["org2"]
   133  
   134  	// add the config block and verify GetAllMSPIDs
   135  	lastBlockNum++
   136  	lastConfigBlockNum = lastBlockNum
   137  	configEnv := getEnvelopeFromConfig(channelName, config)
   138  	configBlock = newBlock([]*cb.Envelope{configEnv}, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(block.Header))
   139  	require.NoError(t, blkStore.AddBlock(configBlock))
   140  	verifyGetAllMSPIDs(t, channelInfoProvider, []string{"Org1MSP", "Org2MSP", "SampleOrg"})
   141  
   142  	// update the config by removing "org1"
   143  	config = getConfigFromBlock(configBlock)
   144  	delete(config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups, "org1")
   145  
   146  	// add the config block and verify GetAllMSPIDs
   147  	lastBlockNum++
   148  	lastConfigBlockNum = lastBlockNum
   149  	configEnv = getEnvelopeFromConfig(channelName, config)
   150  	configBlock = newBlock([]*cb.Envelope{configEnv}, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(configBlock.Header))
   151  	require.NoError(t, blkStore.AddBlock(configBlock))
   152  	verifyGetAllMSPIDs(t, channelInfoProvider, []string{"Org1MSP", "Org2MSP", "SampleOrg"})
   153  
   154  	// add some blocks and verify GetAllMSPIDs
   155  	block = configBlock
   156  	for i := 0; i < 2; i++ {
   157  		lastBlockNum++
   158  		block = newBlock([]*cb.Envelope{}, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(block.Header))
   159  		require.NoError(t, blkStore.AddBlock(block))
   160  	}
   161  	verifyGetAllMSPIDs(t, channelInfoProvider, []string{"Org1MSP", "Org2MSP", "SampleOrg"})
   162  
   163  	// verify the orgs in most recent config block
   164  	lastConfigBlock, err := channelInfoProvider.mostRecentConfigBlockAsOf(lastBlockNum)
   165  	require.NoError(t, err)
   166  	config = getConfigFromBlock(lastConfigBlock)
   167  	require.Equal(t, 2, len(config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups))
   168  	require.Contains(t, config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups, "SampleOrg")
   169  	require.Contains(t, config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups, "org2")
   170  }
   171  
   172  func TestGetAllMSPIDs_NegativeTests(t *testing.T) {
   173  	channelName := "testgetallmspidsnegativetests"
   174  	basePath, err := ioutil.TempDir("", "testchannelinfoprovider_negativetests")
   175  	require.NoError(t, err)
   176  	defer os.RemoveAll(basePath)
   177  
   178  	blkStoreProvider, blkStore := openBlockStorage(t, channelName, basePath)
   179  	defer blkStoreProvider.Close()
   180  	channelInfoProvider := &channelInfoProvider{channelName, blkStore, nil}
   181  
   182  	var configBlock *cb.Block
   183  	var lastBlockNum = uint64(0)
   184  	var lastConfigBlockNum = uint64(0)
   185  
   186  	// add genesis block
   187  	configBlock, err = test.MakeGenesisBlock(channelName)
   188  	require.NoError(t, err)
   189  	require.NoError(t, blkStore.AddBlock(configBlock))
   190  
   191  	// test ExtractMSPIDsForApplicationOrgs error by having a malformed config block
   192  	lastBlockNum++
   193  	lastConfigBlockNum++
   194  	configBlock = newBlock([]*cb.Envelope{}, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(configBlock.Header))
   195  	require.NoError(t, blkStore.AddBlock(configBlock))
   196  	_, err = channelInfoProvider.getAllMSPIDs()
   197  	require.EqualError(t, err, "malformed configuration block: envelope index out of bounds")
   198  
   199  	// test RetrieveBlockByNumber error by using a non-existent block num for config block index
   200  	lastBlockNum++
   201  	lastConfigBlockNum++
   202  	configBlock = newBlock(nil, lastBlockNum, lastBlockNum+1, protoutil.BlockHeaderHash(configBlock.Header))
   203  	require.NoError(t, blkStore.AddBlock(configBlock))
   204  	_, err = channelInfoProvider.getAllMSPIDs()
   205  	require.EqualError(t, err, "Entry not found in index")
   206  
   207  	// test GetLastConfigIndexFromBlock error by using invalid bytes for LastConfig metadata value
   208  	lastBlockNum++
   209  	lastConfigBlockNum++
   210  	configBlock = newBlock(nil, lastBlockNum, lastConfigBlockNum, protoutil.BlockHeaderHash(configBlock.Header))
   211  	configBlock.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("invalid_bytes")
   212  	require.NoError(t, blkStore.AddBlock(configBlock))
   213  	_, err = channelInfoProvider.getAllMSPIDs()
   214  	require.EqualError(t, err, "failed to retrieve metadata: error unmarshaling metadata at index [SIGNATURES]: unexpected EOF")
   215  
   216  	// test RetrieveBlockByNumber error (before calling GetLastConfigIndexFromBlock) by closing block store provider
   217  	blkStoreProvider.Close()
   218  	_, err = channelInfoProvider.getAllMSPIDs()
   219  	require.Contains(t, err.Error(), "leveldb: closed")
   220  }
   221  
   222  func openBlockStorage(t *testing.T, channelName string, basePath string) (*blkstorage.BlockStoreProvider, *blkstorage.BlockStore) {
   223  	blkStoreProvider, err := blkstorage.NewProvider(
   224  		blkstorage.NewConf(basePath, maxBlockFileSize),
   225  		&blkstorage.IndexConfig{AttrsToIndex: attrsToIndex},
   226  		&disabled.Provider{},
   227  	)
   228  	require.NoError(t, err)
   229  	blkStore, err := blkStoreProvider.Open(channelName)
   230  	require.NoError(t, err)
   231  	return blkStoreProvider, blkStore
   232  }
   233  
   234  func verifyGetAllMSPIDs(t *testing.T, channelInfoProvider *channelInfoProvider, expectedMSPIDs []string) {
   235  	mspids, err := channelInfoProvider.getAllMSPIDs()
   236  	require.NoError(t, err)
   237  	require.ElementsMatch(t, expectedMSPIDs, mspids)
   238  }
   239  
   240  func newBlock(env []*common.Envelope, blockNum uint64, lastConfigBlockNum uint64, previousHash []byte) *cb.Block {
   241  	block := testutil.NewBlock(env, blockNum, previousHash)
   242  	block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
   243  		Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   244  			LastConfig: &cb.LastConfig{Index: lastConfigBlockNum},
   245  		}),
   246  	})
   247  	return block
   248  }
   249  
   250  func getConfigFromBlock(block *cb.Block) *cb.Config {
   251  	blockDataEnvelope := &cb.Envelope{}
   252  	err := proto.Unmarshal(block.Data.Data[0], blockDataEnvelope)
   253  	if err != nil {
   254  		panic(err)
   255  	}
   256  
   257  	blockDataPayload := &cb.Payload{}
   258  	err = proto.Unmarshal(blockDataEnvelope.Payload, blockDataPayload)
   259  	if err != nil {
   260  		panic(err)
   261  	}
   262  
   263  	config := &cb.ConfigEnvelope{}
   264  	err = proto.Unmarshal(blockDataPayload.Data, config)
   265  	if err != nil {
   266  		panic(err)
   267  	}
   268  
   269  	return config.Config
   270  }
   271  
   272  func getEnvelopeFromConfig(channelName string, config *cb.Config) *cb.Envelope {
   273  	return &cb.Envelope{
   274  		Payload: protoutil.MarshalOrPanic(&cb.Payload{
   275  			Header: &cb.Header{
   276  				ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
   277  					ChannelId: channelName,
   278  					Type:      int32(cb.HeaderType_CONFIG),
   279  				}),
   280  			},
   281  			Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{
   282  				Config: config,
   283  			}),
   284  		}),
   285  	}
   286  }
   287  
   288  // createTestOrgGroups returns application org ConfigGroups based on test_configblock.json.
   289  // The config block contains the following organizations(MSPIDs): org1(Org1MSP) and org2(Org2MSP)
   290  func createTestOrgGroups(t *testing.T) map[string]*cb.ConfigGroup {
   291  	blockData, err := ioutil.ReadFile("testdata/test_configblock.json")
   292  	require.NoError(t, err)
   293  	block := &common.Block{}
   294  	protolator.DeepUnmarshalJSON(bytes.NewBuffer(blockData), block)
   295  	config := getConfigFromBlock(block)
   296  	return config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups
   297  }
   298  
   299  func prepareCollectionConfigPackage(collNames []string) *pb.CollectionConfigPackage {
   300  	collConfigs := make([]*pb.CollectionConfig, len(collNames))
   301  	for i, name := range collNames {
   302  		collConfigs[i] = &pb.CollectionConfig{
   303  			Payload: &pb.CollectionConfig_StaticCollectionConfig{
   304  				StaticCollectionConfig: &pb.StaticCollectionConfig{
   305  					Name: name,
   306  				},
   307  			},
   308  		}
   309  	}
   310  	return &pb.CollectionConfigPackage{Config: collConfigs}
   311  }