github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/channelconfig" 23 "github.com/osdi23p228/fabric/common/configtx" 24 "github.com/osdi23p228/fabric/common/configtx/test" 25 "github.com/osdi23p228/fabric/discovery/support/config" 26 "github.com/osdi23p228/fabric/discovery/support/mocks" 27 "github.com/osdi23p228/fabric/internal/configtxgen/encoder" 28 "github.com/osdi23p228/fabric/internal/configtxgen/genesisconfig" 29 "github.com/osdi23p228/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/osdi23p228/fabric/cmd/cryptogen") 74 assert.NoError(t, err) 75 defer os.Remove(cryptogen) 76 77 idemixgen, err := gexec.Build("github.com/osdi23p228/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 assert.NoError(t, err) 120 121 actualKeys := make(map[string]struct{}) 122 for key := range res.Orderers { 123 actualKeys[key] = struct{}{} 124 } 125 126 for key := range res.Msps { 127 actualKeys[key] = struct{}{} 128 } 129 130 // Note that Org3MSP is an idemix org, but it shouldn't be listed here 131 // because peers can't have idemix credentials 132 expected := map[string]struct{}{ 133 "OrdererMSP": {}, 134 "Org1MSP": {}, 135 "Org2MSP": {}, 136 } 137 assert.Equal(t, expected, actualKeys) 138 } 139 140 func TestSupportGreenPath(t *testing.T) { 141 fakeBlockGetter := &mocks.ConfigBlockGetter{} 142 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, nil) 143 144 cs := config.NewDiscoverySupport(fakeBlockGetter) 145 res, err := cs.Config("test") 146 assert.Nil(t, res) 147 assert.Equal(t, "could not get last config block for channel test", err.Error()) 148 149 block, err := test.MakeGenesisBlock("test") 150 assert.NoError(t, err) 151 assert.NotNil(t, block) 152 153 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, block) 154 res, err = cs.Config("test") 155 assert.NoError(t, err) 156 assert.NotNil(t, res) 157 } 158 159 func TestSupportBadConfig(t *testing.T) { 160 fakeBlockGetter := &mocks.ConfigBlockGetter{} 161 cs := config.NewDiscoverySupport(fakeBlockGetter) 162 163 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, &common.Block{ 164 Data: &common.BlockData{}, 165 }) 166 res, err := cs.Config("test") 167 assert.Contains(t, err.Error(), "no transactions in block") 168 assert.Nil(t, res) 169 170 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, &common.Block{ 171 Data: &common.BlockData{ 172 Data: [][]byte{{1, 2, 3}}, 173 }, 174 }) 175 res, err = cs.Config("test") 176 assert.Contains(t, err.Error(), "failed unmarshaling envelope") 177 assert.Nil(t, res) 178 179 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(2, blockWithPayload()) 180 res, err = cs.Config("test") 181 assert.Contains(t, err.Error(), "failed unmarshaling payload") 182 assert.Nil(t, res) 183 184 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(3, blockWithConfigEnvelope()) 185 res, err = cs.Config("test") 186 assert.Contains(t, err.Error(), "failed unmarshaling config envelope") 187 assert.Nil(t, res) 188 } 189 190 func TestValidateConfigEnvelope(t *testing.T) { 191 tests := []struct { 192 name string 193 ce *common.ConfigEnvelope 194 containsError string 195 }{ 196 { 197 name: "nil Config field", 198 ce: &common.ConfigEnvelope{}, 199 containsError: "field Config is nil", 200 }, 201 { 202 name: "nil ChannelGroup field", 203 ce: &common.ConfigEnvelope{ 204 Config: &common.Config{}, 205 }, 206 containsError: "field Config.ChannelGroup is nil", 207 }, 208 { 209 name: "nil Groups field", 210 ce: &common.ConfigEnvelope{ 211 Config: &common.Config{ 212 ChannelGroup: &common.ConfigGroup{}, 213 }, 214 }, 215 containsError: "field Config.ChannelGroup.Groups is nil", 216 }, 217 { 218 name: "no orderer group key", 219 ce: &common.ConfigEnvelope{ 220 Config: &common.Config{ 221 ChannelGroup: &common.ConfigGroup{ 222 Groups: map[string]*common.ConfigGroup{ 223 channelconfig.ApplicationGroupKey: {}, 224 }, 225 }, 226 }, 227 }, 228 containsError: "key Config.ChannelGroup.Groups[Orderer] is missing", 229 }, 230 { 231 name: "no application group key", 232 ce: &common.ConfigEnvelope{ 233 Config: &common.Config{ 234 ChannelGroup: &common.ConfigGroup{ 235 Groups: map[string]*common.ConfigGroup{ 236 channelconfig.OrdererGroupKey: { 237 Groups: map[string]*common.ConfigGroup{}, 238 }, 239 }, 240 }, 241 }, 242 }, 243 containsError: "key Config.ChannelGroup.Groups[Application] is missing", 244 }, 245 { 246 name: "no groups key in orderer group", 247 ce: &common.ConfigEnvelope{ 248 Config: &common.Config{ 249 ChannelGroup: &common.ConfigGroup{ 250 Groups: map[string]*common.ConfigGroup{ 251 channelconfig.ApplicationGroupKey: { 252 Groups: map[string]*common.ConfigGroup{}, 253 }, 254 channelconfig.OrdererGroupKey: {}, 255 }, 256 }, 257 }, 258 }, 259 containsError: "key Config.ChannelGroup.Groups[Orderer].Groups is nil", 260 }, 261 { 262 name: "no groups key in application group", 263 ce: &common.ConfigEnvelope{ 264 Config: &common.Config{ 265 ChannelGroup: &common.ConfigGroup{ 266 Groups: map[string]*common.ConfigGroup{ 267 channelconfig.ApplicationGroupKey: {}, 268 channelconfig.OrdererGroupKey: { 269 Groups: map[string]*common.ConfigGroup{}, 270 }, 271 }, 272 }, 273 }, 274 }, 275 containsError: "key Config.ChannelGroup.Groups[Application].Groups is nil", 276 }, 277 { 278 name: "no Values in ChannelGroup", 279 ce: &common.ConfigEnvelope{ 280 Config: &common.Config{ 281 ChannelGroup: &common.ConfigGroup{ 282 Groups: map[string]*common.ConfigGroup{ 283 channelconfig.ApplicationGroupKey: { 284 Groups: map[string]*common.ConfigGroup{}, 285 }, 286 channelconfig.OrdererGroupKey: { 287 Groups: map[string]*common.ConfigGroup{}, 288 }, 289 }, 290 }, 291 }, 292 }, 293 containsError: "field Config.ChannelGroup.Values is nil", 294 }, 295 } 296 297 for _, test := range tests { 298 test := test 299 t.Run(test.name, func(t *testing.T) { 300 err := config.ValidateConfigEnvelope(test.ce) 301 assert.Contains(t, test.containsError, err.Error()) 302 }) 303 } 304 305 } 306 307 func TestOrdererEndpoints(t *testing.T) { 308 t.Run("Global endpoints", func(t *testing.T) { 309 block, err := test.MakeGenesisBlock("mychannel") 310 assert.NoError(t, err) 311 312 fakeBlockGetter := &mocks.ConfigBlockGetter{} 313 cs := config.NewDiscoverySupport(fakeBlockGetter) 314 315 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 316 317 injectGlobalOrdererEndpoint(t, block, "globalEndpoint:7050") 318 319 res, err := cs.Config("test") 320 assert.NoError(t, err) 321 assert.Equal(t, map[string]*discovery.Endpoints{ 322 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "globalEndpoint", Port: 7050}}}, 323 }, res.Orderers) 324 }) 325 326 t.Run("Per org endpoints alongside global endpoints", func(t *testing.T) { 327 block, err := test.MakeGenesisBlock("mychannel") 328 assert.NoError(t, err) 329 330 fakeBlockGetter := &mocks.ConfigBlockGetter{} 331 cs := config.NewDiscoverySupport(fakeBlockGetter) 332 333 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 334 335 injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "anotherOrg") 336 injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg") 337 338 res, err := cs.Config("test") 339 assert.NoError(t, err) 340 assert.Equal(t, map[string]*discovery.Endpoints{ 341 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "127.0.0.1", Port: 7050}}}, 342 "anotherOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}}, 343 "aBadOrg": {}, 344 }, res.Orderers) 345 }) 346 347 t.Run("Per org endpoints without global endpoints", func(t *testing.T) { 348 block, err := test.MakeGenesisBlock("mychannel") 349 assert.NoError(t, err) 350 351 fakeBlockGetter := &mocks.ConfigBlockGetter{} 352 cs := config.NewDiscoverySupport(fakeBlockGetter) 353 354 fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block) 355 356 removeGlobalEndpoints(t, block) 357 injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "SampleOrg") 358 injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg") 359 360 res, err := cs.Config("test") 361 assert.NoError(t, err) 362 assert.Equal(t, map[string]*discovery.Endpoints{ 363 "SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}}, 364 "aBadOrg": {}, 365 }, res.Orderers) 366 }) 367 } 368 369 func removeGlobalEndpoints(t *testing.T, block *common.Block) { 370 // Unwrap the layers until we reach the orderer addresses 371 env, err := protoutil.ExtractEnvelope(block, 0) 372 assert.NoError(t, err) 373 payload := protoutil.UnmarshalPayloadOrPanic(env.Payload) 374 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 375 assert.NoError(t, err) 376 // Remove the orderer addresses 377 delete(confEnv.Config.ChannelGroup.Values, channelconfig.OrdererAddressesKey) 378 // And put it back into the block 379 payload.Data = protoutil.MarshalOrPanic(confEnv) 380 env.Payload = protoutil.MarshalOrPanic(payload) 381 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 382 } 383 384 func injectGlobalOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) { 385 ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint}) 386 // Unwrap the layers until we reach the orderer addresses 387 env, err := protoutil.ExtractEnvelope(block, 0) 388 assert.NoError(t, err) 389 payload, err := protoutil.UnmarshalPayload(env.Payload) 390 assert.NoError(t, err) 391 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 392 assert.NoError(t, err) 393 // Replace the orderer addresses 394 confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()] = &common.ConfigValue{ 395 Value: protoutil.MarshalOrPanic(ordererAddresses.Value()), 396 ModPolicy: "/Channel/Orderer/Admins", 397 } 398 // Remove the per org addresses, if applicable 399 ordererGrps := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups 400 for _, grp := range ordererGrps { 401 if grp.Values[channelconfig.EndpointsKey] == nil { 402 continue 403 } 404 grp.Values[channelconfig.EndpointsKey].Value = nil 405 } 406 // And put it back into the block 407 payload.Data = protoutil.MarshalOrPanic(confEnv) 408 env.Payload = protoutil.MarshalOrPanic(payload) 409 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 410 } 411 412 func injectAdditionalEndpointPair(t *testing.T, block *common.Block, endpoint string, orgName string) { 413 // Unwrap the layers until we reach the orderer addresses 414 env, err := protoutil.ExtractEnvelope(block, 0) 415 assert.NoError(t, err) 416 payload, err := protoutil.UnmarshalPayload(env.Payload) 417 assert.NoError(t, err) 418 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 419 assert.NoError(t, err) 420 ordererGrp := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups 421 // Get the first orderer org config 422 var firstOrdererConfig *common.ConfigGroup 423 for _, grp := range ordererGrp { 424 firstOrdererConfig = grp 425 break 426 } 427 // Duplicate it. 428 secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup) 429 ordererGrp[orgName] = secondOrdererConfig 430 // Reach the FabricMSPConfig buried in it. 431 mspConfig := &msp.MSPConfig{} 432 err = proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig) 433 assert.NoError(t, err) 434 435 fabricConfig := &msp.FabricMSPConfig{} 436 err = proto.Unmarshal(mspConfig.Config, fabricConfig) 437 assert.NoError(t, err) 438 439 // Rename it. 440 fabricConfig.Name = orgName 441 442 // Pack the MSP config back into the config 443 secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{ 444 Config: protoutil.MarshalOrPanic(fabricConfig), 445 Type: mspConfig.Type, 446 }) 447 448 // Inject the endpoint 449 ordererOrgProtos := &common.OrdererAddresses{ 450 Addresses: []string{endpoint}, 451 } 452 secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos) 453 454 // Fold everything back into the block 455 payload.Data = protoutil.MarshalOrPanic(confEnv) 456 env.Payload = protoutil.MarshalOrPanic(payload) 457 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 458 }