github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/orderer/common/server/main_test.go (about)

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