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 }