github.com/kaituanwang/hyperledger@v2.0.1+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_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ 299 Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{ 300 LastConfig: &common.LastConfig{Index: rl.Height()}, 301 }), 302 }) 303 nextBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ 304 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}), 305 }) 306 err = rl.Append(nextBlock) 307 require.NoError(t, err) 308 309 lastConf = extractSysChanLastConfig(rlf, genesisBlock) 310 assert.NotNil(t, lastConf) 311 assert.Equal(t, uint64(1), lastConf.Header.Number) 312 } 313 314 func TestSelectClusterBootBlock(t *testing.T) { 315 bootstrapBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 316 lastConfBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 317 318 clusterBoot := selectClusterBootBlock(bootstrapBlock, nil) 319 assert.NotNil(t, clusterBoot) 320 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 321 assert.True(t, bootstrapBlock == clusterBoot) 322 323 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 324 assert.NotNil(t, clusterBoot) 325 assert.Equal(t, uint64(100), clusterBoot.Header.Number) 326 assert.True(t, bootstrapBlock == clusterBoot) 327 328 lastConfBlock.Header.Number = 200 329 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 330 assert.NotNil(t, clusterBoot) 331 assert.Equal(t, uint64(200), clusterBoot.Header.Number) 332 assert.True(t, lastConfBlock == clusterBoot) 333 334 bootstrapBlock.Header.Number = 300 335 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 336 assert.NotNil(t, clusterBoot) 337 assert.Equal(t, uint64(300), clusterBoot.Header.Number) 338 assert.True(t, bootstrapBlock == clusterBoot) 339 } 340 341 func TestLoadLocalMSP(t *testing.T) { 342 t.Run("Happy", func(t *testing.T) { 343 localMSPDir := configtest.GetDevMspDir() 344 localMSP := loadLocalMSP( 345 &localconfig.TopLevel{ 346 General: localconfig.General{ 347 LocalMSPDir: localMSPDir, 348 LocalMSPID: "SampleOrg", 349 BCCSP: &factory.FactoryOpts{ 350 ProviderName: "SW", 351 SwOpts: &factory.SwOpts{ 352 HashFamily: "SHA2", 353 SecLevel: 256, 354 Ephemeral: true, 355 }, 356 }, 357 }, 358 }, 359 ) 360 require.NotNil(t, localMSP) 361 id, err := localMSP.GetIdentifier() 362 require.NoError(t, err) 363 require.Equal(t, id, "SampleOrg") 364 }) 365 366 t.Run("Error", func(t *testing.T) { 367 oldLogger := logger 368 defer func() { logger = oldLogger }() 369 logger, _ = floggingtest.NewTestLogger(t) 370 371 assert.Panics(t, func() { 372 loadLocalMSP( 373 &localconfig.TopLevel{ 374 General: localconfig.General{ 375 LocalMSPDir: "", 376 LocalMSPID: "", 377 }, 378 }, 379 ) 380 }) 381 }) 382 } 383 384 func TestInitializeMultichannelRegistrar(t *testing.T) { 385 cleanup := configtest.SetDevFabricConfigPath(t) 386 defer cleanup() 387 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 388 defer os.Remove(genesisFile) 389 390 conf := genesisConfig(t, genesisFile) 391 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 392 assert.NoError(t, err) 393 394 signer := &server_mocks.SignerSerializer{} 395 396 t.Run("registrar with a system channel", func(t *testing.T) { 397 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 398 assert.NoError(t, err) 399 bootBlock := file.New(genesisFile).GenesisBlock() 400 initializeBootstrapChannel(bootBlock, lf) 401 registrar := initializeMultichannelRegistrar( 402 bootBlock, 403 &replicationInitiator{cryptoProvider: cryptoProvider}, 404 &cluster.PredicateDialer{}, 405 comm.ServerConfig{}, 406 nil, 407 conf, 408 signer, 409 &disabled.Provider{}, 410 &server_mocks.HealthChecker{}, 411 lf, 412 cryptoProvider, 413 ) 414 assert.NotNil(t, registrar) 415 assert.Equal(t, "testchannelid", registrar.SystemChannelID()) 416 }) 417 418 t.Run("registrar without a system channel", func(t *testing.T) { 419 conf.General.BootstrapMethod = "none" 420 conf.General.GenesisFile = "" 421 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 422 assert.NoError(t, err) 423 registrar := initializeMultichannelRegistrar( 424 nil, 425 &replicationInitiator{cryptoProvider: cryptoProvider}, 426 &cluster.PredicateDialer{}, 427 comm.ServerConfig{}, 428 nil, 429 conf, 430 signer, 431 &disabled.Provider{}, 432 &server_mocks.HealthChecker{}, 433 lf, 434 cryptoProvider, 435 ) 436 assert.NotNil(t, registrar) 437 assert.Empty(t, registrar.SystemChannelID()) 438 }) 439 } 440 441 func TestInitializeGrpcServer(t *testing.T) { 442 // get a free random port 443 listenAddr := func() string { 444 l, _ := net.Listen("tcp", "localhost:0") 445 l.Close() 446 return l.Addr().String() 447 }() 448 host := strings.Split(listenAddr, ":")[0] 449 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 450 conf := &localconfig.TopLevel{ 451 General: localconfig.General{ 452 ListenAddress: host, 453 ListenPort: uint16(port), 454 TLS: localconfig.TLS{ 455 Enabled: false, 456 ClientAuthRequired: false, 457 }, 458 }, 459 } 460 assert.NotPanics(t, func() { 461 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 462 grpcServer.Listener().Close() 463 }) 464 } 465 466 // generateCryptoMaterials uses cryptogen to generate the necessary 467 // MSP files and TLS certificates 468 func generateCryptoMaterials(t *testing.T, cryptogen string) string { 469 gt := NewGomegaWithT(t) 470 cryptoPath := filepath.Join(tempDir, "crypto") 471 472 cmd := exec.Command( 473 cryptogen, 474 "generate", 475 "--config", filepath.Join(tempDir, "examplecom-config.yaml"), 476 "--output", cryptoPath, 477 ) 478 cryptogenProcess, err := gexec.Start(cmd, nil, nil) 479 gt.Expect(err).NotTo(HaveOccurred()) 480 gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0)) 481 482 return cryptoPath 483 } 484 485 func TestUpdateTrustedRoots(t *testing.T) { 486 cleanup := configtest.SetDevFabricConfigPath(t) 487 defer cleanup() 488 489 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 490 defer os.Remove(genesisFile) 491 492 cryptoPath := generateCryptoMaterials(t, cryptogen) 493 defer os.RemoveAll(cryptoPath) 494 495 // get a free random port 496 listenAddr := func() string { 497 l, _ := net.Listen("tcp", "localhost:0") 498 l.Close() 499 return l.Addr().String() 500 }() 501 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 502 conf := &localconfig.TopLevel{ 503 General: localconfig.General{ 504 BootstrapMethod: "file", 505 BootstrapFile: genesisFile, 506 ListenAddress: "localhost", 507 ListenPort: uint16(port), 508 TLS: localconfig.TLS{ 509 Enabled: false, 510 ClientAuthRequired: false, 511 }, 512 }, 513 } 514 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 515 caMgr := &caManager{ 516 appRootCAsByChain: make(map[string][][]byte), 517 ordererRootCAsByChain: make(map[string][][]byte), 518 } 519 callback := func(bundle *channelconfig.Bundle) { 520 if grpcServer.MutualTLSRequired() { 521 t.Log("callback called") 522 caMgr.updateTrustedRoots(bundle, grpcServer) 523 } 524 } 525 lf, _, err := createLedgerFactory(conf, &disabled.Provider{}) 526 assert.NoError(t, err) 527 bootBlock := file.New(genesisFile).GenesisBlock() 528 initializeBootstrapChannel(bootBlock, lf) 529 signer := &server_mocks.SignerSerializer{} 530 531 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 532 assert.NoError(t, err) 533 534 initializeMultichannelRegistrar( 535 bootBlock, 536 &replicationInitiator{cryptoProvider: cryptoProvider}, 537 &cluster.PredicateDialer{}, 538 comm.ServerConfig{}, 539 nil, 540 genesisConfig(t, genesisFile), 541 signer, 542 &disabled.Provider{}, 543 &server_mocks.HealthChecker{}, 544 lf, 545 cryptoProvider, 546 callback, 547 ) 548 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 549 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 550 // mutual TLS not required so no updates should have occurred 551 assert.Equal(t, 0, len(caMgr.appRootCAsByChain["testchannelid"])) 552 assert.Equal(t, 0, len(caMgr.ordererRootCAsByChain["testchannelid"])) 553 grpcServer.Listener().Close() 554 555 conf = &localconfig.TopLevel{ 556 General: localconfig.General{ 557 ListenAddress: "localhost", 558 ListenPort: uint16(port), 559 TLS: localconfig.TLS{ 560 Enabled: true, 561 ClientAuthRequired: true, 562 PrivateKey: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.key"), 563 Certificate: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.crt"), 564 }, 565 }, 566 } 567 grpcServer = initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 568 caMgr = &caManager{ 569 appRootCAsByChain: make(map[string][][]byte), 570 ordererRootCAsByChain: make(map[string][][]byte), 571 } 572 573 clusterConf := initializeClusterClientConfig(conf) 574 predDialer := &cluster.PredicateDialer{ 575 Config: clusterConf, 576 } 577 578 callback = func(bundle *channelconfig.Bundle) { 579 if grpcServer.MutualTLSRequired() { 580 t.Log("callback called") 581 caMgr.updateTrustedRoots(bundle, grpcServer) 582 caMgr.updateClusterDialer(predDialer, clusterConf.SecOpts.ServerRootCAs) 583 } 584 } 585 initializeMultichannelRegistrar( 586 bootBlock, 587 &replicationInitiator{cryptoProvider: cryptoProvider}, 588 predDialer, 589 comm.ServerConfig{}, 590 nil, 591 genesisConfig(t, genesisFile), 592 signer, 593 &disabled.Provider{}, 594 &server_mocks.HealthChecker{}, 595 lf, 596 cryptoProvider, 597 callback, 598 ) 599 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 600 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 601 // mutual TLS is required so updates should have occurred 602 // we expect an intermediate and root CA for apps and orderers 603 assert.Equal(t, 2, len(caMgr.appRootCAsByChain["testchannelid"])) 604 assert.Equal(t, 2, len(caMgr.ordererRootCAsByChain["testchannelid"])) 605 assert.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 606 grpcServer.Listener().Close() 607 } 608 609 func TestConfigureClusterListener(t *testing.T) { 610 logEntries := make(chan string, 100) 611 612 allocatePort := func() uint16 { 613 l, err := net.Listen("tcp", "127.0.0.1:0") 614 assert.NoError(t, err) 615 _, portStr, err := net.SplitHostPort(l.Addr().String()) 616 assert.NoError(t, err) 617 port, err := strconv.ParseInt(portStr, 10, 64) 618 assert.NoError(t, err) 619 assert.NoError(t, l.Close()) 620 t.Log("picked unused port", port) 621 return uint16(port) 622 } 623 624 unUsedPort := allocatePort() 625 626 backupLogger := logger 627 logger = logger.With(zap.Hooks(func(entry zapcore.Entry) error { 628 logEntries <- entry.Message 629 return nil 630 })) 631 632 defer func() { 633 logger = backupLogger 634 }() 635 636 ca, err := tlsgen.NewCA() 637 assert.NoError(t, err) 638 serverKeyPair, err := ca.NewServerCertKeyPair("127.0.0.1") 639 assert.NoError(t, err) 640 641 loadPEM := func(fileName string) ([]byte, error) { 642 switch fileName { 643 case "cert": 644 return serverKeyPair.Cert, nil 645 case "key": 646 return serverKeyPair.Key, nil 647 case "ca": 648 return ca.CertBytes(), nil 649 default: 650 return nil, errors.New("I/O error") 651 } 652 } 653 654 for _, testCase := range []struct { 655 name string 656 conf *localconfig.TopLevel 657 generalConf comm.ServerConfig 658 generalSrv *comm.GRPCServer 659 shouldBeEqual bool 660 expectedPanic string 661 expectedLogEntries []string 662 }{ 663 { 664 name: "invalid certificate", 665 generalConf: comm.ServerConfig{}, 666 conf: &localconfig.TopLevel{ 667 General: localconfig.General{ 668 Cluster: localconfig.Cluster{ 669 ListenAddress: "127.0.0.1", 670 ListenPort: 5000, 671 ServerPrivateKey: "key", 672 ServerCertificate: "bad", 673 RootCAs: []string{"ca"}, 674 }, 675 }, 676 }, 677 expectedPanic: "Failed to load cluster server certificate from 'bad' (I/O error)", 678 generalSrv: &comm.GRPCServer{}, 679 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 680 }, 681 { 682 name: "invalid key", 683 generalConf: comm.ServerConfig{}, 684 conf: &localconfig.TopLevel{ 685 General: localconfig.General{ 686 Cluster: localconfig.Cluster{ 687 ListenAddress: "127.0.0.1", 688 ListenPort: 5000, 689 ServerPrivateKey: "bad", 690 ServerCertificate: "cert", 691 RootCAs: []string{"ca"}, 692 }, 693 }, 694 }, 695 expectedPanic: "Failed to load cluster server key from 'bad' (I/O error)", 696 generalSrv: &comm.GRPCServer{}, 697 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 698 }, 699 { 700 name: "invalid ca cert", 701 generalConf: comm.ServerConfig{}, 702 conf: &localconfig.TopLevel{ 703 General: localconfig.General{ 704 Cluster: localconfig.Cluster{ 705 ListenAddress: "127.0.0.1", 706 ListenPort: 5000, 707 ServerPrivateKey: "key", 708 ServerCertificate: "cert", 709 RootCAs: []string{"bad"}, 710 }, 711 }, 712 }, 713 expectedPanic: "Failed to load CA cert file 'bad' (I/O error)", 714 generalSrv: &comm.GRPCServer{}, 715 expectedLogEntries: []string{"Failed to load CA cert file 'bad' (I/O error)"}, 716 }, 717 { 718 name: "bad listen address", 719 generalConf: comm.ServerConfig{}, 720 conf: &localconfig.TopLevel{ 721 General: localconfig.General{ 722 Cluster: localconfig.Cluster{ 723 ListenAddress: "99.99.99.99", 724 ListenPort: unUsedPort, 725 ServerPrivateKey: "key", 726 ServerCertificate: "cert", 727 RootCAs: []string{"ca"}, 728 }, 729 }, 730 }, 731 expectedPanic: fmt.Sprintf("Failed creating gRPC server on 99.99.99.99:%d due "+ 732 "to listen tcp 99.99.99.99:%d:", unUsedPort, unUsedPort), 733 generalSrv: &comm.GRPCServer{}, 734 }, 735 { 736 name: "green path", 737 generalConf: comm.ServerConfig{}, 738 conf: &localconfig.TopLevel{ 739 General: localconfig.General{ 740 Cluster: localconfig.Cluster{ 741 ListenAddress: "127.0.0.1", 742 ListenPort: 5000, 743 ServerPrivateKey: "key", 744 ServerCertificate: "cert", 745 RootCAs: []string{"ca"}, 746 }, 747 }, 748 }, 749 generalSrv: &comm.GRPCServer{}, 750 }, 751 } { 752 t.Run(testCase.name, func(t *testing.T) { 753 if testCase.shouldBeEqual { 754 conf, srv := configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 755 assert.Equal(t, conf, testCase.generalConf) 756 assert.Equal(t, srv, testCase.generalSrv) 757 } 758 759 if testCase.expectedPanic != "" { 760 f := func() { 761 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 762 } 763 assert.Contains(t, panicMsg(f), testCase.expectedPanic) 764 } else { 765 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 766 } 767 // Ensure logged messages that are expected were all logged 768 var loggedMessages []string 769 for len(logEntries) > 0 { 770 logEntry := <-logEntries 771 loggedMessages = append(loggedMessages, logEntry) 772 } 773 assert.Subset(t, testCase.expectedLogEntries, loggedMessages) 774 }) 775 } 776 } 777 778 func TestReuseListener(t *testing.T) { 779 t.Run("good to reuse", func(t *testing.T) { 780 top := &localconfig.TopLevel{General: localconfig.General{TLS: localconfig.TLS{Enabled: true}}} 781 require.True(t, reuseListener(top, "foo")) 782 }) 783 784 t.Run("reuse tls disabled", func(t *testing.T) { 785 top := &localconfig.TopLevel{} 786 require.PanicsWithValue( 787 t, 788 "TLS is required for running ordering nodes of type foo.", 789 func() { reuseListener(top, "foo") }, 790 ) 791 }) 792 793 t.Run("good not to reuse", func(t *testing.T) { 794 top := &localconfig.TopLevel{ 795 General: localconfig.General{ 796 Cluster: localconfig.Cluster{ 797 ListenAddress: "127.0.0.1", 798 ListenPort: 5000, 799 ServerPrivateKey: "key", 800 ServerCertificate: "bad", 801 }, 802 }, 803 } 804 require.False(t, reuseListener(top, "foo")) 805 }) 806 807 t.Run("partial config", func(t *testing.T) { 808 top := &localconfig.TopLevel{ 809 General: localconfig.General{ 810 Cluster: localconfig.Cluster{ 811 ListenAddress: "127.0.0.1", 812 ListenPort: 5000, 813 ServerCertificate: "bad", 814 }, 815 }, 816 } 817 require.PanicsWithValue( 818 t, 819 "Options: General.Cluster.ListenPort, General.Cluster.ListenAddress,"+ 820 " General.Cluster.ServerCertificate, General.Cluster.ServerPrivateKey, should be defined altogether.", 821 func() { reuseListener(top, "foo") }, 822 ) 823 }) 824 } 825 826 func TestInitializeEtcdraftConsenter(t *testing.T) { 827 consenters := make(map[string]consensus.Consenter) 828 829 tmpdir, err := ioutil.TempDir("", "main_test-") 830 require.NoError(t, err) 831 defer os.RemoveAll(tmpdir) 832 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 833 require.NoError(t, err) 834 835 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 836 genesisBlock := encoder.New(conf).GenesisBlock() 837 838 ca, _ := tlsgen.NewCA() 839 crt, _ := ca.NewServerCertKeyPair("127.0.0.1") 840 841 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 842 assert.NoError(t, err) 843 844 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 845 assert.NoError(t, err) 846 847 initializeEtcdraftConsenter(consenters, 848 &localconfig.TopLevel{}, 849 rlf, 850 &cluster.PredicateDialer{}, 851 genesisBlock, &replicationInitiator{cryptoProvider: cryptoProvider}, 852 comm.ServerConfig{ 853 SecOpts: comm.SecureOptions{ 854 Certificate: crt.Cert, 855 Key: crt.Key, 856 UseTLS: true, 857 }, 858 }, 859 srv, 860 &multichannel.Registrar{}, 861 &disabled.Provider{}, 862 cryptoProvider, 863 ) 864 assert.NotNil(t, consenters["etcdraft"]) 865 } 866 867 func genesisConfig(t *testing.T, genesisFile string) *localconfig.TopLevel { 868 t.Helper() 869 localMSPDir := configtest.GetDevMspDir() 870 return &localconfig.TopLevel{ 871 General: localconfig.General{ 872 BootstrapMethod: "file", 873 BootstrapFile: genesisFile, 874 LocalMSPDir: localMSPDir, 875 LocalMSPID: "SampleOrg", 876 BCCSP: &factory.FactoryOpts{ 877 ProviderName: "SW", 878 SwOpts: &factory.SwOpts{ 879 HashFamily: "SHA2", 880 SecLevel: 256, 881 Ephemeral: true, 882 }, 883 }, 884 }, 885 } 886 } 887 888 func panicMsg(f func()) string { 889 var message interface{} 890 func() { 891 892 defer func() { 893 message = recover() 894 }() 895 896 f() 897 898 }() 899 900 return message.(string) 901 902 } 903 904 func TestCreateReplicator(t *testing.T) { 905 cleanup := configtest.SetDevFabricConfigPath(t) 906 defer cleanup() 907 bootBlock := encoder.New(genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile)).GenesisBlockForChannel("system") 908 909 iterator := &deliver_mocks.BlockIterator{} 910 iterator.NextReturnsOnCall(0, bootBlock, common.Status_SUCCESS) 911 iterator.NextReturnsOnCall(1, bootBlock, common.Status_SUCCESS) 912 913 ledger := &ledger_mocks.ReadWriter{} 914 ledger.On("Height").Return(uint64(1)) 915 ledger.On("Iterator", mock.Anything).Return(iterator, uint64(1)) 916 917 ledgerFactory := &server_mocks.Factory{} 918 ledgerFactory.On("GetOrCreate", "mychannel").Return(ledger, nil) 919 ledgerFactory.On("ChannelIDs").Return([]string{"mychannel"}) 920 921 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 922 assert.NoError(t, err) 923 924 signer := &server_mocks.SignerSerializer{} 925 r := createReplicator(ledgerFactory, bootBlock, &localconfig.TopLevel{}, comm.SecureOptions{}, signer, cryptoProvider) 926 927 err = r.verifierRetriever.RetrieveVerifier("mychannel").VerifyBlockSignature(nil, nil) 928 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") 929 930 err = r.verifierRetriever.RetrieveVerifier("system").VerifyBlockSignature(nil, nil) 931 assert.NoError(t, err) 932 } 933 934 func produceGenesisFile(t *testing.T, profile, channelID string) string { 935 conf := genesisconfig.Load(profile, configtest.GetDevConfigDir()) 936 f, err := ioutil.TempFile("", fmt.Sprintf("%s-genesis_block-", t.Name())) 937 require.NoError(t, err) 938 _, err = f.Write(protoutil.MarshalOrPanic(encoder.New(conf).GenesisBlockForChannel(channelID))) 939 require.NoError(t, err) 940 err = f.Close() 941 require.NoError(t, err) 942 return f.Name() 943 }