github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/multichannel/registrar_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package multichannel
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/golang/protobuf/proto"
    20  	"github.com/hechain20/hechain/bccsp"
    21  	"github.com/hechain20/hechain/bccsp/sw"
    22  	"github.com/hechain20/hechain/common/channelconfig"
    23  	"github.com/hechain20/hechain/common/crypto/tlsgen"
    24  	"github.com/hechain20/hechain/common/ledger/blockledger"
    25  	"github.com/hechain20/hechain/common/ledger/blockledger/fileledger"
    26  	"github.com/hechain20/hechain/common/metrics/disabled"
    27  	"github.com/hechain20/hechain/common/policies"
    28  	"github.com/hechain20/hechain/core/config/configtest"
    29  	"github.com/hechain20/hechain/internal/configtxgen/encoder"
    30  	"github.com/hechain20/hechain/internal/configtxgen/genesisconfig"
    31  	"github.com/hechain20/hechain/internal/pkg/comm"
    32  	"github.com/hechain20/hechain/internal/pkg/identity"
    33  	"github.com/hechain20/hechain/orderer/common/blockcutter"
    34  	"github.com/hechain20/hechain/orderer/common/cluster"
    35  	"github.com/hechain20/hechain/orderer/common/localconfig"
    36  	"github.com/hechain20/hechain/orderer/common/multichannel/mocks"
    37  	"github.com/hechain20/hechain/orderer/common/types"
    38  	"github.com/hechain20/hechain/orderer/consensus"
    39  	"github.com/hechain20/hechain/orderer/consensus/etcdraft"
    40  	"github.com/hechain20/hechain/protoutil"
    41  	cb "github.com/hyperledger/fabric-protos-go/common"
    42  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    43  	"github.com/pkg/errors"
    44  	"github.com/stretchr/testify/assert"
    45  	"github.com/stretchr/testify/require"
    46  )
    47  
    48  //go:generate counterfeiter -o mocks/resources.go --fake-name Resources . resources
    49  
    50  type resources interface {
    51  	channelconfig.Resources
    52  }
    53  
    54  //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig
    55  
    56  type ordererConfig interface {
    57  	channelconfig.Orderer
    58  }
    59  
    60  //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities
    61  
    62  type ordererCapabilities interface {
    63  	channelconfig.OrdererCapabilities
    64  }
    65  
    66  //go:generate counterfeiter -o mocks/channel_config.go --fake-name ChannelConfig . channelConfig
    67  
    68  type channelConfig interface {
    69  	channelconfig.Channel
    70  }
    71  
    72  //go:generate counterfeiter -o mocks/channel_capabilities.go --fake-name ChannelCapabilities . channelCapabilities
    73  
    74  type channelCapabilities interface {
    75  	channelconfig.ChannelCapabilities
    76  }
    77  
    78  //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer
    79  
    80  type signerSerializer interface {
    81  	identity.SignerSerializer
    82  }
    83  
    84  //go:generate counterfeiter -o mocks/consenter.go --fake-name Consenter . consenter
    85  type consenter interface {
    86  	consensus.Consenter
    87  	consensus.ClusterConsenter
    88  }
    89  
    90  func mockCrypto() *mocks.SignerSerializer {
    91  	return &mocks.SignerSerializer{}
    92  }
    93  
    94  func newFactory(dir string) blockledger.Factory {
    95  	rlf, err := fileledger.New(dir, &disabled.Provider{})
    96  	if err != nil {
    97  		panic(err)
    98  	}
    99  
   100  	return rlf
   101  }
   102  
   103  func newLedgerAndFactory(dir string, chainID string, genesisBlockSys *cb.Block) (blockledger.Factory, blockledger.ReadWriter) {
   104  	rlf := newFactory(dir)
   105  	rl := newLedger(rlf, chainID, genesisBlockSys)
   106  	return rlf, rl
   107  }
   108  
   109  func newLedger(rlf blockledger.Factory, chainID string, genesisBlockSys *cb.Block) blockledger.ReadWriter {
   110  	rl, err := rlf.GetOrCreate(chainID)
   111  	if err != nil {
   112  		panic(err)
   113  	}
   114  
   115  	if genesisBlockSys != nil {
   116  		err = rl.Append(genesisBlockSys)
   117  		if err != nil {
   118  			panic(err)
   119  		}
   120  	}
   121  	return rl
   122  }
   123  
   124  func testMessageOrderAndRetrieval(maxMessageCount uint32, chainID string, chainSupport *ChainSupport, lr blockledger.ReadWriter, t *testing.T) {
   125  	messages := make([]*cb.Envelope, maxMessageCount)
   126  	for i := uint32(0); i < maxMessageCount; i++ {
   127  		messages[i] = makeNormalTx(chainID, int(i))
   128  	}
   129  	for _, message := range messages {
   130  		chainSupport.Order(message, 0)
   131  	}
   132  	it, _ := lr.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   133  	defer it.Close()
   134  	block, status := it.Next()
   135  	require.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block")
   136  	for i := uint32(0); i < maxMessageCount; i++ {
   137  		require.True(t, proto.Equal(messages[i], protoutil.ExtractEnvelopeOrPanic(block, int(i))), "Block contents wrong at index %d", i)
   138  	}
   139  }
   140  
   141  func TestConfigTx(t *testing.T) {
   142  	// system channel
   143  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   144  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   145  
   146  	// Tests for a normal channel which contains 3 config transactions and other
   147  	// normal transactions to make sure the right one returned
   148  	t.Run("GetConfigTx - ok", func(t *testing.T) {
   149  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   150  		require.NoError(t, err)
   151  		defer os.RemoveAll(tmpdir)
   152  
   153  		_, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   154  		for i := 0; i < 5; i++ {
   155  			rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", i)}))
   156  		}
   157  		rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx("testchannelid", 5)}))
   158  		ctx := makeConfigTx("testchannelid", 6)
   159  		rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{ctx}))
   160  
   161  		// block with LAST_CONFIG metadata in SIGNATURES field
   162  		block := blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", 7)})
   163  		blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   164  			LastConfig: &cb.LastConfig{Index: 7},
   165  		})
   166  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: blockSignatureValue})
   167  		rl.Append(block)
   168  
   169  		pctx := configTx(rl)
   170  		require.True(t, proto.Equal(pctx, ctx), "Did not select most recent config transaction")
   171  	})
   172  }
   173  
   174  func TestNewRegistrar(t *testing.T) {
   175  	// system channel
   176  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   177  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   178  
   179  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   180  	require.NoError(t, err)
   181  
   182  	// This test checks to make sure the orderer can come up if it cannot find any chains
   183  	t.Run("No chains", func(t *testing.T) {
   184  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   185  		require.NoError(t, err)
   186  		defer os.RemoveAll(tmpdir)
   187  
   188  		lf, err := fileledger.New(tmpdir, &disabled.Provider{})
   189  		require.NoError(t, err)
   190  
   191  		consenters := map[string]consensus.Consenter{"etcdraft": &mocks.Consenter{}}
   192  
   193  		var manager *Registrar
   194  		require.NotPanics(t, func() {
   195  			manager = NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   196  			manager.Initialize(consenters)
   197  		}, "Should not panic when starting without a system channel")
   198  		require.NotNil(t, manager)
   199  		list := manager.ChannelList()
   200  		require.Equal(t, types.ChannelList{}, list)
   201  		info, err := manager.ChannelInfo("my-channel")
   202  		require.EqualError(t, err, types.ErrChannelNotExist.Error())
   203  		require.Equal(t, types.ChannelInfo{}, info)
   204  	})
   205  
   206  	// This test checks to make sure that the orderer refuses to come up if there are multiple system channels
   207  	t.Run("Multiple system chains - failure", func(t *testing.T) {
   208  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   209  		require.NoError(t, err)
   210  		defer os.RemoveAll(tmpdir)
   211  
   212  		lf, err := fileledger.New(tmpdir, &disabled.Provider{})
   213  		require.NoError(t, err)
   214  
   215  		for _, id := range []string{"foo", "bar"} {
   216  			rl, err := lf.GetOrCreate(id)
   217  			require.NoError(t, err)
   218  
   219  			err = rl.Append(encoder.New(confSys).GenesisBlockForChannel(id))
   220  			require.NoError(t, err)
   221  		}
   222  
   223  		consenter := &mocks.Consenter{}
   224  		consenter.HandleChainCalls(handleChain)
   225  		consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   226  
   227  		require.Panics(t, func() {
   228  			NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil).Initialize(consenters)
   229  		}, "Two system channels should have caused panic")
   230  	})
   231  
   232  	// This test essentially brings the entire system up and is ultimately what main.go will replicate
   233  	t.Run("Correct flow with system channel", func(t *testing.T) {
   234  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   235  		require.NoError(t, err)
   236  		defer os.RemoveAll(tmpdir)
   237  
   238  		lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   239  
   240  		consenter := &mocks.Consenter{}
   241  		consenter.HandleChainCalls(handleChain)
   242  		consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   243  
   244  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   245  		manager.Initialize(consenters)
   246  
   247  		chainSupport := manager.GetChain("Fake")
   248  		require.Nilf(t, chainSupport, "Should not have found a chain that was not created")
   249  
   250  		chainSupport = manager.GetChain("testchannelid")
   251  		require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger")
   252  
   253  		list := manager.ChannelList()
   254  		require.NotNil(t, list.SystemChannel)
   255  
   256  		require.Equal(
   257  			t,
   258  			types.ChannelList{
   259  				SystemChannel: &types.ChannelInfoShort{Name: "testchannelid", URL: ""},
   260  				Channels:      nil,
   261  			},
   262  			list,
   263  		)
   264  
   265  		info, err := manager.ChannelInfo("testchannelid")
   266  		require.NoError(t, err)
   267  		require.Equal(t,
   268  			types.ChannelInfo{Name: "testchannelid", URL: "", ConsensusRelation: "other", Status: "active", Height: 1},
   269  			info,
   270  		)
   271  
   272  		testMessageOrderAndRetrieval(confSys.Orderer.BatchSize.MaxMessageCount, "testchannelid", chainSupport, rl, t)
   273  	})
   274  }
   275  
   276  func TestRegistrar_Initialize(t *testing.T) {
   277  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   278  	require.NoError(t, err)
   279  
   280  	tlsCA, err := tlsgen.NewCA()
   281  	require.NoError(t, err)
   282  
   283  	dialer := &cluster.PredicateDialer{
   284  		Config: comm.ClientConfig{
   285  			SecOpts: comm.SecureOptions{
   286  				Certificate: tlsCA.CertBytes(),
   287  			},
   288  		},
   289  	}
   290  
   291  	config := localconfig.TopLevel{
   292  		General: localconfig.General{
   293  			BootstrapMethod: "none",
   294  			GenesisFile:     "",
   295  			Cluster: localconfig.Cluster{
   296  				ReplicationBufferSize:   1,
   297  				ReplicationPullTimeout:  time.Microsecond,
   298  				ReplicationRetryTimeout: time.Microsecond,
   299  				ReplicationMaxRetries:   2,
   300  			},
   301  		},
   302  		ChannelParticipation: localconfig.ChannelParticipation{
   303  			Enabled: true,
   304  		},
   305  	}
   306  
   307  	confAppRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
   308  	confAppRaft.Consortiums = nil
   309  	confAppRaft.Consortium = ""
   310  	certDir, err := ioutil.TempDir("", "registrar_test-")
   311  	require.NoError(t, err)
   312  	defer os.RemoveAll(certDir)
   313  	generateCertificates(t, confAppRaft, tlsCA, certDir)
   314  	bootstrapper, err := encoder.NewBootstrapper(confAppRaft)
   315  	require.NoError(t, err, "cannot create bootstrapper")
   316  	genesisBlockAppRaft := bootstrapper.GenesisBlockForChannel("my-raft-channel")
   317  	require.NotNil(t, genesisBlockAppRaft)
   318  
   319  	confSysRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
   320  	confSysRaft.Orderer.EtcdRaft.Consenters = confAppRaft.Orderer.EtcdRaft.Consenters
   321  	bootstrapper, err = encoder.NewBootstrapper(confSysRaft)
   322  	require.NoError(t, err, "cannot create bootstrapper")
   323  	genesisBlockSysRaft := bootstrapper.GenesisBlockForChannel("my-sys-channel")
   324  	require.NotNil(t, genesisBlockSysRaft)
   325  
   326  	consenter := &mocks.Consenter{}
   327  	consenter.HandleChainCalls(handleChainCluster)
   328  	consenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: consenter}
   329  
   330  	// This test essentially brings the entire system up and is ultimately what main.go will replicate
   331  	t.Run("Correct flow with system channel - etcdraft.Chain", func(t *testing.T) {
   332  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   333  		require.NoError(t, err)
   334  		defer os.RemoveAll(tmpdir)
   335  
   336  		lf, _ := newLedgerAndFactory(tmpdir, "my-sys-channel", genesisBlockSysRaft)
   337  
   338  		manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
   339  		manager.Initialize(consenters)
   340  
   341  		chainSupport := manager.GetChain("Fake")
   342  		require.Nilf(t, chainSupport, "Should not have found a chain that was not created")
   343  
   344  		chainSupport = manager.GetChain("my-sys-channel")
   345  		require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger")
   346  
   347  		list := manager.ChannelList()
   348  		require.NotNil(t, list.SystemChannel)
   349  
   350  		require.Equal(t,
   351  			types.ChannelList{
   352  				SystemChannel: &types.ChannelInfoShort{Name: "my-sys-channel", URL: ""},
   353  				Channels:      nil,
   354  			},
   355  			list,
   356  		)
   357  
   358  		info, err := manager.ChannelInfo("my-sys-channel")
   359  		require.NoError(t, err)
   360  		require.Equal(t,
   361  			types.ChannelInfo{Name: "my-sys-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1},
   362  			info,
   363  		)
   364  	})
   365  
   366  	t.Run("Correct flow without system channel - etcdraft.Chain", func(t *testing.T) {
   367  		// TODO
   368  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   369  		require.NoError(t, err)
   370  		defer os.RemoveAll(tmpdir)
   371  
   372  		lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft)
   373  
   374  		consenter.IsChannelMemberReturns(true, nil)
   375  
   376  		manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
   377  		manager.Initialize(consenters)
   378  
   379  		chainSupport := manager.GetChain("not-there")
   380  		require.Nilf(t, chainSupport, "Should not have found a chain that was not created")
   381  
   382  		chainSupport = manager.GetChain("my-raft-channel")
   383  		require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger")
   384  
   385  		list := manager.ChannelList()
   386  		require.Nil(t, list.SystemChannel)
   387  
   388  		require.Equal(
   389  			t,
   390  			types.ChannelList{
   391  				SystemChannel: nil,
   392  				Channels:      []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}},
   393  			},
   394  			list,
   395  		)
   396  
   397  		info, err := manager.ChannelInfo("my-raft-channel")
   398  		require.NoError(t, err)
   399  		require.Equal(t,
   400  			types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1},
   401  			info,
   402  		)
   403  	})
   404  
   405  	t.Run("Correct flow without system channel - follower.Chain", func(t *testing.T) {
   406  		// TODO
   407  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   408  		require.NoError(t, err)
   409  		defer os.RemoveAll(tmpdir)
   410  
   411  		lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft)
   412  
   413  		consenter.IsChannelMemberReturns(false, nil)
   414  
   415  		manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
   416  		manager.Initialize(consenters)
   417  
   418  		fChain := manager.GetFollower("not-there")
   419  		require.Nil(t, fChain, "Should not have found a follower that was not created")
   420  
   421  		list := manager.ChannelList()
   422  		require.Nil(t, list.SystemChannel)
   423  
   424  		require.Equal(
   425  			t,
   426  			types.ChannelList{
   427  				SystemChannel: nil,
   428  				Channels:      []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}},
   429  			},
   430  			list,
   431  		)
   432  
   433  		info, err := manager.ChannelInfo("my-raft-channel")
   434  		require.NoError(t, err)
   435  		require.Equal(t,
   436  			types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "active", Height: 1},
   437  			info,
   438  		)
   439  
   440  		fChain = manager.GetFollower("my-raft-channel")
   441  		require.NotNil(t, fChain, "Should have gotten follower which was initialized by ledger")
   442  		fChain.Halt()
   443  	})
   444  
   445  	t.Run("Correct flow without system channel - follower.Chain with join block", func(t *testing.T) {
   446  		// TODO
   447  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   448  		require.NoError(t, err)
   449  		defer os.RemoveAll(tmpdir)
   450  
   451  		config.FileLedger = localconfig.FileLedger{Location: tmpdir}
   452  
   453  		lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft)
   454  
   455  		consenter.IsChannelMemberReturns(false, nil)
   456  
   457  		manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
   458  
   459  		// Emulate saved join-block from before the restart
   460  		joinBlockAppRaft := protoutil.UnmarshalBlockOrPanic(protoutil.MarshalOrPanic(genesisBlockAppRaft))
   461  		joinBlockAppRaft.Header.Number = 10
   462  		manager.joinBlockFileRepo.Save("my-raft-channel", protoutil.MarshalOrPanic(joinBlockAppRaft))
   463  
   464  		manager.Initialize(consenters)
   465  
   466  		fChain := manager.GetFollower("not-there")
   467  		require.Nil(t, fChain, "Should not have found a follower that was not created")
   468  
   469  		list := manager.ChannelList()
   470  		require.Nil(t, list.SystemChannel)
   471  
   472  		require.Equal(t,
   473  			types.ChannelList{
   474  				SystemChannel: nil,
   475  				Channels:      []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}},
   476  			},
   477  			list,
   478  		)
   479  
   480  		info, err := manager.ChannelInfo("my-raft-channel")
   481  		require.NoError(t, err)
   482  		require.Equal(t,
   483  			types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 1},
   484  			info,
   485  		)
   486  
   487  		fChain = manager.GetFollower("my-raft-channel")
   488  		require.NotNil(t, fChain, "Should have gotten follower which was initialized by ledger")
   489  		fChain.Halt()
   490  	})
   491  }
   492  
   493  func TestNewRegistrarWithFileRepo(t *testing.T) {
   494  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   495  	require.NoError(t, err)
   496  
   497  	consenter := &mocks.Consenter{}
   498  	consenter.HandleChainCalls(handleChain)
   499  	consenter.IsChannelMemberReturns(true, nil)
   500  	consenters := map[string]consensus.Consenter{"etcdraft": consenter}
   501  
   502  	t.Run("Correct flow with valid file repo dir, one existing channel, two joinblocks", func(t *testing.T) {
   503  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   504  		require.NoError(t, err)
   505  		defer os.RemoveAll(tmpdir)
   506  
   507  		tlsCA, err := tlsgen.NewCA()
   508  		require.NoError(t, err)
   509  
   510  		confAppRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
   511  		confAppRaft.Consortiums = nil
   512  		confAppRaft.Consortium = ""
   513  
   514  		generateCertificates(t, confAppRaft, tlsCA, tmpdir)
   515  
   516  		bootstrapper, err := encoder.NewBootstrapper(confAppRaft)
   517  		require.NoError(t, err, "cannot create bootstrapper")
   518  
   519  		genesisBlockAppRaft := bootstrapper.GenesisBlockForChannel("my-cft-channel")
   520  		require.NotNil(t, genesisBlockAppRaft)
   521  		genesisBlockAppRaft2 := bootstrapper.GenesisBlockForChannel("my-other-cft-channel")
   522  		require.NotNil(t, genesisBlockAppRaft2)
   523  
   524  		// create joinblock file repo directory with two existing joinblocks
   525  		createJoinBlockFileRepoDirWithBlocks(
   526  			t,
   527  			tmpdir,
   528  			&joinBlock{
   529  				channel: "my-cft-channel",
   530  				block:   genesisBlockAppRaft,
   531  			},
   532  			&joinBlock{
   533  				channel: "my-other-cft-channel",
   534  				block:   genesisBlockAppRaft2,
   535  			},
   536  		)
   537  
   538  		// create one existing channel
   539  		genesisBlockAppExisting := bootstrapper.GenesisBlockForChannel("my-existing-channel")
   540  		require.NotNil(t, genesisBlockAppExisting)
   541  		lf, _ := newLedgerAndFactory(tmpdir, "my-existing-channel", genesisBlockAppExisting)
   542  		require.NoError(t, err)
   543  
   544  		// create the ledger for one of the channels with a joinblock in the file repo
   545  		_, err = lf.GetOrCreate("my-other-cft-channel")
   546  		require.NoError(t, err)
   547  
   548  		config := localconfig.TopLevel{
   549  			ChannelParticipation: localconfig.ChannelParticipation{Enabled: true},
   550  			FileLedger:           localconfig.FileLedger{Location: tmpdir},
   551  		}
   552  		var manager *Registrar
   553  		require.NotPanics(t, func() {
   554  			manager = NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   555  			manager.Initialize(consenters)
   556  		}, "Should not panic when file repo dir exists and is read writable")
   557  		require.NotNil(t, manager)
   558  		require.NotNil(t, manager.joinBlockFileRepo)
   559  		require.DirExists(t, filepath.Join(tmpdir, "pendingops"))
   560  
   561  		list := manager.ChannelList()
   562  		require.Nil(t, list.SystemChannel)
   563  		require.ElementsMatch(
   564  			t,
   565  			[]types.ChannelInfoShort{
   566  				{Name: "my-cft-channel", URL: ""},
   567  				{Name: "my-other-cft-channel", URL: ""},
   568  				{Name: "my-existing-channel", URL: ""},
   569  			},
   570  			list.Channels,
   571  		)
   572  	})
   573  }
   574  
   575  type joinBlock struct {
   576  	channel string
   577  	block   *cb.Block
   578  }
   579  
   580  func createJoinBlockFileRepoDirWithBlocks(t *testing.T, tmpdir string, joinBlocks ...*joinBlock) {
   581  	joinBlockRepoPath := filepath.Join(tmpdir, "pendingops", "join")
   582  	err := os.MkdirAll(joinBlockRepoPath, 0o755)
   583  	require.NoError(t, err)
   584  	for _, jb := range joinBlocks {
   585  		blockBytes, err := proto.Marshal(jb.block)
   586  		require.NoError(t, err)
   587  		err = ioutil.WriteFile(filepath.Join(joinBlockRepoPath, fmt.Sprintf("%s.join", jb.channel)), blockBytes, 0o600)
   588  		require.NoError(t, err)
   589  	}
   590  }
   591  
   592  func TestCreateChain(t *testing.T) {
   593  	// system channel
   594  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   595  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   596  
   597  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   598  	require.NoError(t, err)
   599  
   600  	t.Run("Create chain", func(t *testing.T) {
   601  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   602  		require.NoError(t, err)
   603  		defer os.RemoveAll(tmpdir)
   604  
   605  		lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   606  
   607  		consenter := &mocks.Consenter{}
   608  		consenter.HandleChainCalls(handleChainCluster)
   609  		consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   610  
   611  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   612  		manager.Initialize(consenters)
   613  
   614  		ledger, err := lf.GetOrCreate("mychannel")
   615  		require.NoError(t, err)
   616  
   617  		genesisBlock := encoder.New(confSys).GenesisBlockForChannel("mychannel")
   618  		ledger.Append(genesisBlock)
   619  
   620  		// Before creating the chain, it doesn't exist
   621  		require.Nil(t, manager.GetChain("mychannel"))
   622  		// After creating the chain, it exists
   623  		manager.CreateChain("mychannel")
   624  		chain := manager.GetChain("mychannel")
   625  		require.NotNil(t, chain)
   626  
   627  		list := manager.ChannelList()
   628  		require.Equal(
   629  			t,
   630  			types.ChannelList{
   631  				SystemChannel: &types.ChannelInfoShort{Name: "testchannelid", URL: ""},
   632  				Channels:      []types.ChannelInfoShort{{Name: "mychannel", URL: ""}},
   633  			},
   634  			list,
   635  		)
   636  
   637  		info, err := manager.ChannelInfo("testchannelid")
   638  		require.NoError(t, err)
   639  		require.Equal(t,
   640  			types.ChannelInfo{Name: "testchannelid", URL: "", ConsensusRelation: types.ConsensusRelationConsenter, Status: types.StatusActive, Height: 1},
   641  			info,
   642  		)
   643  
   644  		info, err = manager.ChannelInfo("mychannel")
   645  		require.NoError(t, err)
   646  		require.Equal(t,
   647  			types.ChannelInfo{Name: "mychannel", URL: "", ConsensusRelation: types.ConsensusRelationConsenter, Status: types.StatusActive, Height: 1},
   648  			info,
   649  		)
   650  
   651  		// A subsequent creation, replaces the chain.
   652  		manager.CreateChain("mychannel")
   653  		chain2 := manager.GetChain("mychannel")
   654  		require.NotNil(t, chain2)
   655  		// They are not the same
   656  		require.NotEqual(t, chain, chain2)
   657  		// The old chain is halted
   658  		_, ok := <-chain.Chain.(*mockChainCluster).queue
   659  		require.False(t, ok)
   660  
   661  		// The new chain is not halted: Close the channel to prove that.
   662  		close(chain2.Chain.(*mockChainCluster).queue)
   663  	})
   664  
   665  	t.Run("chain of type etcdraft.Chain is already created", func(t *testing.T) {
   666  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   667  		require.NoError(t, err)
   668  		defer os.RemoveAll(tmpdir)
   669  
   670  		lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   671  
   672  		consenter := &mocks.Consenter{}
   673  		consenter.HandleChainCalls(handleChain)
   674  		consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   675  
   676  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   677  		manager.Initialize(consenters)
   678  
   679  		testChainSupport := &ChainSupport{Chain: &etcdraft.Chain{}}
   680  		manager.chains["test"] = testChainSupport
   681  
   682  		orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir())
   683  		envConfigUpdate, err := encoder.MakeChannelCreationTransaction("test", mockCrypto(), orglessChannelConf)
   684  		require.NoError(t, err, "Constructing chain creation tx")
   685  
   686  		manager.newChain(envConfigUpdate)
   687  
   688  		testChainSupport2 := manager.GetChain("test")
   689  		require.NotNil(t, testChainSupport2)
   690  
   691  		assert.Same(t, testChainSupport, testChainSupport2)
   692  	})
   693  
   694  	// This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain
   695  	t.Run("New chain", func(t *testing.T) {
   696  		expectedLastConfigSeq := uint64(1)
   697  		newChainID := "test-new-chain"
   698  
   699  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   700  		require.NoError(t, err)
   701  		defer os.RemoveAll(tmpdir)
   702  
   703  		lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   704  
   705  		consenter := &mocks.Consenter{}
   706  		consenter.HandleChainCalls(handleChain)
   707  		consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   708  
   709  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   710  		manager.Initialize(consenters)
   711  		orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir())
   712  		orglessChannelConf.Application.Organizations = nil
   713  		envConfigUpdate, err := encoder.MakeChannelCreationTransaction(newChainID, mockCrypto(), orglessChannelConf)
   714  		require.NoError(t, err, "Constructing chain creation tx")
   715  
   716  		res, err := manager.NewChannelConfig(envConfigUpdate)
   717  		require.NoError(t, err, "Constructing initial channel config")
   718  
   719  		configEnv, err := res.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate)
   720  		require.NoError(t, err, "Proposing initial update")
   721  		require.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq)
   722  
   723  		ingressTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch)
   724  		require.NoError(t, err, "Creating ingresstx")
   725  
   726  		wrapped := wrapConfigTx(ingressTx)
   727  
   728  		chainSupport := manager.GetChain(manager.SystemChannelID())
   729  		require.NotNilf(t, chainSupport, "Could not find system channel")
   730  
   731  		chainSupport.Configure(wrapped, 0)
   732  		func() {
   733  			it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   734  			defer it.Close()
   735  			block, status := it.Next()
   736  			if status != cb.Status_SUCCESS {
   737  				t.Fatalf("Could not retrieve block")
   738  			}
   739  			if len(block.Data.Data) != 1 {
   740  				t.Fatalf("Should have had only one message in the orderer transaction block")
   741  			}
   742  
   743  			require.True(t, proto.Equal(wrapped, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Orderer config block contains wrong transaction")
   744  		}()
   745  
   746  		chainSupport = manager.GetChain(newChainID)
   747  		if chainSupport == nil {
   748  			t.Fatalf("Should have gotten new chain which was created")
   749  		}
   750  
   751  		messages := make([]*cb.Envelope, confSys.Orderer.BatchSize.MaxMessageCount)
   752  		for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ {
   753  			messages[i] = makeNormalTx(newChainID, i)
   754  		}
   755  
   756  		for _, message := range messages {
   757  			chainSupport.Order(message, 0)
   758  		}
   759  
   760  		it, _ := chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}})
   761  		defer it.Close()
   762  		block, status := it.Next()
   763  		if status != cb.Status_SUCCESS {
   764  			t.Fatalf("Could not retrieve new chain genesis block")
   765  		}
   766  		if len(block.Data.Data) != 1 {
   767  			t.Fatalf("Should have had only one message in the new genesis block")
   768  		}
   769  
   770  		require.True(t, proto.Equal(ingressTx, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Genesis block contains wrong transaction")
   771  
   772  		block, status = it.Next()
   773  		if status != cb.Status_SUCCESS {
   774  			t.Fatalf("Could not retrieve block on new chain")
   775  		}
   776  		for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ {
   777  			if !proto.Equal(protoutil.ExtractEnvelopeOrPanic(block, i), messages[i]) {
   778  				t.Errorf("Block contents wrong at index %d in new chain", i)
   779  			}
   780  		}
   781  
   782  		cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   783  		require.NoError(t, err)
   784  		rcs, err := newChainSupport(manager, chainSupport.ledgerResources, consenters, mockCrypto(), blockcutter.NewMetrics(&disabled.Provider{}), cryptoProvider)
   785  		require.NoError(t, err)
   786  		require.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq")
   787  	})
   788  }
   789  
   790  func TestResourcesCheck(t *testing.T) {
   791  	mockOrderer := &mocks.OrdererConfig{}
   792  	mockOrdererCaps := &mocks.OrdererCapabilities{}
   793  	mockOrderer.CapabilitiesReturns(mockOrdererCaps)
   794  	mockChannel := &mocks.ChannelConfig{}
   795  	mockChannelCaps := &mocks.ChannelCapabilities{}
   796  	mockChannel.CapabilitiesReturns(mockChannelCaps)
   797  
   798  	mockResources := &mocks.Resources{}
   799  	mockResources.PolicyManagerReturns(&policies.ManagerImpl{})
   800  
   801  	t.Run("GoodResources", func(t *testing.T) {
   802  		mockResources.OrdererConfigReturns(mockOrderer, true)
   803  		mockResources.ChannelConfigReturns(mockChannel)
   804  
   805  		err := checkResources(mockResources)
   806  		require.NoError(t, err)
   807  	})
   808  
   809  	t.Run("MissingOrdererConfigPanic", func(t *testing.T) {
   810  		mockResources.OrdererConfigReturns(nil, false)
   811  
   812  		err := checkResources(mockResources)
   813  		require.Error(t, err)
   814  		require.Regexp(t, "config does not contain orderer config", err.Error())
   815  	})
   816  
   817  	t.Run("MissingOrdererCapability", func(t *testing.T) {
   818  		mockResources.OrdererConfigReturns(mockOrderer, true)
   819  		mockOrdererCaps.SupportedReturns(errors.New("An error"))
   820  
   821  		err := checkResources(mockResources)
   822  		require.Error(t, err)
   823  		require.Regexp(t, "config requires unsupported orderer capabilities:", err.Error())
   824  
   825  		// reset
   826  		mockOrdererCaps.SupportedReturns(nil)
   827  	})
   828  
   829  	t.Run("MissingChannelCapability", func(t *testing.T) {
   830  		mockChannelCaps.SupportedReturns(errors.New("An error"))
   831  
   832  		err := checkResources(mockResources)
   833  		require.Error(t, err)
   834  		require.Regexp(t, "config requires unsupported channel capabilities:", err.Error())
   835  	})
   836  
   837  	t.Run("MissingOrdererConfigPanic", func(t *testing.T) {
   838  		mockResources.OrdererConfigReturns(nil, false)
   839  
   840  		require.Panics(t, func() {
   841  			checkResourcesOrPanic(mockResources)
   842  		})
   843  	})
   844  }
   845  
   846  // The registrar's BroadcastChannelSupport implementation should reject message types which should not be processed directly.
   847  func TestBroadcastChannelSupport(t *testing.T) {
   848  	// system channel
   849  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   850  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   851  
   852  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   853  	require.NoError(t, err)
   854  
   855  	t.Run("Rejection", func(t *testing.T) {
   856  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   857  		require.NoError(t, err)
   858  		defer os.RemoveAll(tmpdir)
   859  
   860  		ledgerFactory, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   861  		consenter := &mocks.Consenter{}
   862  		consenter.HandleChainCalls(handleChain)
   863  		mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
   864  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   865  		registrar.Initialize(mockConsenters)
   866  		randomValue := 1
   867  		configTx := makeConfigTx("testchannelid", randomValue)
   868  		_, _, _, err = registrar.BroadcastChannelSupport(configTx)
   869  		require.Error(t, err, "Messages of type HeaderType_CONFIG should return an error.")
   870  	})
   871  
   872  	t.Run("No system channel", func(t *testing.T) {
   873  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   874  		require.NoError(t, err)
   875  		defer os.RemoveAll(tmpdir)
   876  
   877  		ledgerFactory, _ := newLedgerAndFactory(tmpdir, "", nil)
   878  		consenter := &mocks.Consenter{}
   879  		consenter.HandleChainCalls(handleChain)
   880  		mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter, "etcdraft": &mocks.Consenter{}}
   881  		registrar := NewRegistrar(localconfig.TopLevel{
   882  			General: localconfig.General{
   883  				BootstrapMethod: "none",
   884  				GenesisFile:     "",
   885  			},
   886  		}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   887  		registrar.Initialize(mockConsenters)
   888  		configTx := makeConfigTxFull("testchannelid", 1)
   889  		_, _, _, err = registrar.BroadcastChannelSupport(configTx)
   890  		require.Error(t, err)
   891  		require.Equal(t, "channel creation request not allowed because the orderer system channel is not defined", err.Error())
   892  	})
   893  }
   894  
   895  func TestRegistrar_JoinChannel(t *testing.T) {
   896  	var (
   897  		tmpdir              string
   898  		tlsCA               tlsgen.CA
   899  		confAppRaft         *genesisconfig.Profile
   900  		genesisBlockAppRaft *cb.Block
   901  		confSysRaft         *genesisconfig.Profile
   902  		genesisBlockSysRaft *cb.Block
   903  		cryptoProvider      bccsp.BCCSP
   904  		config              localconfig.TopLevel
   905  		dialer              *cluster.PredicateDialer
   906  		ledgerFactory       blockledger.Factory
   907  		consenter           *mocks.Consenter
   908  		mockConsenters      map[string]consensus.Consenter
   909  	)
   910  
   911  	setup := func(t *testing.T) {
   912  		var err error
   913  		tmpdir, err = ioutil.TempDir("", "registrar_test-")
   914  		require.NoError(t, err)
   915  
   916  		tlsCA, err = tlsgen.NewCA()
   917  		require.NoError(t, err)
   918  
   919  		confAppRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
   920  		confAppRaft.Consortiums = nil
   921  		confAppRaft.Consortium = ""
   922  		generateCertificates(t, confAppRaft, tlsCA, tmpdir)
   923  		bootstrapper, err := encoder.NewBootstrapper(confAppRaft)
   924  		require.NoError(t, err, "cannot create bootstrapper")
   925  		genesisBlockAppRaft = bootstrapper.GenesisBlockForChannel("my-raft-channel")
   926  		require.NotNil(t, genesisBlockAppRaft)
   927  
   928  		confSysRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
   929  		generateCertificates(t, confSysRaft, tlsCA, tmpdir)
   930  		bootstrapper, err = encoder.NewBootstrapper(confSysRaft)
   931  		require.NoError(t, err, "cannot create bootstrapper")
   932  		genesisBlockSysRaft = bootstrapper.GenesisBlockForChannel("sys-raft-channel")
   933  		require.NotNil(t, genesisBlockSysRaft)
   934  
   935  		cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   936  		require.NoError(t, err)
   937  
   938  		config = localconfig.TopLevel{
   939  			General: localconfig.General{
   940  				BootstrapMethod: "none",
   941  				Cluster: localconfig.Cluster{
   942  					ReplicationBufferSize:   1,
   943  					ReplicationPullTimeout:  time.Microsecond,
   944  					ReplicationRetryTimeout: time.Microsecond,
   945  					ReplicationMaxRetries:   2,
   946  				},
   947  			},
   948  			ChannelParticipation: localconfig.ChannelParticipation{
   949  				Enabled: true,
   950  			},
   951  			FileLedger: localconfig.FileLedger{
   952  				Location: tmpdir,
   953  			},
   954  		}
   955  
   956  		dialer = &cluster.PredicateDialer{
   957  			Config: comm.ClientConfig{
   958  				SecOpts: comm.SecureOptions{
   959  					Certificate: tlsCA.CertBytes(),
   960  				},
   961  			},
   962  		}
   963  
   964  		ledgerFactory = newFactory(tmpdir)
   965  		consenter = &mocks.Consenter{}
   966  		consenter.HandleChainCalls(handleChainCluster)
   967  		mockConsenters = map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: consenter}
   968  	}
   969  
   970  	cleanup := func() {
   971  		ledgerFactory.Close()
   972  		os.RemoveAll(tmpdir)
   973  	}
   974  
   975  	t.Run("Reject join when removal is occurring", func(t *testing.T) {
   976  		setup(t)
   977  		defer cleanup()
   978  
   979  		newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft)
   980  
   981  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
   982  		registrar.Initialize(mockConsenters)
   983  
   984  		registrar.pendingRemoval["some-app-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusInactive}
   985  
   986  		info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true)
   987  		require.Equal(t, err, types.ErrChannelPendingRemoval)
   988  		require.Equal(t, types.ChannelInfo{}, info)
   989  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "some-app-channel.join")
   990  		_, err = os.Stat(joinBlockPath)
   991  		require.True(t, os.IsNotExist(err))
   992  	})
   993  
   994  	t.Run("Reject join when removal previously failed", func(t *testing.T) {
   995  		setup(t)
   996  		defer cleanup()
   997  
   998  		newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft)
   999  
  1000  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1001  		registrar.Initialize(mockConsenters)
  1002  
  1003  		registrar.pendingRemoval["some-app-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusFailed}
  1004  
  1005  		info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true)
  1006  		require.Equal(t, types.ErrChannelRemovalFailure, err)
  1007  		require.Equal(t, types.ChannelInfo{}, info)
  1008  	})
  1009  
  1010  	t.Run("Reject join when system channel exists", func(t *testing.T) {
  1011  		setup(t)
  1012  		defer cleanup()
  1013  
  1014  		newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft)
  1015  
  1016  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1017  		registrar.Initialize(mockConsenters)
  1018  
  1019  		info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true)
  1020  		require.EqualError(t, err, "system channel exists")
  1021  		require.Equal(t, types.ChannelInfo{}, info)
  1022  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "some-app-channel.join")
  1023  		_, err = os.Stat(joinBlockPath)
  1024  		require.True(t, os.IsNotExist(err))
  1025  	})
  1026  
  1027  	t.Run("Reject join when channel exists", func(t *testing.T) {
  1028  		setup(t)
  1029  		defer cleanup()
  1030  
  1031  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1032  		registrar.Initialize(mockConsenters)
  1033  
  1034  		ledger, err := ledgerFactory.GetOrCreate("my-raft-channel")
  1035  		require.NoError(t, err)
  1036  		ledger.Append(genesisBlockAppRaft)
  1037  
  1038  		// Before creating the chain, it doesn't exist
  1039  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1040  		// After creating the chain, it exists
  1041  		registrar.CreateChain("my-raft-channel")
  1042  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1043  
  1044  		info, err := registrar.JoinChannel("my-raft-channel", &cb.Block{}, true)
  1045  		require.EqualError(t, err, "channel already exists")
  1046  		require.Equal(t, types.ChannelInfo{}, info)
  1047  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-channel.join")
  1048  		_, err = os.Stat(joinBlockPath)
  1049  		require.True(t, os.IsNotExist(err))
  1050  	})
  1051  
  1052  	t.Run("Reject system channel join when app channels exist", func(t *testing.T) {
  1053  		setup(t)
  1054  		defer cleanup()
  1055  
  1056  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1057  		registrar.Initialize(mockConsenters)
  1058  
  1059  		ledger, err := ledgerFactory.GetOrCreate("my-raft-channel")
  1060  		require.NoError(t, err)
  1061  		ledger.Append(genesisBlockAppRaft)
  1062  
  1063  		// Before creating the chain, it doesn't exist
  1064  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1065  		// After creating the chain, it exists
  1066  		registrar.CreateChain("my-raft-channel")
  1067  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1068  
  1069  		info, err := registrar.JoinChannel("sys-channel", &cb.Block{}, false)
  1070  		require.EqualError(t, err, "application channels already exist")
  1071  		require.Equal(t, types.ChannelInfo{}, info)
  1072  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-channel.join")
  1073  		_, err = os.Stat(joinBlockPath)
  1074  		require.True(t, os.IsNotExist(err))
  1075  	})
  1076  
  1077  	t.Run("Join app channel as member without on-boarding", func(t *testing.T) {
  1078  		setup(t)
  1079  		defer cleanup()
  1080  
  1081  		consenter.IsChannelMemberReturns(true, nil)
  1082  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1083  		fakeFields := newFakeMetricsFields()
  1084  		registrar.channelParticipationMetrics = newFakeMetrics(fakeFields)
  1085  
  1086  		t.Run("failure - consenter channel membership error", func(t *testing.T) {
  1087  			badConsenter := &mocks.Consenter{}
  1088  			badConsenter.IsChannelMemberReturns(false, errors.New("apple"))
  1089  			mockConsenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: badConsenter}
  1090  			registrar.Initialize(mockConsenters)
  1091  
  1092  			// Before joining the channel, it doesn't exist
  1093  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1094  			require.Empty(t, ledgerFactory.ChannelIDs())
  1095  
  1096  			_, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1097  			require.EqualError(t, err, "failed to determine cluster membership from join-block: apple")
  1098  
  1099  			// After join failure, check that everything has been cleaned up
  1100  			require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second)
  1101  			require.Empty(t, ledgerFactory.ChannelIDs())
  1102  			joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join")
  1103  			_, err = os.Stat(joinBlockPath)
  1104  			require.True(t, os.IsNotExist(err))
  1105  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1106  			require.Empty(t, ledgerFactory.ChannelIDs())
  1107  		})
  1108  
  1109  		t.Run("failure - consenter error", func(t *testing.T) {
  1110  			badConsenter := &mocks.Consenter{}
  1111  			badConsenter.IsChannelMemberReturns(true, nil)
  1112  			badConsenter.HandleChainReturns(nil, errors.New("banana"))
  1113  			mockConsenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: badConsenter}
  1114  			registrar.Initialize(mockConsenters)
  1115  
  1116  			// Before joining the channel, it doesn't exist
  1117  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1118  			require.Empty(t, ledgerFactory.ChannelIDs())
  1119  
  1120  			_, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1121  			require.EqualError(t, err, "failed to create chain support: error creating consenter for channel: my-raft-channel: banana")
  1122  
  1123  			// After join failure, check that everything has been cleaned up
  1124  			require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second)
  1125  			require.Empty(t, ledgerFactory.ChannelIDs())
  1126  			joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join")
  1127  			_, err = os.Stat(joinBlockPath)
  1128  			require.True(t, os.IsNotExist(err))
  1129  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1130  			require.Empty(t, ledgerFactory.ChannelIDs())
  1131  		})
  1132  
  1133  		t.Run("success", func(t *testing.T) {
  1134  			registrar.Initialize(mockConsenters)
  1135  			// Before joining the channel, it doesn't exist
  1136  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1137  			info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1138  			require.NoError(t, err)
  1139  			require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info)
  1140  			// After creating the channel, it exists
  1141  			require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1142  
  1143  			// ChannelInfo() and ChannelList() are working fine
  1144  			info, err = registrar.ChannelInfo("my-raft-channel")
  1145  			require.NoError(t, err)
  1146  			require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info)
  1147  			channelList := registrar.ChannelList()
  1148  			require.Equal(t, 1, len(channelList.Channels))
  1149  			require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1150  			require.Nil(t, channelList.SystemChannel)
  1151  			joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join")
  1152  			_, err = os.Stat(joinBlockPath)
  1153  			require.True(t, os.IsNotExist(err))
  1154  			checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 1)
  1155  		})
  1156  	})
  1157  
  1158  	t.Run("Join app channel as member with on-boarding", func(t *testing.T) {
  1159  		setup(t)
  1160  		defer cleanup()
  1161  
  1162  		genesisBlockAppRaft.Header.Number = 10
  1163  		consenter.IsChannelMemberReturns(true, nil)
  1164  
  1165  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1166  		registrar.Initialize(mockConsenters)
  1167  
  1168  		// Before join the chain, it doesn't exist
  1169  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1170  
  1171  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1172  		require.NoError(t, err)
  1173  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "onboarding", Height: 0x0}, info)
  1174  		// After creating the follower.Chain, it not in the chains map.
  1175  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1176  
  1177  		// ChannelInfo() and ChannelList() are working fine
  1178  		info, err = registrar.ChannelInfo("my-raft-channel")
  1179  		require.NoError(t, err)
  1180  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "onboarding", Height: 0x0}, info)
  1181  		channelList := registrar.ChannelList()
  1182  		require.Equal(t, 1, len(channelList.Channels))
  1183  		require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1184  		require.Nil(t, channelList.SystemChannel)
  1185  
  1186  		fChain := registrar.GetFollower("my-raft-channel")
  1187  		require.NotNil(t, fChain)
  1188  		fChain.Halt()
  1189  	})
  1190  
  1191  	t.Run("Join app channel as follower, with on-boarding", func(t *testing.T) {
  1192  		setup(t)
  1193  		defer cleanup()
  1194  
  1195  		genesisBlockAppRaft.Header.Number = 10
  1196  		consenter.IsChannelMemberReturns(false, nil)
  1197  
  1198  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1199  		fakeFields := newFakeMetricsFields()
  1200  		registrar.channelParticipationMetrics = newFakeMetrics(fakeFields)
  1201  		registrar.Initialize(mockConsenters)
  1202  
  1203  		// Before join the chain, it doesn't exist
  1204  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1205  
  1206  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1207  		require.NoError(t, err)
  1208  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info)
  1209  		// After creating the follower.Chain, it not in the chains map.
  1210  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1211  		// ChannelInfo() and ChannelList() are working fine
  1212  		info, err = registrar.ChannelInfo("my-raft-channel")
  1213  		require.NoError(t, err)
  1214  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info)
  1215  		channelList := registrar.ChannelList()
  1216  		require.Equal(t, 1, len(channelList.Channels))
  1217  		require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1218  		require.Nil(t, channelList.SystemChannel)
  1219  
  1220  		fChain := registrar.GetFollower("my-raft-channel")
  1221  		require.NotNil(t, fChain)
  1222  		fChain.Halt()
  1223  
  1224  		checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 2, 1)
  1225  	})
  1226  
  1227  	t.Run("Join app channel as follower then switch to member", func(t *testing.T) {
  1228  		setup(t)
  1229  		defer cleanup()
  1230  
  1231  		consenter.IsChannelMemberReturns(false, nil)
  1232  
  1233  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1234  		fakeFields := newFakeMetricsFields()
  1235  		registrar.channelParticipationMetrics = newFakeMetrics(fakeFields)
  1236  		registrar.Initialize(mockConsenters)
  1237  
  1238  		// Before join the chain, it doesn't exist
  1239  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1240  		require.Nil(t, registrar.GetFollower("my-raft-channel"))
  1241  
  1242  		genesisBlockAppRaft.Header.Number = 1
  1243  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1244  		require.NoError(t, err)
  1245  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info)
  1246  
  1247  		// After creating the follower.Chain, it not in the chains map, it is in the followers map.
  1248  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1249  		fChain := registrar.GetFollower("my-raft-channel")
  1250  		require.NotNil(t, fChain)
  1251  		fChain.Halt()
  1252  
  1253  		// join-block exists before switching from follower to member
  1254  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join")
  1255  		_, err = os.Stat(joinBlockPath)
  1256  		require.NoError(t, err)
  1257  		checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 2, 1)
  1258  
  1259  		// Let's assume the follower appended a block
  1260  		genesisBlockAppRaft.Header.Number = 0
  1261  		newLedger(ledgerFactory, "my-raft-channel", genesisBlockAppRaft)
  1262  
  1263  		// Now Switch => a chain is created and the follower removed
  1264  		require.NotPanics(t, func() { registrar.SwitchFollowerToChain("my-raft-channel") })
  1265  		// Now the chain is in the chains map, the follower is gone
  1266  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1267  		require.Nil(t, registrar.GetFollower("my-raft-channel"))
  1268  		// ChannelInfo() and ChannelList() are still working fine
  1269  		info, err = registrar.ChannelInfo("my-raft-channel")
  1270  		require.NoError(t, err)
  1271  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info)
  1272  		channelList := registrar.ChannelList()
  1273  		require.Equal(t, 1, len(channelList.Channels))
  1274  		require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1275  		require.Nil(t, channelList.SystemChannel)
  1276  
  1277  		// join-block removed after switching from follower to member
  1278  		_, err = os.Stat(joinBlockPath)
  1279  		require.True(t, os.IsNotExist(err))
  1280  
  1281  		checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 2)
  1282  	})
  1283  
  1284  	t.Run("Join app channel as member, then switch to follower", func(t *testing.T) {
  1285  		setup(t)
  1286  		defer cleanup()
  1287  
  1288  		consenter.IsChannelMemberReturns(true, nil)
  1289  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1290  		fakeFields := newFakeMetricsFields()
  1291  		registrar.channelParticipationMetrics = newFakeMetrics(fakeFields)
  1292  		registrar.Initialize(mockConsenters)
  1293  
  1294  		// Before join the chain, it doesn't exist
  1295  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1296  
  1297  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1298  		require.NoError(t, err)
  1299  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info)
  1300  		// After creating the chain, it exists
  1301  		cs := registrar.GetChain("my-raft-channel")
  1302  		require.NotNil(t, cs)
  1303  
  1304  		// ChannelInfo() and ChannelList() are working fine
  1305  		info, err = registrar.ChannelInfo("my-raft-channel")
  1306  		require.NoError(t, err)
  1307  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info)
  1308  		channelList := registrar.ChannelList()
  1309  		require.Equal(t, 1, len(channelList.Channels))
  1310  		require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1311  		require.Nil(t, channelList.SystemChannel)
  1312  		checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 1)
  1313  
  1314  		// Let's assume the chain appended another config block
  1315  		genesisBlockAppRaft.Header.PreviousHash = protoutil.BlockHeaderHash(genesisBlockAppRaft.Header)
  1316  		genesisBlockAppRaft.Header.Number = 1
  1317  		require.NoError(t, cs.Append(genesisBlockAppRaft))
  1318  		consenter.IsChannelMemberReturns(false, nil)
  1319  		require.Equal(t, uint64(2), cs.Height())
  1320  
  1321  		// Now halt and switch, as if the orderer was evicted
  1322  		cs.Halt()
  1323  		require.NotPanics(t, func() { registrar.SwitchChainToFollower("my-raft-channel") })
  1324  		// Now the follower is in the followers map, the chain is gone
  1325  		fChain := registrar.GetFollower("my-raft-channel")
  1326  		require.NotNil(t, fChain)
  1327  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1328  		// ChannelInfo() and ChannelList() are still working fine
  1329  		info, err = registrar.ChannelInfo("my-raft-channel")
  1330  		require.NoError(t, err)
  1331  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "active", Height: 0x2}, info)
  1332  		channelList = registrar.ChannelList()
  1333  		require.Equal(t, 1, len(channelList.Channels))
  1334  		require.Equal(t, "my-raft-channel", channelList.Channels[0].Name)
  1335  		require.Nil(t, channelList.SystemChannel)
  1336  		fChain.Halt()
  1337  		require.False(t, fChain.IsRunning())
  1338  		checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 1, 3)
  1339  	})
  1340  
  1341  	t.Run("Join system channel without on-boarding", func(t *testing.T) {
  1342  		setup(t)
  1343  		defer cleanup()
  1344  
  1345  		consenter.IsChannelMemberReturns(true, nil)
  1346  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1347  		registrar.Initialize(mockConsenters)
  1348  
  1349  		// Before join the chain, it doesn't exist
  1350  		require.Nil(t, registrar.GetChain("sys-raft-channel"))
  1351  
  1352  		info, err := registrar.JoinChannel("sys-raft-channel", genesisBlockSysRaft, false)
  1353  		require.NoError(t, err)
  1354  		require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x1}, info)
  1355  		// After creating the chain, it exists
  1356  		cs := registrar.GetChain("sys-raft-channel")
  1357  		require.NotNil(t, cs)
  1358  
  1359  		// join-block exists
  1360  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-raft-channel.join")
  1361  		_, err = os.Stat(joinBlockPath)
  1362  		require.NoError(t, err)
  1363  
  1364  		// ChannelInfo() and ChannelList() are working fine
  1365  		info, err = registrar.ChannelInfo("sys-raft-channel")
  1366  		require.NoError(t, err)
  1367  		require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x1}, info)
  1368  		channelList := registrar.ChannelList()
  1369  		require.Equal(t, 0, len(channelList.Channels))
  1370  		require.NotNil(t, channelList.SystemChannel)
  1371  		require.Equal(t, "sys-raft-channel", channelList.SystemChannel.Name)
  1372  		ledgerRW, err := ledgerFactory.GetOrCreate("sys-raft-channel")
  1373  		require.NoError(t, err)
  1374  		require.Equal(t, uint64(1), ledgerRW.Height(), "block was appended")
  1375  	})
  1376  
  1377  	t.Run("Join system channel with on-boarding", func(t *testing.T) {
  1378  		setup(t)
  1379  		defer cleanup()
  1380  
  1381  		consenter.IsChannelMemberReturns(true, nil)
  1382  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1383  		registrar.Initialize(mockConsenters)
  1384  
  1385  		// Before join the chain, it doesn't exist
  1386  		require.Nil(t, registrar.GetChain("sys-raft-channel"))
  1387  
  1388  		genesisBlockSysRaft.Header.Number = 7
  1389  		info, err := registrar.JoinChannel("sys-raft-channel", genesisBlockSysRaft, false)
  1390  		require.NoError(t, err)
  1391  		require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x0}, info)
  1392  		// After creating the chain, it exists
  1393  		cs := registrar.GetChain("sys-raft-channel")
  1394  		require.NotNil(t, cs)
  1395  
  1396  		// join-block exists
  1397  		joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-raft-channel.join")
  1398  		_, err = os.Stat(joinBlockPath)
  1399  		require.NoError(t, err)
  1400  
  1401  		// ChannelInfo() and ChannelList() are working fine
  1402  		info, err = registrar.ChannelInfo("sys-raft-channel")
  1403  		require.NoError(t, err)
  1404  		require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x0}, info)
  1405  		channelList := registrar.ChannelList()
  1406  		require.Equal(t, 0, len(channelList.Channels))
  1407  		require.NotNil(t, channelList.SystemChannel)
  1408  		require.Equal(t, "sys-raft-channel", channelList.SystemChannel.Name)
  1409  		ledgerRW, err := ledgerFactory.GetOrCreate("sys-raft-channel")
  1410  		require.NoError(t, err)
  1411  		require.Equal(t, uint64(0), ledgerRW.Height(), "block was not appended")
  1412  	})
  1413  }
  1414  
  1415  func checkMetrics(t *testing.T, fakeFields *fakeMetricsFields, expectedLabels []string, expectedRelation, expectedStatus, expectedCallCount int) {
  1416  	require.Equal(t, expectedCallCount, fakeFields.fakeConsensusRelation.SetCallCount())
  1417  	require.Equal(t, float64(expectedRelation), fakeFields.fakeConsensusRelation.SetArgsForCall(expectedCallCount-1))
  1418  	require.Equal(t, expectedCallCount, fakeFields.fakeConsensusRelation.WithCallCount())
  1419  	require.Equal(t, expectedLabels, fakeFields.fakeConsensusRelation.WithArgsForCall(expectedCallCount-1))
  1420  	require.Equal(t, expectedCallCount, fakeFields.fakeStatus.SetCallCount())
  1421  	require.Equal(t, float64(expectedStatus), fakeFields.fakeStatus.SetArgsForCall(expectedCallCount-1))
  1422  	require.Equal(t, expectedCallCount, fakeFields.fakeStatus.WithCallCount())
  1423  	require.Equal(t, expectedLabels, fakeFields.fakeStatus.WithArgsForCall(expectedCallCount-1))
  1424  }
  1425  
  1426  func TestRegistrar_RemoveChannel(t *testing.T) {
  1427  	var (
  1428  		tmpdir              string
  1429  		tlsCA               tlsgen.CA
  1430  		confAppRaft         *genesisconfig.Profile
  1431  		genesisBlockAppRaft *cb.Block
  1432  		confSysRaft         *genesisconfig.Profile
  1433  		genesisBlockSysRaft *cb.Block
  1434  
  1435  		cryptoProvider  bccsp.BCCSP
  1436  		config          localconfig.TopLevel
  1437  		dialer          *cluster.PredicateDialer
  1438  		appBootstrapper *encoder.Bootstrapper
  1439  		ledgerFactory   blockledger.Factory
  1440  		consenter       *mocks.Consenter
  1441  		mockConsenters  map[string]consensus.Consenter
  1442  	)
  1443  
  1444  	setup := func(t *testing.T) {
  1445  		var err error
  1446  		tmpdir, err = ioutil.TempDir("", "remove-channel")
  1447  		require.NoError(t, err)
  1448  
  1449  		tlsCA, err = tlsgen.NewCA()
  1450  		require.NoError(t, err)
  1451  
  1452  		confAppRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
  1453  		confAppRaft.Consortiums = nil
  1454  		confAppRaft.Consortium = ""
  1455  		generateCertificates(t, confAppRaft, tlsCA, tmpdir)
  1456  		appBootstrapper, err = encoder.NewBootstrapper(confAppRaft)
  1457  		require.NoError(t, err, "cannot create bootstrapper")
  1458  		genesisBlockAppRaft = appBootstrapper.GenesisBlockForChannel("my-raft-channel")
  1459  		require.NotNil(t, genesisBlockAppRaft)
  1460  
  1461  		confSysRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir())
  1462  		generateCertificates(t, confSysRaft, tlsCA, tmpdir)
  1463  		sysBootstrapper, err := encoder.NewBootstrapper(confSysRaft)
  1464  		require.NoError(t, err, "cannot create bootstrapper")
  1465  		genesisBlockSysRaft = sysBootstrapper.GenesisBlockForChannel("raft-sys-channel")
  1466  		require.NotNil(t, genesisBlockSysRaft)
  1467  
  1468  		cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
  1469  		require.NoError(t, err)
  1470  
  1471  		config = localconfig.TopLevel{
  1472  			ChannelParticipation: localconfig.ChannelParticipation{
  1473  				Enabled: true,
  1474  			},
  1475  			General: localconfig.General{
  1476  				BootstrapMethod: "none",
  1477  				Cluster: localconfig.Cluster{
  1478  					ReplicationBufferSize:   1,
  1479  					ReplicationPullTimeout:  time.Microsecond,
  1480  					ReplicationRetryTimeout: time.Microsecond,
  1481  					ReplicationMaxRetries:   2,
  1482  				},
  1483  			},
  1484  			FileLedger: localconfig.FileLedger{
  1485  				Location: tmpdir,
  1486  			},
  1487  		}
  1488  		dialer = &cluster.PredicateDialer{
  1489  			Config: comm.ClientConfig{
  1490  				SecOpts: comm.SecureOptions{
  1491  					Certificate: tlsCA.CertBytes(),
  1492  				},
  1493  			},
  1494  		}
  1495  
  1496  		ledgerFactory = newFactory(tmpdir)
  1497  		consenter = &mocks.Consenter{}
  1498  		consenter.HandleChainCalls(handleChainCluster)
  1499  		mockConsenters = map[string]consensus.Consenter{"etcdraft": consenter}
  1500  	}
  1501  
  1502  	cleanup := func() {
  1503  		ledgerFactory.Close()
  1504  		os.RemoveAll(tmpdir)
  1505  	}
  1506  
  1507  	t.Run("kafka system channel exists", func(t *testing.T) {
  1508  		setup(t)
  1509  		defer cleanup()
  1510  
  1511  		confSysKafka := genesisconfig.Load(genesisconfig.SampleInsecureKafkaProfile, configtest.GetDevConfigDir())
  1512  		genesisBlockSysKafka := encoder.New(confSysKafka).GenesisBlockForChannel("kafka-sys-channel")
  1513  		newLedger(ledgerFactory, "kafka-sys-channel", genesisBlockSysKafka)
  1514  
  1515  		kafkaConsenter := &mocks.Consenter{}
  1516  		kafkaConsenter.HandleChainCalls(handleChain)
  1517  		mockConsenters["kafka"] = kafkaConsenter
  1518  
  1519  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1520  		registrar.Initialize(mockConsenters)
  1521  
  1522  		t.Run("reject removal of app channel", func(t *testing.T) {
  1523  			err := registrar.RemoveChannel("some-app-channel")
  1524  			require.EqualError(t, err, "system channel exists")
  1525  		})
  1526  
  1527  		t.Run("reject removal of kafka system channel", func(t *testing.T) {
  1528  			err := registrar.RemoveChannel("kafka-sys-channel")
  1529  			require.EqualError(t, err, "cannot remove kafka system channel: kafka-sys-channel")
  1530  		})
  1531  	})
  1532  
  1533  	t.Run("without a system channel failures", func(t *testing.T) {
  1534  		setup(t)
  1535  		defer cleanup()
  1536  
  1537  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
  1538  		registrar.Initialize(mockConsenters)
  1539  
  1540  		t.Run("when channel id does not exist", func(t *testing.T) {
  1541  			err := registrar.RemoveChannel("some-raft-channel")
  1542  			require.EqualError(t, err, "channel does not exist")
  1543  		})
  1544  
  1545  		t.Run("when channel id is blank", func(t *testing.T) {
  1546  			err := registrar.RemoveChannel("")
  1547  			require.EqualError(t, err, "channel does not exist")
  1548  		})
  1549  	})
  1550  
  1551  	t.Run("remove app channel", func(t *testing.T) {
  1552  		setup(t)
  1553  		defer cleanup()
  1554  
  1555  		t.Run("consenter", func(t *testing.T) {
  1556  			consenter.IsChannelMemberReturns(true, nil)
  1557  			registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1558  			registrar.Initialize(mockConsenters)
  1559  
  1560  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1561  			require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1562  			info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1563  			require.NoError(t, err)
  1564  			require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info)
  1565  			require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1566  			require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1567  
  1568  			err = registrar.RemoveChannel("my-raft-channel")
  1569  			require.NoError(t, err)
  1570  
  1571  			// After removing the channel, it no longer exists in the registrar or the ledger
  1572  			require.Nil(t, registrar.GetChain("my-raft-channel"))
  1573  			require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second)
  1574  			require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1575  		})
  1576  
  1577  		t.Run("follower", func(t *testing.T) {
  1578  			consenter.IsChannelMemberReturns(false, nil)
  1579  			registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1580  			registrar.Initialize(mockConsenters)
  1581  
  1582  			genesisBlockAppRaftFollower := appBootstrapper.GenesisBlockForChannel("my-follower-raft-channel")
  1583  			require.NotNil(t, genesisBlockAppRaftFollower)
  1584  
  1585  			require.Nil(t, registrar.GetChain("my-follower-raft-channel"))
  1586  			require.NotContains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel")
  1587  			info, err := registrar.JoinChannel("my-follower-raft-channel", genesisBlockAppRaftFollower, true)
  1588  			require.NoError(t, err)
  1589  			require.Equal(t, types.ChannelInfo{Name: "my-follower-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0}, info)
  1590  			require.NotNil(t, registrar.GetFollower("my-follower-raft-channel"))
  1591  			require.Contains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel")
  1592  
  1593  			err = registrar.RemoveChannel("my-follower-raft-channel")
  1594  			require.NoError(t, err)
  1595  
  1596  			// After removing the channel, it no longer exists in the registrar or the ledger
  1597  			require.Nil(t, registrar.GetFollower("my-follower-raft-channel"))
  1598  			require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second)
  1599  			require.NotContains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel")
  1600  
  1601  			channelInfo, err := registrar.ChannelInfo("my-follower-raft-channel")
  1602  			require.Equal(t, err, types.ErrChannelNotExist)
  1603  			require.Equal(t, channelInfo, types.ChannelInfo{})
  1604  		})
  1605  	})
  1606  
  1607  	t.Run("remove system channel", func(t *testing.T) {
  1608  		setup(t)
  1609  		defer cleanup()
  1610  
  1611  		newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft)
  1612  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1613  		registrar.Initialize(mockConsenters)
  1614  		require.NotNil(t, registrar.GetChain("raft-sys-channel"))
  1615  		require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1616  
  1617  		createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaft, "my-raft-channel")
  1618  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1619  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1620  		genesisBlockAppRaftNonMember := appBootstrapper.GenesisBlockForChannel("channel-im-not-a-member-of")
  1621  		require.NotNil(t, genesisBlockAppRaftNonMember)
  1622  		createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaftNonMember, "channel-im-not-a-member-of")
  1623  
  1624  		require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of"))
  1625  		require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of")
  1626  		consenter.IsChannelMemberStub = func(b *cb.Block) (bool, error) {
  1627  			if bytes.Equal(b.Header.DataHash, genesisBlockAppRaft.Header.DataHash) {
  1628  				return true, nil
  1629  			}
  1630  			return false, nil
  1631  		}
  1632  
  1633  		err := registrar.RemoveChannel("raft-sys-channel")
  1634  		require.NoError(t, err)
  1635  
  1636  		// After removing the system channel, it no longer exists in the registrar or the ledger
  1637  		require.Empty(t, registrar.SystemChannelID())
  1638  		require.Nil(t, registrar.SystemChannel())
  1639  		require.Nil(t, registrar.GetChain("raft-sys-channel"))
  1640  		require.NotContains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1641  
  1642  		// application channel members still exist
  1643  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1644  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1645  		// channels the orderer wasn't a member no longer exist
  1646  		require.Nil(t, registrar.GetChain("channel-im-not-a-member-of"))
  1647  		require.NotContains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of")
  1648  	})
  1649  
  1650  	t.Run("remove system channel ledger fails", func(t *testing.T) {
  1651  		setup(t)
  1652  		defer cleanup()
  1653  
  1654  		newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft)
  1655  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1656  		registrar.Initialize(mockConsenters)
  1657  		require.NotNil(t, registrar.GetChain("raft-sys-channel"))
  1658  		require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1659  
  1660  		os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove"))
  1661  
  1662  		err := registrar.RemoveChannel("raft-sys-channel")
  1663  		require.Error(t, err)
  1664  		require.Contains(t, err.Error(), "failed removing ledger for system channel raft-sys-channel")
  1665  
  1666  		require.NotNil(t, registrar.SystemChannelID())
  1667  		require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1668  	})
  1669  
  1670  	t.Run("remove non-systemchannel members fails", func(t *testing.T) {
  1671  		setup(t)
  1672  		defer cleanup()
  1673  
  1674  		newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft)
  1675  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1676  		registrar.Initialize(mockConsenters)
  1677  		require.NotNil(t, registrar.GetChain("raft-sys-channel"))
  1678  		require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1679  
  1680  		genesisBlockAppRaftNonMember := appBootstrapper.GenesisBlockForChannel("channel-im-not-a-member-of")
  1681  		require.NotNil(t, genesisBlockAppRaftNonMember)
  1682  		createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaftNonMember, "channel-im-not-a-member-of")
  1683  
  1684  		require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of"))
  1685  		require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of")
  1686  		consenter.IsChannelMemberStub = func(b *cb.Block) (bool, error) {
  1687  			os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove"))
  1688  			if bytes.Equal(b.Header.DataHash, genesisBlockAppRaft.Header.DataHash) {
  1689  				return true, nil
  1690  			}
  1691  			return false, nil
  1692  		}
  1693  
  1694  		err := registrar.RemoveChannel("raft-sys-channel")
  1695  		require.Error(t, err)
  1696  		require.Contains(t, err.Error(), "failed removing ledger for channel(s): channel-im-not-a-member-of")
  1697  
  1698  		require.Empty(t, registrar.SystemChannelID())
  1699  		require.Nil(t, registrar.SystemChannel())
  1700  		require.Nil(t, registrar.GetChain("raft-sys-channel"))
  1701  		require.NotContains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel")
  1702  
  1703  		require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of"))
  1704  		require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of")
  1705  	})
  1706  
  1707  	t.Run("app channel is already being removed", func(t *testing.T) {
  1708  		setup(t)
  1709  		defer cleanup()
  1710  
  1711  		consenter.IsChannelMemberReturns(true, nil)
  1712  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1713  		registrar.Initialize(mockConsenters)
  1714  
  1715  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1716  		require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1717  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1718  		require.NoError(t, err)
  1719  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info)
  1720  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1721  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1722  
  1723  		registrar.pendingRemoval["my-raft-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusInactive}
  1724  
  1725  		err = registrar.RemoveChannel("my-raft-channel")
  1726  		require.Error(t, err)
  1727  		require.Equal(t, types.ErrChannelPendingRemoval, err)
  1728  
  1729  		require.Contains(t, registrar.ChannelList().Channels, types.ChannelInfoShort{Name: "my-raft-channel"})
  1730  	})
  1731  
  1732  	t.Run("app channel removal previously failed", func(t *testing.T) {
  1733  		setup(t)
  1734  		defer cleanup()
  1735  
  1736  		consenter.IsChannelMemberReturns(true, nil)
  1737  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1738  		registrar.Initialize(mockConsenters)
  1739  
  1740  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1741  		require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1742  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1743  		require.NoError(t, err)
  1744  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info)
  1745  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1746  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1747  
  1748  		registrar.pendingRemoval["my-raft-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusFailed}
  1749  
  1750  		err = registrar.RemoveChannel("my-raft-channel")
  1751  		require.NoError(t, err)
  1752  
  1753  		require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second)
  1754  		require.NotContains(t, registrar.ChannelList().Channels, types.ChannelInfoShort{Name: "my-raft-channel"})
  1755  	})
  1756  
  1757  	t.Run("remove channel fails", func(t *testing.T) {
  1758  		setup(t)
  1759  		defer cleanup()
  1760  
  1761  		consenter.IsChannelMemberReturns(true, nil)
  1762  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer)
  1763  		registrar.Initialize(mockConsenters)
  1764  
  1765  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1766  		require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1767  		info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true)
  1768  		require.NoError(t, err)
  1769  		require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info)
  1770  		require.NotNil(t, registrar.GetChain("my-raft-channel"))
  1771  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1772  
  1773  		// This will force the failure of the underlying factory.Remove() because the pendingops/remove will not exist
  1774  		os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove"))
  1775  
  1776  		err = registrar.RemoveChannel("my-raft-channel")
  1777  		require.NoError(t, err)
  1778  
  1779  		require.Nil(t, registrar.GetChain("my-raft-channel"))
  1780  		require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 1 }, time.Minute, time.Second)
  1781  		require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel")
  1782  
  1783  		// Confirm removal failure by checking channel status
  1784  		channelInfo, err := registrar.ChannelInfo("my-raft-channel")
  1785  		require.NoError(t, err)
  1786  		require.Equal(t, channelInfo, types.ChannelInfo{
  1787  			Name:              "my-raft-channel",
  1788  			ConsensusRelation: types.ConsensusRelationConsenter,
  1789  			Status:            types.StatusFailed,
  1790  		})
  1791  	})
  1792  }
  1793  
  1794  func generateCertificates(t *testing.T, confAppRaft *genesisconfig.Profile, tlsCA tlsgen.CA, certDir string) {
  1795  	for i, c := range confAppRaft.Orderer.EtcdRaft.Consenters {
  1796  		srvC, err := tlsCA.NewServerCertKeyPair(c.Host)
  1797  		require.NoError(t, err)
  1798  		srvP := path.Join(certDir, fmt.Sprintf("server%d.crt", i))
  1799  		err = ioutil.WriteFile(srvP, srvC.Cert, 0o644)
  1800  		require.NoError(t, err)
  1801  
  1802  		clnC, err := tlsCA.NewClientCertKeyPair()
  1803  		require.NoError(t, err)
  1804  		clnP := path.Join(certDir, fmt.Sprintf("client%d.crt", i))
  1805  		err = ioutil.WriteFile(clnP, clnC.Cert, 0o644)
  1806  		require.NoError(t, err)
  1807  
  1808  		c.ServerTlsCert = []byte(srvP)
  1809  		c.ClientTlsCert = []byte(clnP)
  1810  	}
  1811  }
  1812  
  1813  func createLedgerAndChain(t *testing.T, r *Registrar, lf blockledger.Factory, b *cb.Block, channel string) {
  1814  	require.Nil(t, r.GetChain(channel))
  1815  	require.NotContains(t, lf.ChannelIDs(), channel)
  1816  	ledger, err := lf.GetOrCreate(channel)
  1817  	require.NoError(t, err)
  1818  	ledger.Append(b)
  1819  	require.Contains(t, lf.ChannelIDs(), channel)
  1820  	r.CreateChain(channel)
  1821  	require.NotNil(t, r.GetChain(channel))
  1822  }
  1823  
  1824  func TestRegistrar_ConfigBlockOrPanic(t *testing.T) {
  1825  	t.Run("Panics when ledger is empty", func(t *testing.T) {
  1826  		tmpdir, err := ioutil.TempDir("", "file-ledger")
  1827  		require.NoError(t, err)
  1828  		defer os.RemoveAll(tmpdir)
  1829  
  1830  		_, l := newLedgerAndFactory(tmpdir, "testchannelid", nil)
  1831  
  1832  		require.PanicsWithValue(t, "Failed to retrieve block", func() {
  1833  			ConfigBlockOrPanic(l)
  1834  		})
  1835  	})
  1836  
  1837  	t.Run("Panics when config block not complete", func(t *testing.T) {
  1838  		block := protoutil.NewBlock(0, nil)
  1839  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("bad metadata")
  1840  
  1841  		tmpdir, err := ioutil.TempDir("", "file-ledger")
  1842  		require.NoError(t, err)
  1843  		defer os.RemoveAll(tmpdir)
  1844  
  1845  		_, l := newLedgerAndFactory(tmpdir, "testchannelid", block)
  1846  
  1847  		require.PanicsWithValue(t, "Chain did not have appropriately encoded last config in its latest block", func() {
  1848  			ConfigBlockOrPanic(l)
  1849  		})
  1850  	})
  1851  
  1852  	t.Run("Panics when block referenes invalid config block", func(t *testing.T) {
  1853  		block := protoutil.NewBlock(0, nil)
  1854  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{
  1855  			Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
  1856  				LastConfig: &cb.LastConfig{Index: 2},
  1857  			}),
  1858  		})
  1859  
  1860  		tmpdir, err := ioutil.TempDir("", "file-ledger")
  1861  		require.NoError(t, err)
  1862  		defer os.RemoveAll(tmpdir)
  1863  
  1864  		_, l := newLedgerAndFactory(tmpdir, "testchannelid", block)
  1865  
  1866  		require.PanicsWithValue(t, "Failed to retrieve config block", func() {
  1867  			ConfigBlockOrPanic(l)
  1868  		})
  1869  	})
  1870  
  1871  	t.Run("Returns valid config block", func(t *testing.T) {
  1872  		confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
  1873  		genesisBlockSys := encoder.New(confSys).GenesisBlock()
  1874  
  1875  		tmpdir, err := ioutil.TempDir("", "file-ledger")
  1876  		require.NoError(t, err)
  1877  		defer os.RemoveAll(tmpdir)
  1878  
  1879  		_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
  1880  
  1881  		cBlock := ConfigBlockOrPanic(l)
  1882  		assert.Equal(t, genesisBlockSys.Header, cBlock.Header)
  1883  		assert.Equal(t, genesisBlockSys.Data, cBlock.Data)
  1884  	})
  1885  }