github.com/yimialmonte/fabric@v2.1.1+incompatible/discovery/support/config/support_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package config_test 8 9 import ( 10 "crypto/rand" 11 "encoding/hex" 12 "fmt" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "testing" 17 18 "github.com/golang/protobuf/proto" 19 "github.com/hyperledger/fabric-protos-go/common" 20 "github.com/hyperledger/fabric-protos-go/discovery" 21 "github.com/hyperledger/fabric-protos-go/msp" 22 "github.com/hyperledger/fabric/common/channelconfig" 23 "github.com/hyperledger/fabric/common/configtx" 24 "github.com/hyperledger/fabric/common/configtx/test" 25 "github.com/hyperledger/fabric/discovery/support/config" 26 "github.com/hyperledger/fabric/discovery/support/mocks" 27 "github.com/hyperledger/fabric/internal/configtxgen/encoder" 28 "github.com/hyperledger/fabric/internal/configtxgen/genesisconfig" 29 "github.com/hyperledger/fabric/protoutil" 30 "github.com/onsi/gomega/gexec" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 func blockWithPayload() *common.Block { 35 env := &common.Envelope{ 36 Payload: []byte{1, 2, 3}, 37 } 38 b, _ := proto.Marshal(env) 39 return &common.Block{ 40 Data: &common.BlockData{ 41 Data: [][]byte{b}, 42 }, 43 } 44 } 45 46 func blockWithConfigEnvelope() *common.Block { 47 pl := &common.Payload{ 48 Data: []byte{1, 2, 3}, 49 } 50 plBytes, _ := proto.Marshal(pl) 51 env := &common.Envelope{ 52 Payload: plBytes, 53 } 54 b, _ := proto.Marshal(env) 55 return &common.Block{ 56 Data: &common.BlockData{ 57 Data: [][]byte{b}, 58 }, 59 } 60 } 61 62 func TestMSPIDMapping(t *testing.T) { 63 randString := func() string { 64 buff := make([]byte, 10) 65 rand.Read(buff) 66 return hex.EncodeToString(buff) 67 } 68 69 dir := filepath.Join(os.TempDir(), fmt.Sprintf("TestMSPIDMapping_%s", randString())) 70 os.Mkdir(dir, 0700) 71 defer os.RemoveAll(dir) 72 73 cryptogen, err := gexec.Build("github.com/hyperledger/fabric/cmd/cryptogen") 74 assert.NoError(t, err) 75 defer os.Remove(cryptogen) 76 77 idemixgen, err := gexec.Build("github.com/hyperledger/fabric/cmd/idemixgen") 78 assert.NoError(t, err) 79 defer os.Remove(idemixgen) 80 81 cryptoConfigDir := filepath.Join(dir, "crypto-config") 82 b, err := exec.Command(cryptogen, "generate", fmt.Sprintf("--output=%s", cryptoConfigDir)).CombinedOutput() 83 assert.NoError(t, err, string(b)) 84 85 idemixConfigDir := filepath.Join(dir, "crypto-config", "idemix") 86 b, err = exec.Command(idemixgen, "ca-keygen", fmt.Sprintf("--output=%s", idemixConfigDir)).CombinedOutput() 87 assert.NoError(t, err, string(b)) 88 89 profileConfig := genesisconfig.Load("TwoOrgsChannel", "testdata/") 90 ordererConfig := genesisconfig.Load("TwoOrgsOrdererGenesis", "testdata/") 91 profileConfig.Orderer = ordererConfig.Orderer 92 93 // Override the MSP directory with our randomly generated and populated path 94 for _, org := range ordererConfig.Orderer.Organizations { 95 org.MSPDir = filepath.Join(cryptoConfigDir, "ordererOrganizations", "example.com", "msp") 96 org.Name = randString() 97 } 98 99 // Randomize organization names 100 for _, org := range profileConfig.Application.Organizations { 101 org.Name = randString() 102 // Non bccsp-msp orgs don't have the crypto material produced by cryptogen, 103 // we need to use the idemix crypto folder instead. 104 if org.MSPType != "bccsp" { 105 org.MSPDir = filepath.Join(idemixConfigDir) 106 continue 107 } 108 org.MSPDir = filepath.Join(cryptoConfigDir, "peerOrganizations", "org1.example.com", "msp") 109 } 110 111 gen := encoder.New(profileConfig) 112 block := gen.GenesisBlockForChannel("mychannel") 113 114 fakeBlockGetter := &mocks.ConfigBlockGetter{} 115 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 116 117 cs := config.NewDiscoverySupport(fakeBlockGetter) 118 res, err := cs.Config("mychannel") 119 120 actualKeys := make(map[string]struct{}) 121 for key := range res.Orderers { 122 actualKeys[key] = struct{}{} 123 } 124 125 for key := range res.Msps { 126 actualKeys[key] = struct{}{} 127 } 128 129 // Note that Org3MSP is an idemix org, but it shouldn't be listed here 130 // because peers can't have idemix credentials 131 expected := map[string]struct{}{ 132 "OrdererMSP": {}, 133 "Org1MSP": {}, 134 "Org2MSP": {}, 135 } 136 assert.Equal(t, expected, actualKeys) 137 } 138 139 func TestSupportGreenPath(t *testing.T) { 140 fakeBlockGetter := &mocks.ConfigBlockGetter{} 141 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, nil) 142 143 cs := config.NewDiscoverySupport(fakeBlockGetter) 144 res, err := cs.Config("test") 145 assert.Nil(t, res) 146 assert.Equal(t, "could not get last config block for channel test", err.Error()) 147 148 block, err := test.MakeGenesisBlock("test") 149 assert.NoError(t, err) 150 assert.NotNil(t, block) 151 152 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, block) 153 res, err = cs.Config("test") 154 assert.NoError(t, err) 155 assert.NotNil(t, res) 156 } 157 158 func TestSupportBadConfig(t *testing.T) { 159 fakeBlockGetter := &mocks.ConfigBlockGetter{} 160 cs := config.NewDiscoverySupport(fakeBlockGetter) 161 162 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, &common.Block{ 163 Data: &common.BlockData{}, 164 }) 165 res, err := cs.Config("test") 166 assert.Contains(t, err.Error(), "no transactions in block") 167 assert.Nil(t, res) 168 169 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, &common.Block{ 170 Data: &common.BlockData{ 171 Data: [][]byte{{1, 2, 3}}, 172 }, 173 }) 174 res, err = cs.Config("test") 175 assert.Contains(t, err.Error(), "failed unmarshaling envelope") 176 assert.Nil(t, res) 177 178 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(2, blockWithPayload()) 179 res, err = cs.Config("test") 180 assert.Contains(t, err.Error(), "failed unmarshaling payload") 181 assert.Nil(t, res) 182 183 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(3, blockWithConfigEnvelope()) 184 res, err = cs.Config("test") 185 assert.Contains(t, err.Error(), "failed unmarshaling config envelope") 186 assert.Nil(t, res) 187 } 188 189 func TestValidateConfigEnvelope(t *testing.T) { 190 tests := []struct { 191 name string 192 ce *common.ConfigEnvelope 193 containsError string 194 }{ 195 { 196 name: "nil Config field", 197 ce: &common.ConfigEnvelope{}, 198 containsError: "field Config is nil", 199 }, 200 { 201 name: "nil ChannelGroup field", 202 ce: &common.ConfigEnvelope{ 203 Config: &common.Config{}, 204 }, 205 containsError: "field Config.ChannelGroup is nil", 206 }, 207 { 208 name: "nil Groups field", 209 ce: &common.ConfigEnvelope{ 210 Config: &common.Config{ 211 ChannelGroup: &common.ConfigGroup{}, 212 }, 213 }, 214 containsError: "field Config.ChannelGroup.Groups is nil", 215 }, 216 { 217 name: "no orderer group key", 218 ce: &common.ConfigEnvelope{ 219 Config: &common.Config{ 220 ChannelGroup: &common.ConfigGroup{ 221 Groups: map[string]*common.ConfigGroup{ 222 channelconfig.ApplicationGroupKey: {}, 223 }, 224 }, 225 }, 226 }, 227 containsError: "key Config.ChannelGroup.Groups[Orderer] is missing", 228 }, 229 { 230 name: "no application group key", 231 ce: &common.ConfigEnvelope{ 232 Config: &common.Config{ 233 ChannelGroup: &common.ConfigGroup{ 234 Groups: map[string]*common.ConfigGroup{ 235 channelconfig.OrdererGroupKey: { 236 Groups: map[string]*common.ConfigGroup{}, 237 }, 238 }, 239 }, 240 }, 241 }, 242 containsError: "key Config.ChannelGroup.Groups[Application] is missing", 243 }, 244 { 245 name: "no groups key in orderer group", 246 ce: &common.ConfigEnvelope{ 247 Config: &common.Config{ 248 ChannelGroup: &common.ConfigGroup{ 249 Groups: map[string]*common.ConfigGroup{ 250 channelconfig.ApplicationGroupKey: { 251 Groups: map[string]*common.ConfigGroup{}, 252 }, 253 channelconfig.OrdererGroupKey: {}, 254 }, 255 }, 256 }, 257 }, 258 containsError: "key Config.ChannelGroup.Groups[Orderer].Groups is nil", 259 }, 260 { 261 name: "no groups key in application group", 262 ce: &common.ConfigEnvelope{ 263 Config: &common.Config{ 264 ChannelGroup: &common.ConfigGroup{ 265 Groups: map[string]*common.ConfigGroup{ 266 channelconfig.ApplicationGroupKey: {}, 267 channelconfig.OrdererGroupKey: { 268 Groups: map[string]*common.ConfigGroup{}, 269 }, 270 }, 271 }, 272 }, 273 }, 274 containsError: "key Config.ChannelGroup.Groups[Application].Groups is nil", 275 }, 276 { 277 name: "no Values in ChannelGroup", 278 ce: &common.ConfigEnvelope{ 279 Config: &common.Config{ 280 ChannelGroup: &common.ConfigGroup{ 281 Groups: map[string]*common.ConfigGroup{ 282 channelconfig.ApplicationGroupKey: { 283 Groups: map[string]*common.ConfigGroup{}, 284 }, 285 channelconfig.OrdererGroupKey: { 286 Groups: map[string]*common.ConfigGroup{}, 287 }, 288 }, 289 }, 290 }, 291 }, 292 containsError: "field Config.ChannelGroup.Values is nil", 293 }, 294 } 295 296 for _, test := range tests { 297 test := test 298 t.Run(test.name, func(t *testing.T) { 299 err := config.ValidateConfigEnvelope(test.ce) 300 assert.Contains(t, test.containsError, err.Error()) 301 }) 302 } 303 304 } 305 306 func TestOrdererEndpoints(t *testing.T) { 307 t.Run("Global endpoints", func(t *testing.T) { 308 block, err := test.MakeGenesisBlock("mychannel") 309 assert.NoError(t, err) 310 311 fakeBlockGetter := &mocks.ConfigBlockGetter{} 312 cs := config.NewDiscoverySupport(fakeBlockGetter) 313 314 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 315 316 injectGlobalOrdererEndpoint(t, block, "globalEndpoint:7050") 317 318 res, err := cs.Config("test") 319 assert.NoError(t, err) 320 assert.Equal(t, map[string]*discovery.Endpoints{ 321 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "globalEndpoint", Port: 7050}}}, 322 }, res.Orderers) 323 }) 324 325 t.Run("Per org endpoints alongside global endpoints", func(t *testing.T) { 326 block, err := test.MakeGenesisBlock("mychannel") 327 assert.NoError(t, err) 328 329 fakeBlockGetter := &mocks.ConfigBlockGetter{} 330 cs := config.NewDiscoverySupport(fakeBlockGetter) 331 332 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 333 334 injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "anotherOrg") 335 injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg") 336 337 res, err := cs.Config("test") 338 assert.NoError(t, err) 339 assert.Equal(t, map[string]*discovery.Endpoints{ 340 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "127.0.0.1", Port: 7050}}}, 341 "anotherOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}}, 342 "aBadOrg": {}, 343 }, res.Orderers) 344 }) 345 346 t.Run("Per org endpoints without global endpoints", func(t *testing.T) { 347 block, err := test.MakeGenesisBlock("mychannel") 348 assert.NoError(t, err) 349 350 fakeBlockGetter := &mocks.ConfigBlockGetter{} 351 cs := config.NewDiscoverySupport(fakeBlockGetter) 352 353 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 354 355 removeGlobalEndpoints(t, block) 356 injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "SampleOrg") 357 injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg") 358 359 res, err := cs.Config("test") 360 assert.NoError(t, err) 361 assert.Equal(t, map[string]*discovery.Endpoints{ 362 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}}, 363 "aBadOrg": {}, 364 }, res.Orderers) 365 }) 366 } 367 368 func removeGlobalEndpoints(t *testing.T, block *common.Block) { 369 // Unwrap the layers until we reach the orderer addresses 370 env, err := protoutil.ExtractEnvelope(block, 0) 371 assert.NoError(t, err) 372 payload := protoutil.UnmarshalPayloadOrPanic(env.Payload) 373 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 374 assert.NoError(t, err) 375 // Remove the orderer addresses 376 delete(confEnv.Config.ChannelGroup.Values, channelconfig.OrdererAddressesKey) 377 // And put it back into the block 378 payload.Data = protoutil.MarshalOrPanic(confEnv) 379 env.Payload = protoutil.MarshalOrPanic(payload) 380 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 381 } 382 383 func injectGlobalOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) { 384 ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint}) 385 // Unwrap the layers until we reach the orderer addresses 386 env, err := protoutil.ExtractEnvelope(block, 0) 387 assert.NoError(t, err) 388 payload, err := protoutil.UnmarshalPayload(env.Payload) 389 assert.NoError(t, err) 390 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 391 assert.NoError(t, err) 392 // Replace the orderer addresses 393 confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()].Value = protoutil.MarshalOrPanic(ordererAddresses.Value()) 394 // Remove the per org addresses, if applicable 395 ordererGrps := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups 396 for _, grp := range ordererGrps { 397 if grp.Values[channelconfig.EndpointsKey] == nil { 398 continue 399 } 400 grp.Values[channelconfig.EndpointsKey].Value = nil 401 } 402 // And put it back into the block 403 payload.Data = protoutil.MarshalOrPanic(confEnv) 404 env.Payload = protoutil.MarshalOrPanic(payload) 405 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 406 } 407 408 func injectAdditionalEndpointPair(t *testing.T, block *common.Block, endpoint string, orgName string) { 409 // Unwrap the layers until we reach the orderer addresses 410 env, err := protoutil.ExtractEnvelope(block, 0) 411 assert.NoError(t, err) 412 payload, err := protoutil.UnmarshalPayload(env.Payload) 413 assert.NoError(t, err) 414 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 415 assert.NoError(t, err) 416 ordererGrp := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups 417 // Get the first orderer org config 418 var firstOrdererConfig *common.ConfigGroup 419 for _, grp := range ordererGrp { 420 firstOrdererConfig = grp 421 break 422 } 423 // Duplicate it. 424 secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup) 425 ordererGrp[orgName] = secondOrdererConfig 426 // Reach the FabricMSPConfig buried in it. 427 mspConfig := &msp.MSPConfig{} 428 err = proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig) 429 assert.NoError(t, err) 430 431 fabricConfig := &msp.FabricMSPConfig{} 432 err = proto.Unmarshal(mspConfig.Config, fabricConfig) 433 assert.NoError(t, err) 434 435 // Rename it. 436 fabricConfig.Name = orgName 437 438 // Pack the MSP config back into the config 439 secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{ 440 Config: protoutil.MarshalOrPanic(fabricConfig), 441 Type: mspConfig.Type, 442 }) 443 444 // Inject the endpoint 445 ordererOrgProtos := &common.OrdererAddresses{ 446 Addresses: []string{endpoint}, 447 } 448 secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos) 449 450 // Fold everything back into the block 451 payload.Data = protoutil.MarshalOrPanic(confEnv) 452 env.Payload = protoutil.MarshalOrPanic(payload) 453 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 454 }