github.com/anjalikarhana/fabric@v2.1.1+incompatible/orderer/common/server/main_test.go (about)

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