github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/server/main_test.go (about) 1 // Copyright hechain. 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/hechain20/hechain/bccsp" 21 "github.com/hechain20/hechain/bccsp/factory" 22 "github.com/hechain20/hechain/bccsp/sw" 23 "github.com/hechain20/hechain/common/channelconfig" 24 "github.com/hechain20/hechain/common/crypto/tlsgen" 25 "github.com/hechain20/hechain/common/flogging" 26 "github.com/hechain20/hechain/common/flogging/floggingtest" 27 "github.com/hechain20/hechain/common/ledger/blockledger" 28 "github.com/hechain20/hechain/common/ledger/blockledger/fileledger" 29 "github.com/hechain20/hechain/common/metrics/disabled" 30 "github.com/hechain20/hechain/common/metrics/prometheus" 31 "github.com/hechain20/hechain/core/config/configtest" 32 "github.com/hechain20/hechain/internal/configtxgen/encoder" 33 "github.com/hechain20/hechain/internal/configtxgen/genesisconfig" 34 "github.com/hechain20/hechain/internal/pkg/comm" 35 "github.com/hechain20/hechain/internal/pkg/identity" 36 "github.com/hechain20/hechain/orderer/common/bootstrap/file" 37 "github.com/hechain20/hechain/orderer/common/cluster" 38 "github.com/hechain20/hechain/orderer/common/filerepo" 39 "github.com/hechain20/hechain/orderer/common/localconfig" 40 "github.com/hechain20/hechain/orderer/common/multichannel" 41 "github.com/hechain20/hechain/orderer/common/onboarding" 42 server_mocks "github.com/hechain20/hechain/orderer/common/server/mocks" 43 "github.com/hechain20/hechain/orderer/consensus" 44 "github.com/hechain20/hechain/protoutil" 45 "github.com/hyperledger/fabric-protos-go/common" 46 . "github.com/onsi/gomega" 47 "github.com/onsi/gomega/gexec" 48 "github.com/pkg/errors" 49 "github.com/stretchr/testify/require" 50 "go.uber.org/zap" 51 "go.uber.org/zap/zapcore" 52 ) 53 54 //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer 55 56 type signerSerializer interface { 57 identity.SignerSerializer 58 } 59 60 // the path to cryptogen, which can be used by tests to create certificates 61 var cryptogen, tempDir string 62 63 func TestMain(m *testing.M) { 64 var err error 65 cryptogen, err = gexec.Build("github.com/hechain20/hechain/cmd/cryptogen") 66 if err != nil { 67 fmt.Fprintf(os.Stderr, "cryptogen build failed: %v", err) 68 os.Exit(-1) 69 } 70 defer gexec.CleanupBuildArtifacts() 71 72 tempDir, err = ioutil.TempDir("", "main-test") 73 if err != nil { 74 fmt.Fprintf(os.Stderr, "failed to create temporary directory: %v", err) 75 os.Exit(-1) 76 } 77 defer os.RemoveAll(tempDir) 78 79 copyYamlFiles("testdata", tempDir) 80 81 os.Exit(m.Run()) 82 } 83 84 func copyYamlFiles(src, dst string) { 85 for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} { 86 fileBytes, err := ioutil.ReadFile(filepath.Join(src, file)) 87 if err != nil { 88 os.Exit(-1) 89 } 90 err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0o644) 91 if err != nil { 92 os.Exit(-1) 93 } 94 } 95 } 96 97 func TestInitializeLogging(t *testing.T) { 98 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 99 os.Setenv("FABRIC_LOGGING_SPEC", "foo=debug") 100 initializeLogging() 101 require.Equal(t, "debug", flogging.LoggerLevel("foo")) 102 os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 103 } 104 105 func TestInitializeProfilingService(t *testing.T) { 106 origEnvValue := os.Getenv("FABRIC_LOGGING_SPEC") 107 defer os.Setenv("FABRIC_LOGGING_SPEC", origEnvValue) 108 os.Setenv("FABRIC_LOGGING_SPEC", "debug") 109 // get a free random port 110 listenAddr := func() string { 111 l, _ := net.Listen("tcp", "localhost:0") 112 l.Close() 113 return l.Addr().String() 114 }() 115 go initializeProfilingService( 116 &localconfig.TopLevel{ 117 General: localconfig.General{ 118 Profile: localconfig.Profile{ 119 Enabled: true, 120 Address: listenAddr, 121 }, 122 }, 123 Kafka: localconfig.Kafka{Verbose: true}, 124 }, 125 ) 126 time.Sleep(500 * time.Millisecond) 127 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 128 t.Logf("Expected pprof to be up (will retry again in 3 seconds): %s", err) 129 time.Sleep(3 * time.Second) 130 if _, err := http.Get("http://" + listenAddr + "/" + "/debug/"); err != nil { 131 t.Fatalf("Expected pprof to be up: %s", err) 132 } 133 } 134 } 135 136 func TestInitializeServerConfig(t *testing.T) { 137 conf := &localconfig.TopLevel{ 138 General: localconfig.General{ 139 ConnectionTimeout: 7 * time.Second, 140 TLS: localconfig.TLS{ 141 Enabled: true, 142 ClientAuthRequired: true, 143 Certificate: "main.go", 144 PrivateKey: "main.go", 145 RootCAs: []string{"main.go"}, 146 ClientRootCAs: []string{"main.go"}, 147 }, 148 }, 149 } 150 sc := initializeServerConfig(conf, nil) 151 expectedContent, _ := ioutil.ReadFile("main.go") 152 require.Equal(t, expectedContent, sc.SecOpts.Certificate) 153 require.Equal(t, expectedContent, sc.SecOpts.Key) 154 require.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ServerRootCAs) 155 require.Equal(t, [][]byte{expectedContent}, sc.SecOpts.ClientRootCAs) 156 157 sc = initializeServerConfig(conf, nil) 158 defaultOpts := comm.DefaultKeepaliveOptions 159 require.Equal(t, defaultOpts.ServerMinInterval, sc.KaOpts.ServerMinInterval) 160 require.Equal(t, time.Duration(0), sc.KaOpts.ServerInterval) 161 require.Equal(t, time.Duration(0), sc.KaOpts.ServerTimeout) 162 require.Equal(t, 7*time.Second, sc.ConnectionTimeout) 163 testDuration := 10 * time.Second 164 conf.General.Keepalive = localconfig.Keepalive{ 165 ServerMinInterval: testDuration, 166 ServerInterval: testDuration, 167 ServerTimeout: testDuration, 168 } 169 sc = initializeServerConfig(conf, nil) 170 require.Equal(t, testDuration, sc.KaOpts.ServerMinInterval) 171 require.Equal(t, testDuration, sc.KaOpts.ServerInterval) 172 require.Equal(t, testDuration, sc.KaOpts.ServerTimeout) 173 174 sc = initializeServerConfig(conf, nil) 175 require.NotNil(t, sc.Logger) 176 require.Equal(t, comm.NewServerStatsHandler(&disabled.Provider{}), sc.ServerStatsHandler) 177 require.Len(t, sc.UnaryInterceptors, 2) 178 require.Len(t, sc.StreamInterceptors, 2) 179 180 sc = initializeServerConfig(conf, &prometheus.Provider{}) 181 require.NotNil(t, sc.ServerStatsHandler) 182 183 goodFile := "main.go" 184 badFile := "does_not_exist" 185 186 oldLogger := logger 187 defer func() { logger = oldLogger }() 188 logger, _ = floggingtest.NewTestLogger(t) 189 190 testCases := []struct { 191 name string 192 certificate string 193 privateKey string 194 rootCA string 195 clientRootCert string 196 clusterCert string 197 clusterKey string 198 clusterCA string 199 isCluster bool 200 expectedPanic string 201 }{ 202 { 203 name: "BadCertificate", 204 certificate: badFile, 205 privateKey: goodFile, 206 rootCA: goodFile, 207 clientRootCert: goodFile, 208 expectedPanic: "Failed to load server Certificate file 'does_not_exist' (open does_not_exist: no such file or directory)", 209 }, 210 { 211 name: "BadPrivateKey", 212 certificate: goodFile, 213 privateKey: badFile, 214 rootCA: goodFile, 215 clientRootCert: goodFile, 216 expectedPanic: "Failed to load PrivateKey file 'does_not_exist' (open does_not_exist: no such file or directory)", 217 }, 218 { 219 name: "BadRootCA", 220 certificate: goodFile, 221 privateKey: goodFile, 222 rootCA: badFile, 223 clientRootCert: goodFile, 224 expectedPanic: "Failed to load ServerRootCAs file 'open does_not_exist: no such file or directory' (does_not_exist)", 225 }, 226 { 227 name: "BadClientRootCertificate", 228 certificate: goodFile, 229 privateKey: goodFile, 230 rootCA: goodFile, 231 clientRootCert: badFile, 232 expectedPanic: "Failed to load ClientRootCAs file 'open does_not_exist: no such file or directory' (does_not_exist)", 233 }, 234 { 235 name: "BadCertificate - cluster reuses server config", 236 certificate: badFile, 237 privateKey: goodFile, 238 rootCA: goodFile, 239 clientRootCert: goodFile, 240 clusterCert: "", 241 clusterKey: "", 242 clusterCA: "", 243 isCluster: true, 244 expectedPanic: "Failed to load client TLS certificate file 'does_not_exist' (open does_not_exist: no such file or directory)", 245 }, 246 { 247 name: "BadPrivateKey - cluster reuses server config", 248 certificate: goodFile, 249 privateKey: badFile, 250 rootCA: goodFile, 251 clientRootCert: goodFile, 252 clusterCert: "", 253 clusterKey: "", 254 clusterCA: "", 255 isCluster: true, 256 expectedPanic: "Failed to load client TLS key file 'does_not_exist' (open does_not_exist: no such file or directory)", 257 }, 258 { 259 name: "BadRootCA - cluster reuses server config", 260 certificate: goodFile, 261 privateKey: goodFile, 262 rootCA: badFile, 263 clientRootCert: goodFile, 264 clusterCert: "", 265 clusterKey: "", 266 clusterCA: "", 267 isCluster: true, 268 expectedPanic: "Failed to load ServerRootCAs file '' (open : no such file or directory)", 269 }, 270 { 271 name: "ClusterBadCertificate", 272 certificate: goodFile, 273 privateKey: goodFile, 274 rootCA: goodFile, 275 clientRootCert: goodFile, 276 clusterCert: badFile, 277 clusterKey: goodFile, 278 clusterCA: goodFile, 279 isCluster: true, 280 expectedPanic: "Failed to load client TLS certificate file 'does_not_exist' (open does_not_exist: no such file or directory)", 281 }, 282 { 283 name: "ClusterBadPrivateKey", 284 certificate: goodFile, 285 privateKey: goodFile, 286 rootCA: goodFile, 287 clientRootCert: goodFile, 288 clusterCert: goodFile, 289 clusterKey: badFile, 290 clusterCA: goodFile, 291 isCluster: true, 292 expectedPanic: "Failed to load client TLS key file 'does_not_exist' (open does_not_exist: no such file or directory)", 293 }, 294 { 295 name: "ClusterBadRootCA", 296 certificate: goodFile, 297 privateKey: goodFile, 298 rootCA: goodFile, 299 clientRootCert: goodFile, 300 clusterCert: goodFile, 301 clusterKey: goodFile, 302 clusterCA: badFile, 303 isCluster: true, 304 expectedPanic: "Failed to load ServerRootCAs file 'does_not_exist' (open does_not_exist: no such file or directory)", 305 }, 306 } 307 for _, tc := range testCases { 308 t.Run(tc.name, func(t *testing.T) { 309 conf := &localconfig.TopLevel{ 310 General: localconfig.General{ 311 TLS: localconfig.TLS{ 312 Enabled: true, 313 ClientAuthRequired: true, 314 Certificate: tc.certificate, 315 PrivateKey: tc.privateKey, 316 RootCAs: []string{tc.rootCA}, 317 ClientRootCAs: []string{tc.clientRootCert}, 318 }, 319 Cluster: localconfig.Cluster{ 320 ClientCertificate: tc.clusterCert, 321 ClientPrivateKey: tc.clusterKey, 322 RootCAs: []string{tc.clusterCA}, 323 }, 324 }, 325 } 326 require.PanicsWithValue(t, tc.expectedPanic, func() { 327 if !tc.isCluster { 328 initializeServerConfig(conf, nil) 329 } else { 330 initializeClusterClientConfig(conf) 331 } 332 }, 333 ) 334 }) 335 } 336 } 337 338 func TestInitializeBootstrapChannel(t *testing.T) { 339 cleanup := configtest.SetDevFabricConfigPath(t) 340 defer cleanup() 341 342 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 343 defer os.Remove(genesisFile) 344 345 fileLedgerLocation, _ := ioutil.TempDir("", "main_test-") 346 defer os.RemoveAll(fileLedgerLocation) 347 348 ledgerFactory, err := createLedgerFactory( 349 &localconfig.TopLevel{ 350 FileLedger: localconfig.FileLedger{ 351 Location: fileLedgerLocation, 352 }, 353 }, 354 &disabled.Provider{}, 355 ) 356 require.NoError(t, err) 357 bootstrapConfig := &localconfig.TopLevel{ 358 General: localconfig.General{ 359 BootstrapMethod: "file", 360 BootstrapFile: genesisFile, 361 }, 362 } 363 364 bootstrapBlock := extractBootstrapBlock(bootstrapConfig) 365 initializeBootstrapChannel(bootstrapBlock, ledgerFactory) 366 367 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 368 require.NoError(t, err) 369 require.Equal(t, uint64(1), ledger.Height()) 370 } 371 372 func TestExtractBootstrapBlock(t *testing.T) { 373 cleanup := configtest.SetDevFabricConfigPath(t) 374 defer cleanup() 375 376 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 377 defer os.Remove(genesisFile) 378 379 tests := []struct { 380 config *localconfig.TopLevel 381 block *common.Block 382 }{ 383 { 384 config: &localconfig.TopLevel{ 385 General: localconfig.General{BootstrapMethod: "file", BootstrapFile: genesisFile}, 386 }, 387 block: file.New(genesisFile).GenesisBlock(), 388 }, 389 { 390 config: &localconfig.TopLevel{ 391 General: localconfig.General{BootstrapMethod: "none"}, 392 }, 393 block: nil, 394 }, 395 } 396 for _, tt := range tests { 397 b := extractBootstrapBlock(tt.config) 398 require.Truef(t, proto.Equal(tt.block, b), "wanted %v, got %v", tt.block, b) 399 } 400 } 401 402 func TestInitSystemChannelWithJoinBlock(t *testing.T) { 403 configPathCleanup := configtest.SetDevFabricConfigPath(t) 404 defer configPathCleanup() 405 genesisFile := produceGenesisFile(t, genesisconfig.SampleSingleMSPSoloProfile, "testchannelid") 406 defer os.Remove(genesisFile) 407 408 var ( 409 config *localconfig.TopLevel 410 cryptoProvider bccsp.BCCSP 411 ledgerFactory blockledger.Factory 412 fileRepo *filerepo.Repo 413 genesisBytes []byte 414 ) 415 416 setup := func() func() { 417 fileLedgerLocation, err := ioutil.TempDir("", "main_test-") 418 require.NoError(t, err) 419 420 config = &localconfig.TopLevel{ 421 General: localconfig.General{ 422 BootstrapMethod: "none", 423 }, 424 FileLedger: localconfig.FileLedger{ 425 Location: fileLedgerLocation, 426 }, 427 ChannelParticipation: localconfig.ChannelParticipation{Enabled: true}, 428 } 429 430 cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 431 require.NoError(t, err) 432 433 ledgerFactory, err = createLedgerFactory(config, &disabled.Provider{}) 434 require.NoError(t, err) 435 436 fileRepo, err = multichannel.InitJoinBlockFileRepo(config) 437 require.NoError(t, err) 438 require.NotNil(t, fileRepo) 439 440 genesisBytes, err = ioutil.ReadFile(genesisFile) 441 require.NoError(t, err) 442 require.NotNil(t, genesisBytes) 443 444 return func() { 445 os.RemoveAll(fileLedgerLocation) 446 } 447 } 448 449 t.Run("No join-block", func(t *testing.T) { 450 cleanup := setup() 451 defer cleanup() 452 453 bootstrapBlock := initSystemChannelWithJoinBlock(config, cryptoProvider, ledgerFactory) 454 require.Nil(t, bootstrapBlock) 455 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 456 require.NoError(t, err) 457 require.Equal(t, uint64(0), ledger.Height()) 458 }) 459 460 t.Run("With genesis join-block", func(t *testing.T) { 461 cleanup := setup() 462 defer cleanup() 463 464 err := fileRepo.Save("testchannelid", genesisBytes) 465 require.NoError(t, err) 466 bootstrapBlock := initSystemChannelWithJoinBlock(config, cryptoProvider, ledgerFactory) 467 require.NotNil(t, bootstrapBlock) 468 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 469 require.NoError(t, err) 470 require.Equal(t, uint64(1), ledger.Height()) 471 // Again, ledger already exists 472 bootstrapBlock = initSystemChannelWithJoinBlock(config, cryptoProvider, ledgerFactory) 473 require.NotNil(t, bootstrapBlock) 474 ledger, err = ledgerFactory.GetOrCreate("testchannelid") 475 require.NoError(t, err) 476 require.Equal(t, uint64(1), ledger.Height()) 477 }) 478 479 t.Run("With non-genesis join-block", func(t *testing.T) { 480 cleanup := setup() 481 defer cleanup() 482 483 block := protoutil.UnmarshalBlockOrPanic(genesisBytes) 484 block.Header.Number = 7 485 configBlockBytes := protoutil.MarshalOrPanic(block) 486 err := fileRepo.Save("testchannelid", configBlockBytes) 487 require.NoError(t, err) 488 bootstrapBlock := initSystemChannelWithJoinBlock(config, cryptoProvider, ledgerFactory) 489 require.NotNil(t, bootstrapBlock) 490 ledger, err := ledgerFactory.GetOrCreate("testchannelid") 491 require.NoError(t, err) 492 require.Equal(t, uint64(0), ledger.Height()) 493 }) 494 } 495 496 func TestExtractSystemChannel(t *testing.T) { 497 cryptoProvider, _ := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 498 499 tmpdir, err := ioutil.TempDir("", "main_test-") 500 require.NoError(t, err) 501 defer os.RemoveAll(tmpdir) 502 503 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 504 require.NoError(t, err) 505 506 lastConf := extractSystemChannel(rlf, cryptoProvider) 507 require.Nil(t, lastConf, "no ledgers") 508 509 _, err = rlf.GetOrCreate("emptychannelid") 510 require.NoError(t, err) 511 512 lastConf = extractSystemChannel(rlf, cryptoProvider) 513 require.Nil(t, lastConf, "skip empty ledger") 514 515 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 516 conf.Consortiums = nil 517 configBlock := encoder.New(conf).GenesisBlock() 518 rl, err := rlf.GetOrCreate("appchannelid") 519 require.NoError(t, err) 520 err = rl.Append(configBlock) 521 require.NoError(t, err) 522 523 lastConf = extractSystemChannel(rlf, cryptoProvider) 524 require.Nil(t, lastConf, "skip app ledger") 525 526 conf = genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 527 configBlock = encoder.New(conf).GenesisBlock() 528 rl, err = rlf.GetOrCreate("testchannelid") 529 require.NoError(t, err) 530 err = rl.Append(configBlock) 531 require.NoError(t, err) 532 533 lastConf = extractSystemChannel(rlf, cryptoProvider) 534 require.NotNil(t, lastConf, "get system channel genesis block") 535 require.Equal(t, uint64(0), lastConf.Header.Number) 536 537 // Make and append the next config block 538 prevHash := protoutil.BlockHeaderHash(configBlock.Header) 539 configBlock.Header.Number = 1 540 configBlock.Header.PreviousHash = prevHash 541 configBlock.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ 542 Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{ 543 LastConfig: &common.LastConfig{Index: rl.Height()}, 544 }), 545 }) 546 configBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ 547 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}), 548 }) 549 err = rl.Append(configBlock) 550 require.NoError(t, err) 551 552 lastConf = extractSystemChannel(rlf, cryptoProvider) 553 require.NotNil(t, lastConf, "get system channel last config block") 554 require.Equal(t, uint64(1), lastConf.Header.Number) 555 } 556 557 func TestSelectClusterBootBlock(t *testing.T) { 558 bootstrapBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 559 lastConfBlock := &common.Block{Header: &common.BlockHeader{Number: 100}} 560 561 clusterBoot := selectClusterBootBlock(bootstrapBlock, nil) 562 require.NotNil(t, clusterBoot) 563 require.Equal(t, uint64(100), clusterBoot.Header.Number) 564 require.True(t, bootstrapBlock == clusterBoot) 565 566 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 567 require.NotNil(t, clusterBoot) 568 require.Equal(t, uint64(100), clusterBoot.Header.Number) 569 require.True(t, bootstrapBlock == clusterBoot) 570 571 lastConfBlock.Header.Number = 200 572 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 573 require.NotNil(t, clusterBoot) 574 require.Equal(t, uint64(200), clusterBoot.Header.Number) 575 require.True(t, lastConfBlock == clusterBoot) 576 577 bootstrapBlock.Header.Number = 300 578 clusterBoot = selectClusterBootBlock(bootstrapBlock, lastConfBlock) 579 require.NotNil(t, clusterBoot) 580 require.Equal(t, uint64(300), clusterBoot.Header.Number) 581 require.True(t, bootstrapBlock == clusterBoot) 582 } 583 584 func TestLoadLocalMSP(t *testing.T) { 585 t.Run("Happy", func(t *testing.T) { 586 localMSPDir := configtest.GetDevMspDir() 587 localMSP := loadLocalMSP( 588 &localconfig.TopLevel{ 589 General: localconfig.General{ 590 LocalMSPDir: localMSPDir, 591 LocalMSPID: "SampleOrg", 592 BCCSP: &factory.FactoryOpts{ 593 Default: "SW", 594 SW: &factory.SwOpts{ 595 Hash: "SHA2", 596 Security: 256, 597 }, 598 }, 599 }, 600 }, 601 ) 602 require.NotNil(t, localMSP) 603 id, err := localMSP.GetIdentifier() 604 require.NoError(t, err) 605 require.Equal(t, id, "SampleOrg") 606 }) 607 608 t.Run("Error", func(t *testing.T) { 609 oldLogger := logger 610 defer func() { logger = oldLogger }() 611 logger, _ = floggingtest.NewTestLogger(t) 612 613 require.Panics(t, func() { 614 loadLocalMSP( 615 &localconfig.TopLevel{ 616 General: localconfig.General{ 617 LocalMSPDir: "", 618 LocalMSPID: "", 619 }, 620 }, 621 ) 622 }) 623 }) 624 } 625 626 func TestInitializeMultichannelRegistrar(t *testing.T) { 627 cleanup := configtest.SetDevFabricConfigPath(t) 628 defer cleanup() 629 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 630 defer os.Remove(genesisFile) 631 632 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 633 require.NoError(t, err) 634 635 signer := &server_mocks.SignerSerializer{} 636 637 t.Run("registrar with a system channel", func(t *testing.T) { 638 conf, ledgerDir := genesisConfig(t, genesisFile) 639 defer os.RemoveAll(ledgerDir) 640 lf, err := createLedgerFactory(conf, &disabled.Provider{}) 641 require.NoError(t, err) 642 bootBlock := file.New(genesisFile).GenesisBlock() 643 initializeBootstrapChannel(bootBlock, lf) 644 registrar := initializeMultichannelRegistrar( 645 bootBlock, 646 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 647 &cluster.PredicateDialer{}, 648 comm.ServerConfig{}, 649 nil, 650 conf, 651 signer, 652 &disabled.Provider{}, 653 &server_mocks.HealthChecker{}, 654 lf, 655 cryptoProvider, 656 ) 657 require.NotNil(t, registrar) 658 require.Equal(t, "testchannelid", registrar.SystemChannelID()) 659 }) 660 661 t.Run("registrar without a system channel", func(t *testing.T) { 662 conf, ledgerDir := genesisConfig(t, genesisFile) 663 defer os.RemoveAll(ledgerDir) 664 conf.General.BootstrapMethod = "none" 665 conf.General.GenesisFile = "" 666 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 667 require.NoError(t, err) 668 lf, err := createLedgerFactory(conf, &disabled.Provider{}) 669 require.NoError(t, err) 670 registrar := initializeMultichannelRegistrar( 671 nil, 672 nil, 673 &cluster.PredicateDialer{}, 674 comm.ServerConfig{}, 675 srv, 676 conf, 677 signer, 678 &disabled.Provider{}, 679 &server_mocks.HealthChecker{}, 680 lf, 681 cryptoProvider, 682 ) 683 require.NotNil(t, registrar) 684 require.Empty(t, registrar.SystemChannelID()) 685 }) 686 } 687 688 func TestInitializeGrpcServer(t *testing.T) { 689 // get a free random port 690 listenAddr := func() string { 691 l, _ := net.Listen("tcp", "localhost:0") 692 l.Close() 693 return l.Addr().String() 694 }() 695 host := strings.Split(listenAddr, ":")[0] 696 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 697 conf := &localconfig.TopLevel{ 698 General: localconfig.General{ 699 ListenAddress: host, 700 ListenPort: uint16(port), 701 TLS: localconfig.TLS{ 702 Enabled: false, 703 ClientAuthRequired: false, 704 }, 705 }, 706 } 707 require.NotPanics(t, func() { 708 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 709 grpcServer.Listener().Close() 710 }) 711 } 712 713 // generateCryptoMaterials uses cryptogen to generate the necessary 714 // MSP files and TLS certificates 715 func generateCryptoMaterials(t *testing.T, cryptogen string) string { 716 gt := NewGomegaWithT(t) 717 cryptoPath := filepath.Join(tempDir, "crypto") 718 719 cmd := exec.Command( 720 cryptogen, 721 "generate", 722 "--config", filepath.Join(tempDir, "examplecom-config.yaml"), 723 "--output", cryptoPath, 724 ) 725 cryptogenProcess, err := gexec.Start(cmd, nil, nil) 726 gt.Expect(err).NotTo(HaveOccurred()) 727 gt.Eventually(cryptogenProcess, time.Minute).Should(gexec.Exit(0)) 728 729 return cryptoPath 730 } 731 732 func TestUpdateTrustedRoots(t *testing.T) { 733 cleanup := configtest.SetDevFabricConfigPath(t) 734 defer cleanup() 735 736 genesisFile := produceGenesisFile(t, genesisconfig.SampleDevModeSoloProfile, "testchannelid") 737 defer os.Remove(genesisFile) 738 739 cryptoPath := generateCryptoMaterials(t, cryptogen) 740 defer os.RemoveAll(cryptoPath) 741 742 // get a free random port 743 listenAddr := func() string { 744 l, _ := net.Listen("tcp", "localhost:0") 745 l.Close() 746 return l.Addr().String() 747 }() 748 port, _ := strconv.ParseUint(strings.Split(listenAddr, ":")[1], 10, 16) 749 tempDir, err := ioutil.TempDir("", "ledger-dir") 750 require.NoError(t, err) 751 conf := &localconfig.TopLevel{ 752 General: localconfig.General{ 753 BootstrapMethod: "file", 754 BootstrapFile: genesisFile, 755 ListenAddress: "localhost", 756 ListenPort: uint16(port), 757 TLS: localconfig.TLS{ 758 Enabled: false, 759 ClientAuthRequired: false, 760 }, 761 }, 762 FileLedger: localconfig.FileLedger{ 763 Location: tempDir, 764 }, 765 } 766 grpcServer := initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 767 caMgr := &caManager{ 768 appRootCAsByChain: make(map[string][][]byte), 769 ordererRootCAsByChain: make(map[string][][]byte), 770 } 771 callback := func(bundle *channelconfig.Bundle) { 772 if grpcServer.MutualTLSRequired() { 773 t.Log("callback called") 774 caMgr.updateTrustedRoots(bundle, grpcServer) 775 } 776 } 777 lf, err := createLedgerFactory(conf, &disabled.Provider{}) 778 require.NoError(t, err) 779 bootBlock := file.New(genesisFile).GenesisBlock() 780 initializeBootstrapChannel(bootBlock, lf) 781 signer := &server_mocks.SignerSerializer{} 782 783 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 784 require.NoError(t, err) 785 786 genConfig, ledgerDir := genesisConfig(t, genesisFile) 787 defer os.RemoveAll(ledgerDir) 788 789 initializeMultichannelRegistrar( 790 bootBlock, 791 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 792 &cluster.PredicateDialer{}, 793 comm.ServerConfig{}, 794 nil, 795 genConfig, 796 signer, 797 &disabled.Provider{}, 798 &server_mocks.HealthChecker{}, 799 lf, 800 cryptoProvider, 801 callback, 802 ) 803 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 804 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 805 // mutual TLS not required so no updates should have occurred 806 require.Equal(t, 0, len(caMgr.appRootCAsByChain["testchannelid"])) 807 require.Equal(t, 0, len(caMgr.ordererRootCAsByChain["testchannelid"])) 808 grpcServer.Listener().Close() 809 810 conf = &localconfig.TopLevel{ 811 General: localconfig.General{ 812 ListenAddress: "localhost", 813 ListenPort: uint16(port), 814 TLS: localconfig.TLS{ 815 Enabled: true, 816 ClientAuthRequired: true, 817 PrivateKey: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.key"), 818 Certificate: filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls", "server.crt"), 819 }, 820 }, 821 } 822 grpcServer = initializeGrpcServer(conf, initializeServerConfig(conf, nil)) 823 caMgr = &caManager{ 824 appRootCAsByChain: make(map[string][][]byte), 825 ordererRootCAsByChain: make(map[string][][]byte), 826 } 827 828 clusterConf, _ := initializeClusterClientConfig(conf) 829 predDialer := &cluster.PredicateDialer{ 830 Config: clusterConf, 831 } 832 833 callback = func(bundle *channelconfig.Bundle) { 834 if grpcServer.MutualTLSRequired() { 835 t.Log("callback called") 836 caMgr.updateTrustedRoots(bundle, grpcServer) 837 caMgr.updateClusterDialer(predDialer, clusterConf.SecOpts.ServerRootCAs) 838 } 839 } 840 genConfig2, ledgerDir2 := genesisConfig(t, genesisFile) 841 defer os.RemoveAll(ledgerDir2) 842 843 initializeMultichannelRegistrar( 844 bootBlock, 845 onboarding.NewReplicationInitiator(lf, bootBlock, conf, comm.SecureOptions{}, signer, cryptoProvider), 846 predDialer, 847 comm.ServerConfig{}, 848 nil, 849 genConfig2, 850 signer, 851 &disabled.Provider{}, 852 &server_mocks.HealthChecker{}, 853 lf, 854 cryptoProvider, 855 callback, 856 ) 857 t.Logf("# app CAs: %d", len(caMgr.appRootCAsByChain["testchannelid"])) 858 t.Logf("# orderer CAs: %d", len(caMgr.ordererRootCAsByChain["testchannelid"])) 859 // mutual TLS is required so updates should have occurred 860 // we expect an intermediate and root CA for apps and orderers 861 require.Equal(t, 2, len(caMgr.appRootCAsByChain["testchannelid"])) 862 require.Equal(t, 2, len(caMgr.ordererRootCAsByChain["testchannelid"])) 863 require.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 864 grpcServer.Listener().Close() 865 } 866 867 func TestRootServerCertAggregation(t *testing.T) { 868 caMgr := &caManager{ 869 appRootCAsByChain: make(map[string][][]byte), 870 ordererRootCAsByChain: make(map[string][][]byte), 871 } 872 873 predDialer := &cluster.PredicateDialer{ 874 Config: comm.ClientConfig{}, 875 } 876 877 ca1, err := tlsgen.NewCA() 878 require.NoError(t, err) 879 880 ca2, err := tlsgen.NewCA() 881 require.NoError(t, err) 882 883 caMgr.ordererRootCAsByChain["foo"] = [][]byte{ca1.CertBytes()} 884 caMgr.ordererRootCAsByChain["bar"] = [][]byte{ca1.CertBytes()} 885 886 caMgr.updateClusterDialer(predDialer, [][]byte{ca2.CertBytes(), ca2.CertBytes(), ca2.CertBytes()}) 887 888 require.Len(t, predDialer.Config.SecOpts.ServerRootCAs, 2) 889 require.Contains(t, predDialer.Config.SecOpts.ServerRootCAs, ca1.CertBytes()) 890 require.Contains(t, predDialer.Config.SecOpts.ServerRootCAs, ca2.CertBytes()) 891 } 892 893 func TestConfigureClusterListener(t *testing.T) { 894 logEntries := make(chan string, 100) 895 896 allocatePort := func() uint16 { 897 l, err := net.Listen("tcp", "127.0.0.1:0") 898 require.NoError(t, err) 899 _, portStr, err := net.SplitHostPort(l.Addr().String()) 900 require.NoError(t, err) 901 port, err := strconv.ParseInt(portStr, 10, 64) 902 require.NoError(t, err) 903 require.NoError(t, l.Close()) 904 t.Log("picked unused port", port) 905 return uint16(port) 906 } 907 908 unUsedPort := allocatePort() 909 910 backupLogger := logger 911 logger = logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error { 912 logEntries <- entry.Message 913 return nil 914 })) 915 916 defer func() { 917 logger = backupLogger 918 }() 919 920 ca, err := tlsgen.NewCA() 921 require.NoError(t, err) 922 serverKeyPair, err := ca.NewServerCertKeyPair("127.0.0.1") 923 require.NoError(t, err) 924 925 loadPEM := func(fileName string) ([]byte, error) { 926 switch fileName { 927 case "cert": 928 return serverKeyPair.Cert, nil 929 case "key": 930 return serverKeyPair.Key, nil 931 case "ca": 932 return ca.CertBytes(), nil 933 default: 934 return nil, errors.New("I/O error") 935 } 936 } 937 938 for _, testCase := range []struct { 939 name string 940 conf *localconfig.TopLevel 941 generalConf comm.ServerConfig 942 generalSrv *comm.GRPCServer 943 shouldBeEqual bool 944 expectedPanic string 945 expectedLogEntries []string 946 }{ 947 { 948 name: "invalid certificate", 949 generalConf: comm.ServerConfig{}, 950 conf: &localconfig.TopLevel{ 951 General: localconfig.General{ 952 Cluster: localconfig.Cluster{ 953 ListenAddress: "127.0.0.1", 954 ListenPort: 5000, 955 ServerPrivateKey: "key", 956 ServerCertificate: "bad", 957 RootCAs: []string{"ca"}, 958 }, 959 }, 960 }, 961 expectedPanic: "Failed to load cluster server certificate from 'bad' (I/O error)", 962 generalSrv: &comm.GRPCServer{}, 963 expectedLogEntries: []string{"Failed to load cluster server certificate from 'bad' (I/O error)"}, 964 }, 965 { 966 name: "invalid key", 967 generalConf: comm.ServerConfig{}, 968 conf: &localconfig.TopLevel{ 969 General: localconfig.General{ 970 Cluster: localconfig.Cluster{ 971 ListenAddress: "127.0.0.1", 972 ListenPort: 5000, 973 ServerPrivateKey: "bad", 974 ServerCertificate: "cert", 975 RootCAs: []string{"ca"}, 976 }, 977 }, 978 }, 979 expectedPanic: "Failed to load cluster server key from 'bad' (I/O error)", 980 generalSrv: &comm.GRPCServer{}, 981 expectedLogEntries: []string{"Failed to load cluster server key from 'bad' (I/O error)"}, 982 }, 983 { 984 name: "invalid ca cert", 985 generalConf: comm.ServerConfig{}, 986 conf: &localconfig.TopLevel{ 987 General: localconfig.General{ 988 Cluster: localconfig.Cluster{ 989 ListenAddress: "127.0.0.1", 990 ListenPort: 5000, 991 ServerPrivateKey: "key", 992 ServerCertificate: "cert", 993 RootCAs: []string{"bad"}, 994 }, 995 }, 996 }, 997 expectedPanic: "Failed to load CA cert file 'bad' (I/O error)", 998 generalSrv: &comm.GRPCServer{}, 999 expectedLogEntries: []string{"Failed to load CA cert file 'bad' (I/O error)"}, 1000 }, 1001 { 1002 name: "bad listen address", 1003 generalConf: comm.ServerConfig{}, 1004 conf: &localconfig.TopLevel{ 1005 General: localconfig.General{ 1006 Cluster: localconfig.Cluster{ 1007 ListenAddress: "99.99.99.99", 1008 ListenPort: unUsedPort, 1009 ServerPrivateKey: "key", 1010 ServerCertificate: "cert", 1011 RootCAs: []string{"ca"}, 1012 }, 1013 }, 1014 }, 1015 expectedPanic: fmt.Sprintf("Failed creating gRPC server on 99.99.99.99:%d due "+ 1016 "to listen tcp 99.99.99.99:%d:", unUsedPort, unUsedPort), 1017 generalSrv: &comm.GRPCServer{}, 1018 }, 1019 { 1020 name: "green path", 1021 generalConf: comm.ServerConfig{}, 1022 conf: &localconfig.TopLevel{ 1023 General: localconfig.General{ 1024 Cluster: localconfig.Cluster{ 1025 ListenAddress: "127.0.0.1", 1026 ListenPort: 5000, 1027 ServerPrivateKey: "key", 1028 ServerCertificate: "cert", 1029 RootCAs: []string{"ca"}, 1030 }, 1031 }, 1032 }, 1033 generalSrv: &comm.GRPCServer{}, 1034 }, 1035 } { 1036 t.Run(testCase.name, func(t *testing.T) { 1037 if testCase.shouldBeEqual { 1038 conf, srv := configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 1039 require.Equal(t, conf, testCase.generalConf) 1040 require.Equal(t, srv, testCase.generalSrv) 1041 } 1042 1043 if testCase.expectedPanic != "" { 1044 f := func() { 1045 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 1046 } 1047 require.Contains(t, panicMsg(f), testCase.expectedPanic) 1048 } else { 1049 configureClusterListener(testCase.conf, testCase.generalConf, loadPEM) 1050 } 1051 // Ensure logged messages that are expected were all logged 1052 var loggedMessages []string 1053 for len(logEntries) > 0 { 1054 logEntry := <-logEntries 1055 loggedMessages = append(loggedMessages, logEntry) 1056 } 1057 require.Subset(t, loggedMessages, testCase.expectedLogEntries) 1058 }) 1059 } 1060 } 1061 1062 func TestReuseListener(t *testing.T) { 1063 t.Run("good to reuse", func(t *testing.T) { 1064 top := &localconfig.TopLevel{General: localconfig.General{TLS: localconfig.TLS{Enabled: true}}} 1065 require.True(t, reuseListener(top)) 1066 }) 1067 1068 t.Run("reuse tls disabled", func(t *testing.T) { 1069 top := &localconfig.TopLevel{} 1070 require.PanicsWithValue( 1071 t, 1072 "TLS is required for running ordering nodes of cluster type.", 1073 func() { reuseListener(top) }, 1074 ) 1075 }) 1076 1077 t.Run("good not to reuse", func(t *testing.T) { 1078 top := &localconfig.TopLevel{ 1079 General: localconfig.General{ 1080 Cluster: localconfig.Cluster{ 1081 ListenAddress: "127.0.0.1", 1082 ListenPort: 5000, 1083 ServerPrivateKey: "key", 1084 ServerCertificate: "bad", 1085 }, 1086 }, 1087 } 1088 require.False(t, reuseListener(top)) 1089 }) 1090 1091 t.Run("partial config", func(t *testing.T) { 1092 top := &localconfig.TopLevel{ 1093 General: localconfig.General{ 1094 Cluster: localconfig.Cluster{ 1095 ListenAddress: "127.0.0.1", 1096 ListenPort: 5000, 1097 ServerCertificate: "bad", 1098 }, 1099 }, 1100 } 1101 require.PanicsWithValue( 1102 t, 1103 "Options: General.Cluster.ListenPort, General.Cluster.ListenAddress,"+ 1104 " General.Cluster.ServerCertificate, General.Cluster.ServerPrivateKey, should be defined altogether.", 1105 func() { reuseListener(top) }, 1106 ) 1107 }) 1108 } 1109 1110 func TestInitializeEtcdraftConsenter(t *testing.T) { 1111 consenters := make(map[string]consensus.Consenter) 1112 1113 tmpdir, err := ioutil.TempDir("", "main_test-") 1114 require.NoError(t, err) 1115 defer os.RemoveAll(tmpdir) 1116 rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) 1117 require.NoError(t, err) 1118 1119 conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 1120 genesisBlock := encoder.New(conf).GenesisBlock() 1121 1122 ca, _ := tlsgen.NewCA() 1123 crt, _ := ca.NewServerCertKeyPair("127.0.0.1") 1124 1125 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{}) 1126 require.NoError(t, err) 1127 1128 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 1129 require.NoError(t, err) 1130 1131 initializeEtcdraftConsenter( 1132 consenters, 1133 &localconfig.TopLevel{}, 1134 rlf, 1135 &cluster.PredicateDialer{}, 1136 genesisBlock, 1137 onboarding.NewReplicationInitiator(rlf, genesisBlock, nil, comm.SecureOptions{}, nil, cryptoProvider), 1138 comm.ServerConfig{ 1139 SecOpts: comm.SecureOptions{ 1140 Certificate: crt.Cert, 1141 Key: crt.Key, 1142 UseTLS: true, 1143 }, 1144 }, 1145 srv, 1146 &multichannel.Registrar{}, 1147 &disabled.Provider{}, 1148 cryptoProvider, 1149 ) 1150 require.NotNil(t, consenters["etcdraft"]) 1151 } 1152 1153 func genesisConfig(t *testing.T, genesisFile string) (*localconfig.TopLevel, string) { 1154 t.Helper() 1155 localMSPDir := configtest.GetDevMspDir() 1156 ledgerDir, err := ioutil.TempDir("", "genesis-config") 1157 require.NoError(t, err) 1158 1159 return &localconfig.TopLevel{ 1160 General: localconfig.General{ 1161 BootstrapMethod: "file", 1162 BootstrapFile: genesisFile, 1163 LocalMSPDir: localMSPDir, 1164 LocalMSPID: "SampleOrg", 1165 BCCSP: &factory.FactoryOpts{ 1166 Default: "SW", 1167 SW: &factory.SwOpts{ 1168 Hash: "SHA2", 1169 Security: 256, 1170 }, 1171 }, 1172 }, 1173 FileLedger: localconfig.FileLedger{ 1174 Location: ledgerDir, 1175 }, 1176 }, ledgerDir 1177 } 1178 1179 func panicMsg(f func()) string { 1180 var message interface{} 1181 func() { 1182 defer func() { 1183 message = recover() 1184 }() 1185 1186 f() 1187 }() 1188 1189 return message.(string) 1190 } 1191 1192 func produceGenesisFile(t *testing.T, profile, channelID string) string { 1193 conf := genesisconfig.Load(profile, configtest.GetDevConfigDir()) 1194 f, err := ioutil.TempFile("", fmt.Sprintf("%s-genesis_block-", t.Name())) 1195 require.NoError(t, err) 1196 _, err = f.Write(protoutil.MarshalOrPanic(encoder.New(conf).GenesisBlockForChannel(channelID))) 1197 require.NoError(t, err) 1198 err = f.Close() 1199 require.NoError(t, err) 1200 return f.Name() 1201 }