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