github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/orderer/common/server/main_test.go (about) 1 // Copyright IBM Corp. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package server 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/golang/protobuf/proto" 20 "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/hyperledger/fabric/bccsp/factory" 22 "github.com/hyperledger/fabric/bccsp/sw" 23 "github.com/hyperledger/fabric/common/channelconfig" 24 "github.com/hyperledger/fabric/common/crypto/tlsgen" 25 deliver_mocks "github.com/hyperledger/fabric/common/deliver/mock" 26 "github.com/hyperledger/fabric/common/flogging" 27 "github.com/hyperledger/fabric/common/flogging/floggingtest" 28 "github.com/hyperledger/fabric/common/ledger/blockledger" 29 "github.com/hyperledger/fabric/common/ledger/blockledger/fileledger" 30 ledger_mocks "github.com/hyperledger/fabric/common/ledger/blockledger/mocks" 31 "github.com/hyperledger/fabric/common/metrics/disabled" 32 "github.com/hyperledger/fabric/common/metrics/prometheus" 33 "github.com/hyperledger/fabric/core/comm" 34 "github.com/hyperledger/fabric/core/config/configtest" 35 "github.com/hyperledger/fabric/internal/configtxgen/encoder" 36 "github.com/hyperledger/fabric/internal/configtxgen/genesisconfig" 37 "github.com/hyperledger/fabric/internal/pkg/identity" 38 "github.com/hyperledger/fabric/orderer/common/bootstrap/file" 39 "github.com/hyperledger/fabric/orderer/common/cluster" 40 "github.com/hyperledger/fabric/orderer/common/localconfig" 41 "github.com/hyperledger/fabric/orderer/common/multichannel" 42 server_mocks "github.com/hyperledger/fabric/orderer/common/server/mocks" 43 "github.com/hyperledger/fabric/orderer/consensus" 44 "github.com/hyperledger/fabric/protoutil" 45 . "github.com/onsi/gomega" 46 "github.com/onsi/gomega/gexec" 47 "github.com/pkg/errors" 48 "github.com/stretchr/testify/assert" 49 "github.com/stretchr/testify/mock" 50 "github.com/stretchr/testify/require" 51 "go.uber.org/zap" 52 "go.uber.org/zap/zapcore" 53 ) 54 55 //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer 56 57 type signerSerializer interface { 58 identity.SignerSerializer 59 } 60 61 func TestInitializeLogging(t *testing.T) { 62 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 63 os.Setenv("FABRIC_LOGGING_SPEC", "foo=debug") 64 initializeLogging() 65 assert.Equal(t, "debug", flogging.LoggerLevel("foo")) 66 os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 67 } 68 69 func TestInitializeProfilingService(t *testing.T) { 70 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 71 defer os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 72 os.Setenv("FABRIC_LOGGING_SPEC", "debug") 73 // get a free random port 74 listenAddr := func() string { 75 l, _ := net.Listen("tcp", "localhost:0") 76 l.Close() 77 return l.Addr().String() 78 }() 79 go initializeProfilingService( 80 &localconfig.TopLevel{ 81 General: localconfig.General{ 82 Profile: localconfig.Profile{ 83 Enabled: true, 84 Address: listenAddr, 85 }}, 86 Kafka: localconfig.Kafka{Verbose: true}, 87 }, 88 ) 89 time.Sleep(500 * time.Millisecond) 90 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 91 t.Logf("Expected pprof to be up (will retry again in 3 seconds): %s", err) 92 time.Sleep(3 * time.Second) 93 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 94 t.Fatalf("Expected pprof to be up: %s", err) 95 } 96 } 97 } 98 99 func TestInitializeServerConfig(t *testing.T) { 100 conf := &localconfig.TopLevel{ 101 General: localconfig.General{ 102 ConnectionTimeout: 7 * time.Second, 103 TLS: localconfig.TLS{ 104 Enabled: true, 105 ClientAuthRequired: true, 106 Certificate: "main.go", 107 PrivateKey: "main.go", 108 RootCAs: []string{"main.go"}, 109 ClientRootCAs: []string{"main.go"}, 110 }, 111 }, 112 } 113 sc := initializeServerConfig(conf, nil) 114 expectedContent, _ := ioutil.ReadFile("main.go") 115 assert.Equal(t, expectedContent, sc.SecOpts.Certificate) 116 assert.Equal(t, expectedContent, sc.SecOpts.Key) 117 assert.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ServerRootCAs) 118 assert.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ClientRootCAs) 119 120 sc = initializeServerConfig(conf, nil) 121 defaultOpts := comm.DefaultKeepaliveOptions 122 assert.Equal(t, defaultOpts.ServerMinInterval, sc.KaOpts.ServerMinInterval) 123 assert.Equal(t, time.Duration(0), sc.KaOpts.ServerInterval) 124 assert.Equal(t, time.Duration(0), sc.KaOpts.ServerTimeout) 125 assert.Equal(t, 7*time.Second, sc.ConnectionTimeout) 126 testDuration := 10 * time.Second 127 conf.General.Keepalive = localconfig.Keepalive{ 128 ServerMinInterval: testDuration, 129 ServerInterval: testDuration, 130 ServerTimeout: testDuration, 131 } 132 sc = initializeServerConfig(conf, nil) 133 assert.Equal(t, testDuration, sc.KaOpts.ServerMinInterval) 134 assert.Equal(t, testDuration, sc.KaOpts.ServerInterval) 135 assert.Equal(t, testDuration, sc.KaOpts.ServerTimeout) 136 137 sc = initializeServerConfig(conf, nil) 138 assert.NotNil(t, sc.Logger) 139 assert.Equal(t, comm.NewServerStatsHandler(&disabled.Provider{}), sc.ServerStatsHandler) 140 assert.Len(t, sc.UnaryInterceptors, 2) 141 assert.Len(t, sc.StreamInterceptors, 2) 142 143 sc = initializeServerConfig(conf, &prometheus.Provider{}) 144 assert.NotNil(t, sc.ServerStatsHandler) 145 146 goodFile := "main.go" 147 badFile := "does_not_exist" 148 149 oldLogger := logger 150 defer func() { logger = oldLogger }() 151 logger, _ = floggingtest.NewTestLogger(t) 152 153 testCases := []struct { 154 name string 155 certificate string 156 privateKey string 157 rootCA string 158 clientRootCert string 159 clusterCert string 160 clusterKey string 161 clusterCA string 162 }{ 163 {"BadCertificate", badFile, goodFile, goodFile, goodFile, "", "", ""}, 164 {"BadPrivateKey", goodFile, badFile, goodFile, goodFile, "", "", ""}, 165 {"BadRootCA", goodFile, goodFile, badFile, goodFile, "", "", ""}, 166 {"BadClientRootCertificate", goodFile, goodFile, goodFile, badFile, "", "", ""}, 167 {"ClusterBadCertificate", goodFile, goodFile, goodFile, goodFile, badFile, goodFile, goodFile}, 168 {"ClusterBadPrivateKey", goodFile, goodFile, goodFile, goodFile, goodFile, badFile, goodFile}, 169 {"ClusterBadRootCA", goodFile, goodFile, goodFile, goodFile, goodFile, goodFile, badFile}, 170 } 171 for _, tc := range testCases { 172 t.Run(tc.name, func(t *testing.T) { 173 conf := &localconfig.TopLevel{ 174 General: localconfig.General{ 175 TLS: localconfig.TLS{ 176 Enabled: true, 177 ClientAuthRequired: true, 178 Certificate: tc.certificate, 179 PrivateKey: tc.privateKey, 180 RootCAs: []string{tc.rootCA}, 181 ClientRootCAs: []string{tc.clientRootCert}, 182 }, 183 Cluster: localconfig.Cluster{ 184 ClientCertificate: tc.clusterCert, 185 ClientPrivateKey: tc.clusterKey, 186 RootCAs: []string{tc.clusterCA}, 187 }, 188 }, 189 } 190 assert.Panics(t, func() { 191 if tc.clusterCert == "" { 192 initializeServerConfig(conf, nil) 193 } else { 194 initializeClusterClientConfig(conf) 195 } 196 }, 197 ) 198 }) 199 } 200 } 201 202 func TestInitializeBootstrapChannel(t *testing.T) { 203 cleanup := configtest.SetDevFabricConfigPath(t) 204 defer cleanup() 205 206 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 207 defer os.Remove(genesisFile) 208 209 fileLedgerLocation, _ := ioutil.TempDir("", "main_test-") 210 defer os.RemoveAll(fileLedgerLocation) 211 212 ledgerFactory, _, err := createLedgerFactory( 213 &localconfig.TopLevel{ 214 FileLedger: localconfig.FileLedger{ 215 Location: fileLedgerLocation, 216 }, 217 }, 218 &disabled.Provider{}, 219 ) 220 assert.NoError(t, err) 221 bootstrapConfig := &localconfig.TopLevel{ 222 General: localconfig.General{ 223 BootstrapMethod: "file", 224 BootstrapFile: genesisFile, 225 }, 226 } 227 228 bootstrapBlock := extractBootstrapBlock(bootstrapConfig) 229 initializeBootstrapChannel(bootstrapBlock, ledgerFactory) 230 231 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 232 assert.NoError(t, err) 233 assert.Equal(t, uint64(1), ledger.Height()) 234 } 235 236 func TestExtractBootstrapBlock(t *testing.T) { 237 cleanup := configtest.SetDevFabricConfigPath(t) 238 defer cleanup() 239 240 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 241 defer os.Remove(genesisFile) 242 243 tests := []struct { 244 config *localconfig.TopLevel 245 block *common.Block 246 }{ 247 { 248 config: &localconfig.TopLevel{ 249 General: localconfig.General{BootstrapMethod: "file", BootstrapFile: genesisFile}, 250 }, 251 block: file.New(genesisFile).GenesisBlock(), 252 }, 253 { 254 config: &localconfig.TopLevel{ 255 General: localconfig.General{BootstrapMethod: "none"}, 256 }, 257 block: nil, 258 }, 259 } 260 for _, tt := range tests { 261 b := extractBootstrapBlock(tt.config) 262 assert.Truef(t, proto.Equal(tt.block, b), "wanted %v, got %v", tt.block, b) 263 } 264 } 265 266 func TestExtractSysChanLastConfig(t *testing.T) { 267 tmpdir, err := ioutil.TempDir("", "main_test-") 268 require.NoError(t, err) 269 defer os.RemoveAll(tmpdir) 270 271 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 272 require.NoError(t, err) 273 274 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 275 genesisBlock := encoder.New(conf).GenesisBlock() 276 277 lastConf := extractSysChanLastConfig(rlf, genesisBlock) 278 assert.Nil(t, lastConf) 279 280 rl, err := rlf.GetOrCreate("testchannelid") 281 require.NoError(t, err) 282 283 err = rl.Append(genesisBlock) 284 require.NoError(t, err) 285 286 lastConf = extractSysChanLastConfig(rlf, genesisBlock) 287 assert.NotNil(t, lastConf) 288 assert.Equal(t, uint64(0), lastConf.Header.Number) 289 290 assert.Panics(t, func() { 291 _ = extractSysChanLastConfig(rlf, nil) 292 }) 293 294 configTx, err := protoutil.CreateSignedEnvelope(common.HeaderType_CONFIG, "testchannelid", nil, &common.ConfigEnvelope{}, 0, 0) 295 require.NoError(t, err) 296 297 nextBlock := blockledger.CreateNextBlock(rl, []*common.Envelope{configTx}) 298 nextBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ 299 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}), 300 }) 301 err = rl.Append(nextBlock) 302 require.NoError(t, err) 303 304 lastConf = extractSysChanLastConfig(rlf, genesisBlock) 305 assert.NotNil(t, lastConf) 306 assert.Equal(t, uint64(1), lastConf.Header.Number) 307 } 308 309 func TestSelectClusterBootBlock(t *testing.T) { 310 bootstrapBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 311 lastConfBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 312 313 clusterBoot := selectClusterBootBlock(bootstrapBlock, nil) 314 assert.NotNil(t, clusterBoot) 315 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 316 assert.True(t, bootstrapBlock == clusterBoot) 317 318 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 319 assert.NotNil(t, clusterBoot) 320 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 321 assert.True(t, bootstrapBlock == clusterBoot) 322 323 lastConfBlock.Header.Number = 200 324 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 325 assert.NotNil(t, clusterBoot) 326 assert.Equal(t, uint64(200), clusterBoot.Header.Number) 327 assert.True(t, lastConfBlock == clusterBoot) 328 329 bootstrapBlock.Header.Number = 300 330 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 331 assert.NotNil(t, clusterBoot) 332 assert.Equal(t, uint64(300), clusterBoot.Header.Number) 333 assert.True(t, bootstrapBlock == clusterBoot) 334 } 335 336 func TestLoadLocalMSP(t *testing.T) { 337 t.Run("Happy", func(t *testing.T) { 338 localMSPDir := configtest.GetDevMspDir() 339 localMSP := loadLocalMSP( 340 &localconfig.TopLevel{ 341 General: localconfig.General{ 342 LocalMSPDir: localMSPDir, 343 LocalMSPID: "SampleOrg", 344 BCCSP: &factory.FactoryOpts{ 345 ProviderName: "SW", 346 SwOpts: &factory.SwOpts{ 347 HashFamily: "SHA2", 348 SecLevel: 256, 349 Ephemeral: true, 350 }, 351 }, 352 }, 353 }, 354 ) 355 require.NotNil(t, localMSP) 356 id, err := localMSP.GetIdentifier() 357 require.NoError(t, err) 358 require.Equal(t, id, "SampleOrg") 359 }) 360 361 t.Run("Error", func(t *testing.T) { 362 oldLogger := logger 363 defer func() { logger = oldLogger }() 364 logger, _ = floggingtest.NewTestLogger(t) 365 366 assert.Panics(t, func() { 367 loadLocalMSP( 368 &localconfig.TopLevel{ 369 General: localconfig.General{ 370 LocalMSPDir: "", 371 LocalMSPID: "", 372 }, 373 }, 374 ) 375 }) 376 }) 377 } 378 379 func TestInitializeMultichannelRegistrar(t *testing.T) { 380 cleanup := configtest.SetDevFabricConfigPath(t) 381 defer cleanup() 382 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 383 defer os.Remove(genesisFile) 384 385 conf := genesisConfig(t, genesisFile) 386 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 387 assert.NoError(t, err) 388 389 signer := &server_mocks.SignerSerializer{} 390 391 t.Run("registrar with a system channel", func(t *testing.T) { 392 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 393 assert.NoError(t, err) 394 bootBlock := file.New(genesisFile).GenesisBlock() 395 initializeBootstrapChannel(bootBlock, lf) 396 registrar := initializeMultichannelRegistrar( 397 bootBlock, 398 &replicationInitiator{cryptoProvider: cryptoProvider}, 399 &cluster.PredicateDialer{}, 400 comm.ServerConfig{}, 401 nil, 402 conf, 403 signer, 404 &disabled.Provider{}, 405 &server_mocks.HealthChecker{}, 406 lf, 407 cryptoProvider, 408 ) 409 assert.NotNil(t, registrar) 410 assert.Equal(t, "testchannelid", registrar.SystemChannelID()) 411 }) 412 413 t.Run("registrar without a system channel", func(t *testing.T) { 414 conf.General.BootstrapMethod = "none" 415 conf.General.GenesisFile = "" 416 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 417 assert.NoError(t, err) 418 registrar := initializeMultichannelRegistrar( 419 nil, 420 &replicationInitiator{cryptoProvider: cryptoProvider}, 421 &cluster.PredicateDialer{}, 422 comm.ServerConfig{}, 423 nil, 424 conf, 425 signer, 426 &disabled.Provider{}, 427 &server_mocks.HealthChecker{}, 428 lf, 429 cryptoProvider, 430 ) 431 assert.NotNil(t, registrar) 432 assert.Empty(t, registrar.SystemChannelID()) 433 }) 434 } 435 436 func TestInitializeGrpcServer(t *testing.T) { 437 // get a free random port 438 listenAddr := func() string { 439 l, _ := net.Listen("tcp", "localhost:0") 440 l.Close() 441 return l.Addr().String() 442 }() 443 host := strings.Split(listenAddr, ":")[0] 444 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 445 conf := &localconfig.TopLevel{ 446 General: localconfig.General{ 447 ListenAddress: host, 448 ListenPort: uint16(port), 449 TLS: localconfig.TLS{ 450 Enabled: false, 451 ClientAuthRequired: false, 452 }, 453 }, 454 } 455 assert.NotPanics(t, func() { 456 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 457 grpcServer.Listener().Close() 458 }) 459 } 460 461 // generateCryptoMaterials uses cryptogen to generate the necessary 462 // MSP files and TLS certificates 463 func generateCryptoMaterials(t *testing.T, cryptogen string) string { 464 gt := NewGomegaWithT(t) 465 cryptoPath := filepath.Join(tempDir, "crypto") 466 467 cmd := exec.Command( 468 cryptogen, 469 "generate", 470 "--config", filepath.Join(tempDir, "examplecom-config.yaml"), 471 "--output", cryptoPath, 472 ) 473 cryptogenProcess, err := gexec.Start(cmd, nil, nil) 474 gt.Expect(err).NotTo(HaveOccurred()) 475 gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0)) 476 477 return cryptoPath 478 } 479 480 func TestUpdateTrustedRoots(t *testing.T) { 481 cleanup := configtest.SetDevFabricConfigPath(t) 482 defer cleanup() 483 484 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 485 defer os.Remove(genesisFile) 486 487 cryptoPath := generateCryptoMaterials(t, cryptogen) 488 defer os.RemoveAll(cryptoPath) 489 490 // get a free random port 491 listenAddr := func() string { 492 l, _ := net.Listen("tcp", "localhost:0") 493 l.Close() 494 return l.Addr().String() 495 }() 496 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 497 conf := &localconfig.TopLevel{ 498 General: localconfig.General{ 499 BootstrapMethod: "file", 500 BootstrapFile: genesisFile, 501 ListenAddress: "localhost", 502 ListenPort: uint16(port), 503 TLS: localconfig.TLS{ 504 Enabled: false, 505 ClientAuthRequired: false, 506 }, 507 }, 508 } 509 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 510 caMgr := &caManager{ 511 appRootCAsByChain: make(map[string][][]byte), 512 ordererRootCAsByChain: make(map[string][][]byte), 513 } 514 callback := func(bundle *channelconfig.Bundle) { 515 if grpcServer.MutualTLSRequired() { 516 t.Log("callback called") 517 caMgr.updateTrustedRoots(bundle, grpcServer) 518 } 519 } 520 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 521 assert.NoError(t, err) 522 bootBlock := file.New(genesisFile).GenesisBlock() 523 initializeBootstrapChannel(bootBlock, lf) 524 signer := &server_mocks.SignerSerializer{} 525 526 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 527 assert.NoError(t, err) 528 529 initializeMultichannelRegistrar( 530 bootBlock, 531 &replicationInitiator{cryptoProvider: cryptoProvider}, 532 &cluster.PredicateDialer{}, 533 comm.ServerConfig{}, 534 nil, 535 genesisConfig(t, genesisFile), 536 signer, 537 &disabled.Provider{}, 538 &server_mocks.HealthChecker{}, 539 lf, 540 cryptoProvider, 541 callback, 542 ) 543 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 544 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 545 // mutual TLS not required so no updates should have occurred 546 assert.Equal(t, 0, len(caMgr.appRootCAsByChain["testchannelid"])) 547 assert.Equal(t, 0, len(caMgr.ordererRootCAsByChain["testchannelid"])) 548 grpcServer.Listener().Close() 549 550 conf = &localconfig.TopLevel{ 551 General: localconfig.General{ 552 ListenAddress: "localhost", 553 ListenPort: uint16(port), 554 TLS: localconfig.TLS{ 555 Enabled: true, 556 ClientAuthRequired: true, 557 PrivateKey: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.key"), 558 Certificate: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.crt"), 559 }, 560 }, 561 } 562 grpcServer = initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 563 caMgr = &caManager{ 564 appRootCAsByChain: make(map[string][][]byte), 565 ordererRootCAsByChain: make(map[string][][]byte), 566 } 567 568 clusterConf := initializeClusterClientConfig(conf) 569 predDialer := &cluster.PredicateDialer{ 570 Config: clusterConf, 571 } 572 573 callback = func(bundle *channelconfig.Bundle) { 574 if grpcServer.MutualTLSRequired() { 575 t.Log("callback called") 576 caMgr.updateTrustedRoots(bundle, grpcServer) 577 caMgr.updateClusterDialer(predDialer, clusterConf.SecOpts.ServerRootCAs) 578 } 579 } 580 initializeMultichannelRegistrar( 581 bootBlock, 582 &replicationInitiator{cryptoProvider: cryptoProvider}, 583 predDialer, 584 comm.ServerConfig{}, 585 nil, 586 genesisConfig(t, genesisFile), 587 signer, 588 &disabled.Provider{}, 589 &server_mocks.HealthChecker{}, 590 lf, 591 cryptoProvider, 592 callback, 593 ) 594 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 595 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 596 // mutual TLS is required so updates should have occurred 597 // we expect an intermediate and root CA for apps and orderers 598 assert.Equal(t, 2, len(caMgr.appRootCAsByChain["testchannelid"])) 599 assert.Equal(t, 2, len(caMgr.ordererRootCAsByChain["testchannelid"])) 600 assert.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 601 grpcServer.Listener().Close() 602 } 603 604 func TestConfigureClusterListener(t *testing.T) { 605 logEntries := make(chan string, 100) 606 607 allocatePort := func() uint16 { 608 l, err := net.Listen("tcp", "127.0.0.1:0") 609 assert.NoError(t, err) 610 _, portStr, err := net.SplitHostPort(l.Addr().String()) 611 assert.NoError(t, err) 612 port, err := strconv.ParseInt(portStr, 10, 64) 613 assert.NoError(t, err) 614 assert.NoError(t, l.Close()) 615 t.Log("picked unused port", port) 616 return uint16(port) 617 } 618 619 unUsedPort := allocatePort() 620 621 backupLogger := logger 622 logger = logger.With(zap.Hooks(func(entry zapcore.Entry) error { 623 logEntries <- entry.Message 624 return nil 625 })) 626 627 defer func() { 628 logger = backupLogger 629 }() 630 631 ca, err := tlsgen.NewCA() 632 assert.NoError(t, err) 633 serverKeyPair, err := ca.NewServerCertKeyPair("127.0.0.1") 634 assert.NoError(t, err) 635 636 loadPEM := func(fileName string) ([]byte, error) { 637 switch fileName { 638 case "cert": 639 return serverKeyPair.Cert, nil 640 case "key": 641 return serverKeyPair.Key, nil 642 case "ca": 643 return ca.CertBytes(), nil 644 default: 645 return nil, errors.New("I/O error") 646 } 647 } 648 649 for _, testCase := range []struct { 650 name string 651 conf *localconfig.TopLevel 652 generalConf comm.ServerConfig 653 generalSrv *comm.GRPCServer 654 shouldBeEqual bool 655 expectedPanic string 656 expectedLogEntries []string 657 }{ 658 { 659 name: "invalid certificate", 660 generalConf: comm.ServerConfig{}, 661 conf: &localconfig.TopLevel{ 662 General: localconfig.General{ 663 Cluster: localconfig.Cluster{ 664 ListenAddress: "127.0.0.1", 665 ListenPort: 5000, 666 ServerPrivateKey: "key", 667 ServerCertificate: "bad", 668 RootCAs: []string{"ca"}, 669 }, 670 }, 671 }, 672 expectedPanic: "Failed to load cluster server certificate from 'bad' (I/O error)", 673 generalSrv: &comm.GRPCServer{}, 674 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 675 }, 676 { 677 name: "invalid key", 678 generalConf: comm.ServerConfig{}, 679 conf: &localconfig.TopLevel{ 680 General: localconfig.General{ 681 Cluster: localconfig.Cluster{ 682 ListenAddress: "127.0.0.1", 683 ListenPort: 5000, 684 ServerPrivateKey: "bad", 685 ServerCertificate: "cert", 686 RootCAs: []string{"ca"}, 687 }, 688 }, 689 }, 690 expectedPanic: "Failed to load cluster server key from 'bad' (I/O error)", 691 generalSrv: &comm.GRPCServer{}, 692 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 693 }, 694 { 695 name: "invalid ca cert", 696 generalConf: comm.ServerConfig{}, 697 conf: &localconfig.TopLevel{ 698 General: localconfig.General{ 699 Cluster: localconfig.Cluster{ 700 ListenAddress: "127.0.0.1", 701 ListenPort: 5000, 702 ServerPrivateKey: "key", 703 ServerCertificate: "cert", 704 RootCAs: []string{"bad"}, 705 }, 706 }, 707 }, 708 expectedPanic: "Failed to load CA cert file 'bad' (I/O error)", 709 generalSrv: &comm.GRPCServer{}, 710 expectedLogEntries: []string{"Failed to load CA cert file 'bad' (I/O error)"}, 711 }, 712 { 713 name: "bad listen address", 714 generalConf: comm.ServerConfig{}, 715 conf: &localconfig.TopLevel{ 716 General: localconfig.General{ 717 Cluster: localconfig.Cluster{ 718 ListenAddress: "99.99.99.99", 719 ListenPort: unUsedPort, 720 ServerPrivateKey: "key", 721 ServerCertificate: "cert", 722 RootCAs: []string{"ca"}, 723 }, 724 }, 725 }, 726 expectedPanic: fmt.Sprintf("Failed creating gRPC server on 99.99.99.99:%d due "+ 727 "to listen tcp 99.99.99.99:%d:", unUsedPort, unUsedPort), 728 generalSrv: &comm.GRPCServer{}, 729 }, 730 { 731 name: "green path", 732 generalConf: comm.ServerConfig{}, 733 conf: &localconfig.TopLevel{ 734 General: localconfig.General{ 735 Cluster: localconfig.Cluster{ 736 ListenAddress: "127.0.0.1", 737 ListenPort: 5000, 738 ServerPrivateKey: "key", 739 ServerCertificate: "cert", 740 RootCAs: []string{"ca"}, 741 }, 742 }, 743 }, 744 generalSrv: &comm.GRPCServer{}, 745 }, 746 } { 747 t.Run(testCase.name, func(t *testing.T) { 748 if testCase.shouldBeEqual { 749 conf, srv := configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 750 assert.Equal(t, conf, testCase.generalConf) 751 assert.Equal(t, srv, testCase.generalSrv) 752 } 753 754 if testCase.expectedPanic != "" { 755 f := func() { 756 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 757 } 758 assert.Contains(t, panicMsg(f), testCase.expectedPanic) 759 } else { 760 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 761 } 762 // Ensure logged messages that are expected were all logged 763 var loggedMessages []string 764 for len(logEntries) > 0 { 765 logEntry := <-logEntries 766 loggedMessages = append(loggedMessages, logEntry) 767 } 768 assert.Subset(t, testCase.expectedLogEntries, loggedMessages) 769 }) 770 } 771 } 772 773 func TestReuseListener(t *testing.T) { 774 t.Run("good to reuse", func(t *testing.T) { 775 top := &localconfig.TopLevel{General: localconfig.General{TLS: localconfig.TLS{Enabled: true}}} 776 require.True(t, reuseListener(top, "foo")) 777 }) 778 779 t.Run("reuse tls disabled", func(t *testing.T) { 780 top := &localconfig.TopLevel{} 781 require.PanicsWithValue( 782 t, 783 "TLS is required for running ordering nodes of type foo.", 784 func() { reuseListener(top, "foo") }, 785 ) 786 }) 787 788 t.Run("good not to reuse", func(t *testing.T) { 789 top := &localconfig.TopLevel{ 790 General: localconfig.General{ 791 Cluster: localconfig.Cluster{ 792 ListenAddress: "127.0.0.1", 793 ListenPort: 5000, 794 ServerPrivateKey: "key", 795 ServerCertificate: "bad", 796 }, 797 }, 798 } 799 require.False(t, reuseListener(top, "foo")) 800 }) 801 802 t.Run("partial config", func(t *testing.T) { 803 top := &localconfig.TopLevel{ 804 General: localconfig.General{ 805 Cluster: localconfig.Cluster{ 806 ListenAddress: "127.0.0.1", 807 ListenPort: 5000, 808 ServerCertificate: "bad", 809 }, 810 }, 811 } 812 require.PanicsWithValue( 813 t, 814 "Options: General.Cluster.ListenPort, General.Cluster.ListenAddress,"+ 815 " General.Cluster.ServerCertificate, General.Cluster.ServerPrivateKey, should be defined altogether.", 816 func() { reuseListener(top, "foo") }, 817 ) 818 }) 819 } 820 821 func TestInitializeEtcdraftConsenter(t *testing.T) { 822 consenters := make(map[string]consensus.Consenter) 823 824 tmpdir, err := ioutil.TempDir("", "main_test-") 825 require.NoError(t, err) 826 defer os.RemoveAll(tmpdir) 827 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 828 require.NoError(t, err) 829 830 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 831 genesisBlock := encoder.New(conf).GenesisBlock() 832 833 ca, _ := tlsgen.NewCA() 834 crt, _ := ca.NewServerCertKeyPair("127.0.0.1") 835 836 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 837 assert.NoError(t, err) 838 839 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 840 assert.NoError(t, err) 841 842 initializeEtcdraftConsenter(consenters, 843 &localconfig.TopLevel{}, 844 rlf, 845 &cluster.PredicateDialer{}, 846 genesisBlock, &replicationInitiator{cryptoProvider: cryptoProvider}, 847 comm.ServerConfig{ 848 SecOpts: comm.SecureOptions{ 849 Certificate: crt.Cert, 850 Key: crt.Key, 851 UseTLS: true, 852 }, 853 }, 854 srv, 855 &multichannel.Registrar{}, 856 &disabled.Provider{}, 857 cryptoProvider, 858 ) 859 assert.NotNil(t, consenters["etcdraft"]) 860 } 861 862 func genesisConfig(t *testing.T, genesisFile string) *localconfig.TopLevel { 863 t.Helper() 864 localMSPDir := configtest.GetDevMspDir() 865 return &localconfig.TopLevel{ 866 General: localconfig.General{ 867 BootstrapMethod: "file", 868 BootstrapFile: genesisFile, 869 LocalMSPDir: localMSPDir, 870 LocalMSPID: "SampleOrg", 871 BCCSP: &factory.FactoryOpts{ 872 ProviderName: "SW", 873 SwOpts: &factory.SwOpts{ 874 HashFamily: "SHA2", 875 SecLevel: 256, 876 Ephemeral: true, 877 }, 878 }, 879 }, 880 } 881 } 882 883 func panicMsg(f func()) string { 884 var message interface{} 885 func() { 886 887 defer func() { 888 message = recover() 889 }() 890 891 f() 892 893 }() 894 895 return message.(string) 896 897 } 898 899 func TestCreateReplicator(t *testing.T) { 900 cleanup := configtest.SetDevFabricConfigPath(t) 901 defer cleanup() 902 bootBlock := encoder.New(genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile)).GenesisBlockForChannel("system") 903 904 iterator := &deliver_mocks.BlockIterator{} 905 iterator.NextReturnsOnCall(0, bootBlock, common.Status_SUCCESS) 906 iterator.NextReturnsOnCall(1, bootBlock, common.Status_SUCCESS) 907 908 ledger := &ledger_mocks.ReadWriter{} 909 ledger.On("Height").Return(uint64(1)) 910 ledger.On("Iterator", mock.Anything).Return(iterator, uint64(1)) 911 912 ledgerFactory := &server_mocks.Factory{} 913 ledgerFactory.On("GetOrCreate", "mychannel").Return(ledger, nil) 914 ledgerFactory.On("ChannelIDs").Return([]string{"mychannel"}) 915 916 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 917 assert.NoError(t, err) 918 919 signer := &server_mocks.SignerSerializer{} 920 r := createReplicator(ledgerFactory, bootBlock, &localconfig.TopLevel{}, comm.SecureOptions{}, signer, cryptoProvider) 921 922 err = r.verifierRetriever.RetrieveVerifier("mychannel").VerifyBlockSignature(nil, nil) 923 assert.EqualError(t, err, "implicit policy evaluation failed - 0 sub-policies were satisfied, but this policy requires 1 of the 'Writers' sub-policies to be satisfied") 924 925 err = r.verifierRetriever.RetrieveVerifier("system").VerifyBlockSignature(nil, nil) 926 assert.NoError(t, err) 927 } 928 929 func produceGenesisFile(t *testing.T, profile, channelID string) string { 930 conf := genesisconfig.Load(profile, configtest.GetDevConfigDir()) 931 f, err := ioutil.TempFile("", fmt.Sprintf("%s-genesis_block-", t.Name())) 932 require.NoError(t, err) 933 _, err = f.Write(protoutil.MarshalOrPanic(encoder.New(conf).GenesisBlockForChannel(channelID))) 934 require.NoError(t, err) 935 err = f.Close() 936 require.NoError(t, err) 937 return f.Name() 938 }