github.com/kaituanwang/hyperledger@v2.0.1+incompatible/orderer/common/server/main_test.go (about)

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