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