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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package server
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/golang/protobuf/proto"
    22  	"github.com/hyperledger/fabric-protos-go/common"
    23  	"github.com/hyperledger/fabric-protos-go/orderer"
    24  	"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    25  	"github.com/hyperledger/fabric/bccsp/sw"
    26  	"github.com/hyperledger/fabric/common/channelconfig"
    27  	"github.com/hyperledger/fabric/common/configtx"
    28  	deliver_mocks "github.com/hyperledger/fabric/common/deliver/mock"
    29  	"github.com/hyperledger/fabric/common/flogging"
    30  	ledger_mocks "github.com/hyperledger/fabric/common/ledger/blockledger/mocks"
    31  	"github.com/hyperledger/fabric/core/comm"
    32  	"github.com/hyperledger/fabric/core/config/configtest"
    33  	"github.com/hyperledger/fabric/internal/pkg/identity"
    34  	"github.com/hyperledger/fabric/orderer/common/cluster"
    35  	"github.com/hyperledger/fabric/orderer/common/cluster/mocks"
    36  	"github.com/hyperledger/fabric/orderer/common/localconfig"
    37  	server_mocks "github.com/hyperledger/fabric/orderer/common/server/mocks"
    38  	"github.com/hyperledger/fabric/protoutil"
    39  	. "github.com/onsi/gomega"
    40  	"github.com/onsi/gomega/gbytes"
    41  	"github.com/onsi/gomega/gexec"
    42  	"github.com/pkg/errors"
    43  	"github.com/stretchr/testify/assert"
    44  	"github.com/stretchr/testify/mock"
    45  	"go.uber.org/zap"
    46  	"go.uber.org/zap/zapcore"
    47  )
    48  
    49  // the paths to configtxgen and cryptogen, which can be used by tests to create
    50  // genesis blocks and certificates
    51  var configtxgen, cryptogen, tempDir string
    52  
    53  func TestMain(m *testing.M) {
    54  	var err error
    55  	configtxgen, err = gexec.Build("github.com/hyperledger/fabric/cmd/configtxgen")
    56  	if err != nil {
    57  		os.Exit(-1)
    58  	}
    59  	cryptogen, err = gexec.Build("github.com/hyperledger/fabric/cmd/cryptogen")
    60  	if err != nil {
    61  		os.Exit(-1)
    62  	}
    63  	defer gexec.CleanupBuildArtifacts()
    64  
    65  	tempDir, err = ioutil.TempDir("", "onboarding-test")
    66  	defer os.RemoveAll(tempDir)
    67  
    68  	copyYamlFiles("testdata", tempDir)
    69  
    70  	os.Exit(m.Run())
    71  }
    72  
    73  func copyYamlFiles(src, dst string) {
    74  	for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} {
    75  		fileBytes, err := ioutil.ReadFile(filepath.Join(src, file))
    76  		if err != nil {
    77  			os.Exit(-1)
    78  		}
    79  		err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0644)
    80  		if err != nil {
    81  			os.Exit(-1)
    82  		}
    83  	}
    84  }
    85  
    86  func newServerNode(t *testing.T, key, cert []byte) *deliverServer {
    87  	srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{
    88  		SecOpts: comm.SecureOptions{
    89  			Key:         key,
    90  			Certificate: cert,
    91  			UseTLS:      true,
    92  		},
    93  	})
    94  	if err != nil {
    95  		panic(err)
    96  	}
    97  	ds := &deliverServer{
    98  		t:              t,
    99  		blockResponses: make(chan *orderer.DeliverResponse, 100),
   100  		srv:            srv,
   101  	}
   102  	orderer.RegisterAtomicBroadcastServer(srv.Server(), ds)
   103  	go srv.Start()
   104  	return ds
   105  }
   106  
   107  type deliverServer struct {
   108  	isConnected    int32
   109  	t              *testing.T
   110  	srv            *comm.GRPCServer
   111  	blockResponses chan *orderer.DeliverResponse
   112  }
   113  
   114  func (*deliverServer) Broadcast(orderer.AtomicBroadcast_BroadcastServer) error {
   115  	panic("implement me")
   116  }
   117  
   118  func (ds *deliverServer) Deliver(stream orderer.AtomicBroadcast_DeliverServer) error {
   119  	atomic.StoreInt32(&ds.isConnected, 1)
   120  	seekInfo, err := readSeekEnvelope(stream)
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  	if seekInfo.GetStart().GetSpecified() != nil {
   125  		return ds.deliverBlocks(stream)
   126  	}
   127  	if seekInfo.GetStart().GetNewest() != nil {
   128  		resp := <-ds.blockResponses
   129  		if resp == nil {
   130  			return nil
   131  		}
   132  		return stream.Send(resp)
   133  	}
   134  	panic(fmt.Sprintf("expected either specified or newest seek but got %v", seekInfo.GetStart()))
   135  }
   136  
   137  func readSeekEnvelope(stream orderer.AtomicBroadcast_DeliverServer) (*orderer.SeekInfo, error) {
   138  	env, err := stream.Recv()
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	seekInfo := &orderer.SeekInfo{}
   147  	if err = proto.Unmarshal(payload.Data, seekInfo); err != nil {
   148  		return nil, err
   149  	}
   150  	return seekInfo, nil
   151  }
   152  
   153  func (ds *deliverServer) deliverBlocks(stream orderer.AtomicBroadcast_DeliverServer) error {
   154  	for {
   155  		blockChan := ds.blockResponses
   156  		response := <-blockChan
   157  		if response == nil {
   158  			return nil
   159  		}
   160  		if err := stream.Send(response); err != nil {
   161  			return err
   162  		}
   163  	}
   164  }
   165  
   166  func loadPEM(cryptoPath, suffix string, t *testing.T) []byte {
   167  	ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls")
   168  	b, err := ioutil.ReadFile(filepath.Join(ordererTLSPath, suffix))
   169  	assert.NoError(t, err)
   170  	return b
   171  }
   172  
   173  func channelCreationBlock(systemChannel, applicationChannel string, prevBlock *common.Block) *common.Block {
   174  	block := &common.Block{
   175  		Header: &common.BlockHeader{
   176  			Number:       prevBlock.Header.Number + 1,
   177  			PreviousHash: protoutil.BlockHeaderHash(prevBlock.Header),
   178  		},
   179  		Metadata: &common.BlockMetadata{
   180  			Metadata: [][]byte{{}, {}, {}, {}},
   181  		},
   182  		Data: &common.BlockData{
   183  			Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{
   184  				Payload: protoutil.MarshalOrPanic(&common.Payload{
   185  					Header: &common.Header{
   186  						ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
   187  							ChannelId: systemChannel,
   188  							Type:      int32(common.HeaderType_ORDERER_TRANSACTION),
   189  						}),
   190  					},
   191  					Data: protoutil.MarshalOrPanic(&common.Envelope{
   192  						Payload: protoutil.MarshalOrPanic(&common.Payload{
   193  							Header: &common.Header{
   194  								ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
   195  									Type:      int32(common.HeaderType_CONFIG),
   196  									ChannelId: applicationChannel,
   197  								}),
   198  							},
   199  						}),
   200  					}),
   201  				}),
   202  			})},
   203  		},
   204  	}
   205  
   206  	block.Header.DataHash = protoutil.BlockDataHash(block.Data)
   207  	return block
   208  }
   209  
   210  func TestOnboardingChannelUnavailable(t *testing.T) {
   211  	// Scenario: During the probing phase of the onboarding,
   212  	// a channel is deemed relevant and we try to pull it during the
   213  	// second phase, but alas - precisely at that time - it becomes
   214  	// unavailable.
   215  	// The onboarding code is expected to skip the replication after
   216  	// the maximum attempt number is exhausted, and not to try to replicate
   217  	// the channel indefinitely.
   218  	cryptoPath := generateCryptoMaterials(t, cryptogen)
   219  	defer os.RemoveAll(cryptoPath)
   220  
   221  	caCert := loadPEM(cryptoPath, "ca.crt", t)
   222  	key := loadPEM(cryptoPath, "server.key", t)
   223  	cert := loadPEM(cryptoPath, "server.crt", t)
   224  
   225  	deliverServer := newServerNode(t, key, cert)
   226  	defer deliverServer.srv.Stop()
   227  
   228  	systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel")
   229  	systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath)
   230  	assert.NoError(t, err)
   231  
   232  	applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "testchannel", "SampleOrgChannel")
   233  	applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath)
   234  	assert.NoError(t, err)
   235  
   236  	testchannelGB := &common.Block{}
   237  	assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, testchannelGB))
   238  	testchannelGB.Header.Number = 0
   239  
   240  	systemChannelGenesisBlock := &common.Block{
   241  		Header: &common.BlockHeader{
   242  			Number: 0,
   243  		},
   244  		Metadata: &common.BlockMetadata{
   245  			Metadata: [][]byte{{}, {}, {}},
   246  		},
   247  		Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{
   248  			Payload: protoutil.MarshalOrPanic(&common.Payload{
   249  				Header: &common.Header{},
   250  			}),
   251  		})}},
   252  	}
   253  	systemChannelGenesisBlock.Header.DataHash = protoutil.BlockDataHash(systemChannelGenesisBlock.Data)
   254  
   255  	channelCreationBlock := channelCreationBlock("system", "testchannel", systemChannelGenesisBlock)
   256  
   257  	bootBlock := &common.Block{}
   258  	assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, bootBlock))
   259  	bootBlock.Header.Number = 2
   260  	bootBlock.Header.PreviousHash = protoutil.BlockHeaderHash(channelCreationBlock.Header)
   261  	injectOrdererEndpoint(t, bootBlock, deliverServer.srv.Address())
   262  	injectConsenterCertificate(t, testchannelGB, cert)
   263  
   264  	blocksCommittedToSystemLedger := make(chan uint64, 3)
   265  	blocksCommittedToApplicationLedger := make(chan uint64, 1)
   266  
   267  	systemLedger := &mocks.LedgerWriter{}
   268  	systemLedger.On("Height").Return(uint64(0))
   269  	systemLedger.On("Append", mock.Anything).Return(nil).Run(func(arguments mock.Arguments) {
   270  		seq := arguments.Get(0).(*common.Block).Header.Number
   271  		blocksCommittedToSystemLedger <- seq
   272  	})
   273  
   274  	appLedger := &mocks.LedgerWriter{}
   275  	appLedger.On("Height").Return(uint64(0))
   276  	appLedger.On("Append", mock.Anything).Return(nil).Run(func(arguments mock.Arguments) {
   277  		seq := arguments.Get(0).(*common.Block).Header.Number
   278  		blocksCommittedToApplicationLedger <- seq
   279  	})
   280  
   281  	lf := &mocks.LedgerFactory{}
   282  	lf.On("GetOrCreate", "system").Return(systemLedger, nil)
   283  	lf.On("GetOrCreate", "testchannel").Return(appLedger, nil)
   284  	lf.On("Close")
   285  
   286  	config := &localconfig.TopLevel{
   287  		General: localconfig.General{
   288  			Cluster: localconfig.Cluster{
   289  				ReplicationPullTimeout:  time.Hour,
   290  				DialTimeout:             time.Hour,
   291  				RPCTimeout:              time.Hour,
   292  				ReplicationRetryTimeout: time.Millisecond,
   293  				ReplicationBufferSize:   1,
   294  				ReplicationMaxRetries:   5,
   295  			},
   296  		},
   297  	}
   298  
   299  	secConfig := comm.SecureOptions{
   300  		Certificate:   cert,
   301  		Key:           key,
   302  		UseTLS:        true,
   303  		ServerRootCAs: [][]byte{caCert},
   304  	}
   305  
   306  	verifier := &mocks.BlockVerifier{}
   307  	verifier.On("VerifyBlockSignature", mock.Anything, mock.Anything).Return(nil)
   308  	vr := &mocks.VerifierRetriever{}
   309  	vr.On("RetrieveVerifier", mock.Anything).Return(verifier)
   310  
   311  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   312  	assert.NoError(t, err)
   313  
   314  	r := &replicationInitiator{
   315  		verifierRetriever: vr,
   316  		lf:                lf,
   317  		logger:            flogging.MustGetLogger("testOnboarding"),
   318  		conf:              config,
   319  		secOpts:           secConfig,
   320  		cryptoProvider:    cryptoProvider,
   321  	}
   322  
   323  	type event struct {
   324  		expectedLog  string
   325  		responseFunc func(chan *orderer.DeliverResponse)
   326  	}
   327  
   328  	var probe, pullSystemChannel, pullAppChannel, failedPulling bool
   329  
   330  	var loggerHooks []zap.Option
   331  
   332  	for _, e := range []event{
   333  		{
   334  			expectedLog: "Probing whether I should pull channel testchannel",
   335  			responseFunc: func(blockResponses chan *orderer.DeliverResponse) {
   336  				probe = true
   337  
   338  				// At this point the client will re-connect, so close the stream.
   339  				blockResponses <- nil
   340  				// And send the genesis block of the application channel 'testchannel'
   341  				blockResponses <- &orderer.DeliverResponse{
   342  					Type: &orderer.DeliverResponse_Block{
   343  						Block: testchannelGB,
   344  					},
   345  				}
   346  				blockResponses <- &orderer.DeliverResponse{
   347  					Type: &orderer.DeliverResponse_Block{
   348  						Block: testchannelGB,
   349  					},
   350  				}
   351  				blockResponses <- &orderer.DeliverResponse{
   352  					Type: &orderer.DeliverResponse_Block{
   353  						Block: testchannelGB,
   354  					},
   355  				}
   356  				blockResponses <- nil
   357  				blockResponses <- &orderer.DeliverResponse{
   358  					Type: &orderer.DeliverResponse_Block{
   359  						Block: testchannelGB,
   360  					},
   361  				}
   362  				blockResponses <- &orderer.DeliverResponse{
   363  					Type: &orderer.DeliverResponse_Block{
   364  						Block: testchannelGB,
   365  					},
   366  				}
   367  				blockResponses <- nil
   368  			},
   369  		},
   370  		{
   371  			expectedLog: "Pulling channel system",
   372  			responseFunc: func(blockResponses chan *orderer.DeliverResponse) {
   373  				pullSystemChannel = true
   374  
   375  				blockResponses <- &orderer.DeliverResponse{
   376  					Type: &orderer.DeliverResponse_Block{
   377  						Block: bootBlock,
   378  					},
   379  				}
   380  				blockResponses <- &orderer.DeliverResponse{
   381  					Type: &orderer.DeliverResponse_Block{
   382  						Block: bootBlock,
   383  					},
   384  				}
   385  				blockResponses <- &orderer.DeliverResponse{
   386  					Type: &orderer.DeliverResponse_Block{
   387  						Block: systemChannelGenesisBlock,
   388  					},
   389  				}
   390  				blockResponses <- &orderer.DeliverResponse{
   391  					Type: &orderer.DeliverResponse_Block{
   392  						Block: channelCreationBlock,
   393  					},
   394  				}
   395  				blockResponses <- &orderer.DeliverResponse{
   396  					Type: &orderer.DeliverResponse_Block{
   397  						Block: bootBlock,
   398  					},
   399  				}
   400  			},
   401  		},
   402  		{
   403  			expectedLog: "Pulling channel testchannel",
   404  			responseFunc: func(blockResponses chan *orderer.DeliverResponse) {
   405  				pullAppChannel = true
   406  
   407  				for i := 0; i < config.General.Cluster.ReplicationMaxRetries+1; i++ {
   408  					// Send once the genesis block, to make the client think this is a valid OSN endpoint
   409  					deliverServer.blockResponses <- &orderer.DeliverResponse{
   410  						Type: &orderer.DeliverResponse_Block{
   411  							Block: testchannelGB,
   412  						},
   413  					}
   414  					// Send EOF to make the client abort and retry again
   415  					deliverServer.blockResponses <- nil
   416  				}
   417  			},
   418  		},
   419  		{
   420  			expectedLog: "Failed pulling channel testchannel: retry attempts exhausted",
   421  			responseFunc: func(blockResponses chan *orderer.DeliverResponse) {
   422  				failedPulling = true
   423  			},
   424  		},
   425  	} {
   426  		event := e
   427  		loggerHooks = append(loggerHooks, zap.Hooks(func(entry zapcore.Entry) error {
   428  			if strings.Contains(entry.Message, event.expectedLog) {
   429  				event.responseFunc(deliverServer.blockResponses)
   430  			}
   431  			return nil
   432  		}))
   433  	}
   434  
   435  	// Program the logger to intercept the event
   436  	r.logger = r.logger.WithOptions(loggerHooks...)
   437  
   438  	// Send the latest system channel block (sequence 2) that is identical to the bootstrap block
   439  	deliverServer.blockResponses <- &orderer.DeliverResponse{
   440  		Type: &orderer.DeliverResponse_Block{
   441  			Block: bootBlock,
   442  		},
   443  	}
   444  	// Send the bootstrap block of the system channel
   445  	deliverServer.blockResponses <- &orderer.DeliverResponse{
   446  		Type: &orderer.DeliverResponse_Block{
   447  			Block: systemChannelGenesisBlock,
   448  		},
   449  	}
   450  	// Send a channel creation block (sequence 1) that denotes creation of 'testchannel'
   451  	deliverServer.blockResponses <- &orderer.DeliverResponse{
   452  		Type: &orderer.DeliverResponse_Block{
   453  			Block: channelCreationBlock,
   454  		},
   455  	}
   456  
   457  	r.replicateIfNeeded(bootBlock)
   458  
   459  	// Ensure all events were invoked
   460  	assert.True(t, probe)
   461  	assert.True(t, pullSystemChannel)
   462  	assert.True(t, pullAppChannel)
   463  	assert.True(t, failedPulling)
   464  
   465  	// Ensure system channel was fully pulled
   466  	assert.Len(t, blocksCommittedToSystemLedger, 3)
   467  	// But the application channel only contains 1 block (the genesis block)
   468  	assert.Len(t, blocksCommittedToApplicationLedger, 1)
   469  }
   470  
   471  func TestReplicate(t *testing.T) {
   472  	clusterConfig := localconfig.Cluster{
   473  		ReplicationPullTimeout:  time.Hour,
   474  		DialTimeout:             time.Hour,
   475  		RPCTimeout:              time.Hour,
   476  		ReplicationRetryTimeout: time.Hour,
   477  		ReplicationBufferSize:   1,
   478  	}
   479  
   480  	var bootBlock common.Block
   481  	var bootBlockWithCorruptedPayload common.Block
   482  
   483  	flogging.ActivateSpec("testReplicateIfNeeded=debug")
   484  
   485  	cleanup := configtest.SetDevFabricConfigPath(t)
   486  	defer cleanup()
   487  
   488  	cryptoPath := generateCryptoMaterials(t, cryptogen)
   489  	defer os.RemoveAll(cryptoPath)
   490  
   491  	applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "testchannel", "SampleOrgChannel")
   492  	applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath)
   493  	assert.NoError(t, err)
   494  
   495  	caCert := loadPEM(cryptoPath, "ca.crt", t)
   496  	key := loadPEM(cryptoPath, "server.key", t)
   497  	cert := loadPEM(cryptoPath, "server.crt", t)
   498  
   499  	prepareTestCase := func() *deliverServer {
   500  		deliverServer := newServerNode(t, key, cert)
   501  
   502  		assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, &bootBlock))
   503  		bootBlock.Header.Number = 10
   504  		injectOrdererEndpoint(t, &bootBlock, deliverServer.srv.Address())
   505  
   506  		copyBlock := func(block *common.Block, seq uint64) common.Block {
   507  			res := common.Block{}
   508  			assert.NoError(t, proto.Unmarshal(protoutil.MarshalOrPanic(block), &res))
   509  			res.Header.Number = seq
   510  			return res
   511  		}
   512  
   513  		bootBlockWithCorruptedPayload = copyBlock(&bootBlock, 100)
   514  		env := &common.Envelope{}
   515  		assert.NoError(t, proto.Unmarshal(bootBlockWithCorruptedPayload.Data.Data[0], env))
   516  		payload := &common.Payload{}
   517  		assert.NoError(t, proto.Unmarshal(env.Payload, payload))
   518  		payload.Data = []byte{1, 2, 3}
   519  
   520  		deliverServer.blockResponses <- &orderer.DeliverResponse{
   521  			Type: &orderer.DeliverResponse_Block{Block: &bootBlock},
   522  		}
   523  
   524  		blocks := make([]*common.Block, 11)
   525  		for seq := uint64(0); seq <= uint64(10); seq++ {
   526  			block := copyBlock(&bootBlock, seq)
   527  			if seq > 0 {
   528  				block.Header.PreviousHash = protoutil.BlockHeaderHash(blocks[seq-1].Header)
   529  			}
   530  			blocks[seq] = &block
   531  			deliverServer.blockResponses <- &orderer.DeliverResponse{
   532  				Type: &orderer.DeliverResponse_Block{Block: &block},
   533  			}
   534  		}
   535  		// We close the block responses to mark the server side to return from
   536  		// the method dispatch.
   537  		close(deliverServer.blockResponses)
   538  
   539  		// We need to ensure the hash chain is valid with respect to the bootstrap block.
   540  		// Validating the hash chain itself when we traverse channels will be taken care
   541  		// of in FAB-12926.
   542  		bootBlock.Header.PreviousHash = protoutil.BlockHeaderHash(blocks[9].Header)
   543  		return deliverServer
   544  	}
   545  
   546  	var hooksActivated bool
   547  
   548  	for _, testCase := range []struct {
   549  		name               string
   550  		panicValue         string
   551  		systemLedgerHeight uint64
   552  		bootBlock          *common.Block
   553  		secOpts            comm.SecureOptions
   554  		conf               *localconfig.TopLevel
   555  		ledgerFactoryErr   error
   556  		signer             identity.SignerSerializer
   557  		zapHooks           []func(zapcore.Entry) error
   558  		shouldConnect      bool
   559  		replicateFunc      func(*replicationInitiator, *common.Block)
   560  		verificationCount  int
   561  	}{
   562  		{
   563  			name:               "Genesis block makes replication be skipped",
   564  			bootBlock:          &common.Block{Header: &common.BlockHeader{Number: 0}},
   565  			systemLedgerHeight: 10,
   566  			zapHooks: []func(entry zapcore.Entry) error{
   567  				func(entry zapcore.Entry) error {
   568  					hooksActivated = true
   569  					assert.Equal(t, entry.Message, "Booted with a genesis block, replication isn't an option")
   570  					return nil
   571  				},
   572  			},
   573  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   574  				ri.replicateIfNeeded(bootstrapBlock)
   575  			},
   576  		},
   577  		{
   578  			name:               "Block puller initialization failure panics",
   579  			systemLedgerHeight: 10,
   580  			panicValue:         "Failed creating puller config from bootstrap block: unable to decode TLS certificate PEM: ",
   581  			bootBlock:          &bootBlockWithCorruptedPayload,
   582  			conf:               &localconfig.TopLevel{},
   583  			secOpts:            comm.SecureOptions{},
   584  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   585  				ri.replicateIfNeeded(bootstrapBlock)
   586  			},
   587  		},
   588  		{
   589  			name:               "Extraction of system channel name fails",
   590  			systemLedgerHeight: 10,
   591  			panicValue:         "Failed extracting system channel name from bootstrap block: failed to retrieve channel id - block is empty",
   592  			bootBlock:          &common.Block{Header: &common.BlockHeader{Number: 100}},
   593  			conf:               &localconfig.TopLevel{},
   594  			secOpts:            comm.SecureOptions{},
   595  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   596  				ri.replicateIfNeeded(bootstrapBlock)
   597  			},
   598  		},
   599  		{
   600  			name:               "Is Replication needed fails",
   601  			systemLedgerHeight: 10,
   602  			ledgerFactoryErr:   errors.New("I/O error"),
   603  			panicValue:         "Failed determining whether replication is needed: I/O error",
   604  			bootBlock:          &bootBlock,
   605  			conf:               &localconfig.TopLevel{},
   606  			secOpts: comm.SecureOptions{
   607  				Certificate: cert,
   608  				Key:         key,
   609  			},
   610  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   611  				ri.replicateIfNeeded(bootstrapBlock)
   612  			},
   613  		},
   614  		{
   615  			name:               "Replication isn't needed",
   616  			systemLedgerHeight: 11,
   617  			bootBlock:          &bootBlock,
   618  			conf:               &localconfig.TopLevel{},
   619  			secOpts: comm.SecureOptions{
   620  				Certificate: cert,
   621  				Key:         key,
   622  			},
   623  			zapHooks: []func(entry zapcore.Entry) error{
   624  				func(entry zapcore.Entry) error {
   625  					hooksActivated = true
   626  					assert.Equal(t, entry.Message, "Replication isn't needed")
   627  					return nil
   628  				},
   629  			},
   630  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   631  				ri.replicateIfNeeded(bootstrapBlock)
   632  			},
   633  		},
   634  		{
   635  			name: "Replication is needed, but pulling fails",
   636  			panicValue: "Failed pulling system channel: " +
   637  				"failed obtaining the latest block for channel testchannel",
   638  			shouldConnect:      true,
   639  			systemLedgerHeight: 10,
   640  			bootBlock:          &bootBlock,
   641  			conf: &localconfig.TopLevel{
   642  				General: localconfig.General{
   643  					Cluster: clusterConfig,
   644  				},
   645  			},
   646  			secOpts: comm.SecureOptions{
   647  				Certificate:   cert,
   648  				Key:           key,
   649  				UseTLS:        true,
   650  				ServerRootCAs: [][]byte{caCert},
   651  			},
   652  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   653  				ri.replicateIfNeeded(bootstrapBlock)
   654  			},
   655  		},
   656  		{
   657  			name:               "Explicit replication is requested, but the channel shouldn't be pulled",
   658  			verificationCount:  10,
   659  			shouldConnect:      true,
   660  			systemLedgerHeight: 10,
   661  			bootBlock:          &bootBlock,
   662  			conf: &localconfig.TopLevel{
   663  				General: localconfig.General{
   664  					Cluster: clusterConfig,
   665  				},
   666  			},
   667  			secOpts: comm.SecureOptions{
   668  				Certificate:   cert,
   669  				Key:           key,
   670  				UseTLS:        true,
   671  				ServerRootCAs: [][]byte{caCert},
   672  			},
   673  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   674  				ri.ReplicateChains(bootstrapBlock, []string{"foo"})
   675  			},
   676  			zapHooks: []func(entry zapcore.Entry) error{
   677  				func(entry zapcore.Entry) error {
   678  					possibleLogs := []string{
   679  						"Will now replicate chains [foo]",
   680  						"Channel testchannel shouldn't be pulled. Skipping it",
   681  					}
   682  					for _, possibleLog := range possibleLogs {
   683  						if entry.Message == possibleLog {
   684  							hooksActivated = true
   685  						}
   686  					}
   687  					return nil
   688  				},
   689  			},
   690  		},
   691  		{
   692  			name: "Explicit replication is requested, but the channel cannot be pulled",
   693  			panicValue: "Failed pulling system channel: " +
   694  				"failed obtaining the latest block for channel testchannel",
   695  			shouldConnect:      true,
   696  			systemLedgerHeight: 10,
   697  			bootBlock:          &bootBlock,
   698  			conf: &localconfig.TopLevel{
   699  				General: localconfig.General{
   700  					Cluster: clusterConfig,
   701  				},
   702  			},
   703  			secOpts: comm.SecureOptions{
   704  				Certificate:   cert,
   705  				Key:           key,
   706  				UseTLS:        true,
   707  				ServerRootCAs: [][]byte{caCert},
   708  			},
   709  			replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) {
   710  				ri.ReplicateChains(bootstrapBlock, []string{"testchannel"})
   711  			},
   712  		},
   713  	} {
   714  		testCase := testCase
   715  		t.Run(testCase.name, func(t *testing.T) {
   716  			deliverServer := prepareTestCase()
   717  			defer deliverServer.srv.Stop()
   718  
   719  			lw := &mocks.LedgerWriter{}
   720  			lw.On("Height").Return(testCase.systemLedgerHeight).Once()
   721  
   722  			lf := &mocks.LedgerFactory{}
   723  			lf.On("GetOrCreate", mock.Anything).Return(lw, testCase.ledgerFactoryErr).Twice()
   724  			lf.On("Close")
   725  
   726  			verifier := &mocks.BlockVerifier{}
   727  			verifier.On("VerifyBlockSignature", mock.Anything, mock.Anything).Return(nil)
   728  			vr := &mocks.VerifierRetriever{}
   729  			vr.On("RetrieveVerifier", mock.Anything).Return(verifier)
   730  
   731  			cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   732  			assert.NoError(t, err)
   733  
   734  			r := &replicationInitiator{
   735  				verifierRetriever: vr,
   736  				lf:                lf,
   737  				logger:            flogging.MustGetLogger("testReplicateIfNeeded"),
   738  				signer:            testCase.signer,
   739  
   740  				conf:           testCase.conf,
   741  				secOpts:        testCase.secOpts,
   742  				cryptoProvider: cryptoProvider,
   743  			}
   744  
   745  			if testCase.panicValue != "" {
   746  				assert.PanicsWithValue(t, testCase.panicValue, func() {
   747  					testCase.replicateFunc(r, testCase.bootBlock)
   748  				})
   749  				return
   750  			}
   751  
   752  			// Else, we are not expected to panic.
   753  			r.logger = r.logger.WithOptions(zap.Hooks(testCase.zapHooks...))
   754  
   755  			// This is the method we're testing.
   756  			testCase.replicateFunc(r, testCase.bootBlock)
   757  
   758  			// Ensure we ran the hooks for a test case that doesn't panic
   759  			assert.True(t, hooksActivated)
   760  			// Restore the flag for the next iteration
   761  			defer func() {
   762  				hooksActivated = false
   763  			}()
   764  
   765  			assert.Equal(t, testCase.shouldConnect, atomic.LoadInt32(&deliverServer.isConnected) == int32(1))
   766  			verifier.AssertNumberOfCalls(t, "VerifyBlockSignature", testCase.verificationCount)
   767  		})
   768  	}
   769  }
   770  
   771  func TestInactiveChainReplicator(t *testing.T) {
   772  	for _, testCase := range []struct {
   773  		description                          string
   774  		chainsTracked                        []string
   775  		ReplicateChainsExpectedInput1        []string
   776  		ReplicateChainsExpectedInput1Reverse []string
   777  		ReplicateChainsExpectedInput2        []string
   778  		ReplicateChainsOutput1               []string
   779  		ReplicateChainsOutput2               []string
   780  		chainsExpectedToBeReplicated         []string
   781  		expectedRegisteredChains             map[string]struct{}
   782  		ReplicateChainsExpectedCallCount     int
   783  		genesisBlock                         *common.Block
   784  	}{
   785  		{
   786  			description:              "no chains tracked",
   787  			expectedRegisteredChains: map[string]struct{}{},
   788  		},
   789  		{
   790  			description:                          "some chains tracked, but not all succeed replication",
   791  			chainsTracked:                        []string{"foo", "bar"},
   792  			ReplicateChainsExpectedInput1:        []string{"foo", "bar"},
   793  			ReplicateChainsExpectedInput1Reverse: []string{"bar", "foo"},
   794  			ReplicateChainsExpectedInput2:        []string{"bar"},
   795  			ReplicateChainsOutput1:               []string{"foo"},
   796  			ReplicateChainsOutput2:               []string{},
   797  			chainsExpectedToBeReplicated:         []string{"foo"},
   798  			ReplicateChainsExpectedCallCount:     2,
   799  			genesisBlock:                         &common.Block{},
   800  			expectedRegisteredChains: map[string]struct{}{
   801  				"foo": {},
   802  				"bar": {},
   803  			},
   804  		},
   805  		{
   806  			description:                          "some chains tracked, and all succeed replication but on 2nd pass",
   807  			chainsTracked:                        []string{"foo", "bar"},
   808  			ReplicateChainsExpectedInput1:        []string{"foo", "bar"},
   809  			ReplicateChainsExpectedInput1Reverse: []string{"bar", "foo"},
   810  			ReplicateChainsExpectedInput2:        []string{"bar"},
   811  			ReplicateChainsOutput1:               []string{"foo"},
   812  			ReplicateChainsOutput2:               []string{"bar"},
   813  			chainsExpectedToBeReplicated:         []string{"foo", "bar"},
   814  			ReplicateChainsExpectedCallCount:     2,
   815  			genesisBlock:                         &common.Block{},
   816  			expectedRegisteredChains: map[string]struct{}{
   817  				"foo": {},
   818  				"bar": {},
   819  			},
   820  		},
   821  	} {
   822  		t.Run(testCase.description, func(t *testing.T) {
   823  			registeredChains := make(map[string]struct{})
   824  			registerChain := func(chain string) {
   825  				registeredChains[chain] = struct{}{}
   826  			}
   827  			scheduler := make(chan time.Time)
   828  			replicator := &server_mocks.ChainReplicator{}
   829  			icr := &inactiveChainReplicator{
   830  				registerChain:            registerChain,
   831  				logger:                   flogging.MustGetLogger("test"),
   832  				replicator:               replicator,
   833  				chains2CreationCallbacks: make(map[string]chainCreation),
   834  				retrieveLastSysChannelConfigBlock: func() *common.Block {
   835  					return nil
   836  				},
   837  				quitChan:     make(chan struct{}),
   838  				scheduleChan: scheduler,
   839  			}
   840  
   841  			trackedChains := make(chan string, 10)
   842  			// Simulate starting of a chain by simply adding it to a channel
   843  			for _, trackedChain := range testCase.chainsTracked {
   844  				trackedChain := trackedChain
   845  				icr.TrackChain(trackedChain, testCase.genesisBlock, func() {
   846  					trackedChains <- trackedChain
   847  				})
   848  			}
   849  
   850  			// First pass
   851  			input := testCase.ReplicateChainsExpectedInput1
   852  			output := testCase.ReplicateChainsOutput1
   853  			replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once()
   854  			// the input might be called in reverse order
   855  			input = testCase.ReplicateChainsExpectedInput1Reverse
   856  			replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once()
   857  
   858  			// Second pass
   859  			input = testCase.ReplicateChainsExpectedInput2
   860  			output = testCase.ReplicateChainsOutput2
   861  			replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once()
   862  
   863  			var replicatorStopped sync.WaitGroup
   864  			replicatorStopped.Add(1)
   865  			go func() {
   866  				defer replicatorStopped.Done()
   867  				// trigger to replicate the first time
   868  				scheduler <- time.Time{}
   869  				// trigger to replicate a second time
   870  				scheduler <- time.Time{}
   871  				icr.stop()
   872  			}()
   873  			icr.run()
   874  			replicatorStopped.Wait()
   875  			close(trackedChains)
   876  
   877  			var replicatedChains []string
   878  			for chain := range trackedChains {
   879  				replicatedChains = append(replicatedChains, chain)
   880  			}
   881  			assert.Equal(t, testCase.chainsExpectedToBeReplicated, replicatedChains)
   882  			replicator.AssertNumberOfCalls(t, "ReplicateChains", testCase.ReplicateChainsExpectedCallCount)
   883  			assert.Equal(t, testCase.expectedRegisteredChains, registeredChains)
   884  		})
   885  	}
   886  }
   887  
   888  func TestInactiveChainReplicatorChannels(t *testing.T) {
   889  	icr := &inactiveChainReplicator{
   890  		logger:                   flogging.MustGetLogger("test"),
   891  		chains2CreationCallbacks: make(map[string]chainCreation),
   892  	}
   893  	icr.TrackChain("foo", &common.Block{}, func() {})
   894  	assert.Contains(t, icr.Channels(), cluster.ChannelGenesisBlock{ChannelName: "foo", GenesisBlock: &common.Block{}})
   895  
   896  	icr.TrackChain("bar", nil, func() {})
   897  	assert.Contains(t, icr.Channels(), cluster.ChannelGenesisBlock{ChannelName: "bar", GenesisBlock: nil})
   898  
   899  	icr.Close()
   900  }
   901  
   902  func injectConsenterCertificate(t *testing.T, block *common.Block, tlsCert []byte) {
   903  	env, err := protoutil.ExtractEnvelope(block, 0)
   904  	assert.NoError(t, err)
   905  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   906  	assert.NoError(t, err)
   907  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   908  	assert.NoError(t, err)
   909  	consensus := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Values[channelconfig.ConsensusTypeKey]
   910  	consensus.Value = protoutil.MarshalOrPanic(&orderer.ConsensusType{
   911  		Type: "etcdraft",
   912  		Metadata: protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{
   913  			Consenters: []*etcdraft.Consenter{
   914  				{
   915  					ServerTlsCert: tlsCert,
   916  					ClientTlsCert: tlsCert,
   917  				},
   918  			},
   919  		}),
   920  	})
   921  
   922  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   923  	env.Payload = protoutil.MarshalOrPanic(payload)
   924  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   925  	block.Header.DataHash = protoutil.BlockDataHash(block.Data)
   926  }
   927  
   928  func injectOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) {
   929  	ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint})
   930  	// Unwrap the layers until we reach the orderer addresses
   931  	env, err := protoutil.ExtractEnvelope(block, 0)
   932  	assert.NoError(t, err)
   933  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   934  	assert.NoError(t, err)
   935  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   936  	assert.NoError(t, err)
   937  	// Replace the orderer addresses
   938  	confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()].Value = protoutil.MarshalOrPanic(ordererAddresses.Value())
   939  	// And put it back into the block
   940  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   941  	env.Payload = protoutil.MarshalOrPanic(payload)
   942  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   943  	block.Header.DataHash = protoutil.BlockDataHash(block.Data)
   944  }
   945  
   946  func TestVerifierLoader(t *testing.T) {
   947  	cryptoPath := generateCryptoMaterials(t, cryptogen)
   948  	defer os.RemoveAll(cryptoPath)
   949  
   950  	systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel")
   951  	systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath)
   952  	assert.NoError(t, err)
   953  
   954  	configBlock := &common.Block{}
   955  	assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, configBlock))
   956  
   957  	verifier := &mocks.BlockVerifier{}
   958  
   959  	for _, testCase := range []struct {
   960  		description               string
   961  		ledgerGetOrCreateErr      error
   962  		ledgerHeight              uint64
   963  		lastBlock                 *common.Block
   964  		lastConfigBlock           *common.Block
   965  		verifierFromConfigReturns cluster.BlockVerifier
   966  		verifierFromConfigErr     error
   967  		onFailureInvoked          bool
   968  		expectedPanic             string
   969  		expectedLoggedMessages    map[string]struct{}
   970  		expectedResult            verifiersByChannel
   971  	}{
   972  		{
   973  			description:          "obtaining ledger fails",
   974  			ledgerGetOrCreateErr: errors.New("IO error"),
   975  			expectedPanic:        "Failed obtaining ledger for channel mychannel",
   976  		},
   977  		{
   978  			description: "empty ledger",
   979  			expectedLoggedMessages: map[string]struct{}{
   980  				"Channel mychannel has no blocks, skipping it": {},
   981  			},
   982  			expectedResult: make(verifiersByChannel),
   983  		},
   984  		{
   985  			description:   "block retrieval fails",
   986  			ledgerHeight:  100,
   987  			expectedPanic: "Failed retrieving block [99] for channel mychannel",
   988  		},
   989  		{
   990  			description:   "block retrieval succeeds but the block is bad",
   991  			ledgerHeight:  100,
   992  			lastBlock:     &common.Block{},
   993  			expectedPanic: "Failed retrieving config block [99] for channel mychannel",
   994  		},
   995  		{
   996  			description:  "config block retrieved is bad",
   997  			ledgerHeight: 100,
   998  			lastBlock: &common.Block{
   999  				Metadata: &common.BlockMetadata{
  1000  					Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{
  1001  						Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}),
  1002  					}), {}, {}},
  1003  				},
  1004  			},
  1005  			lastConfigBlock:  &common.Block{Header: &common.BlockHeader{Number: 21}},
  1006  			expectedPanic:    "Failed extracting configuration for channel mychannel from block [21]: empty block",
  1007  			onFailureInvoked: true,
  1008  		},
  1009  		{
  1010  			description:  "VerifierFromConfig fails",
  1011  			ledgerHeight: 100,
  1012  			lastBlock: &common.Block{
  1013  				Metadata: &common.BlockMetadata{
  1014  					Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{
  1015  						Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}),
  1016  					}), {}, {}},
  1017  				},
  1018  			},
  1019  			lastConfigBlock:       configBlock,
  1020  			verifierFromConfigErr: errors.New("failed initializing MSP"),
  1021  			expectedPanic:         "Failed creating verifier for channel mychannel from block [99]: failed initializing MSP",
  1022  			onFailureInvoked:      true,
  1023  		},
  1024  		{
  1025  			description:  "VerifierFromConfig succeeds",
  1026  			ledgerHeight: 100,
  1027  			lastBlock: &common.Block{
  1028  				Metadata: &common.BlockMetadata{
  1029  					Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{
  1030  						Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}),
  1031  					}), {}, {}},
  1032  				},
  1033  			},
  1034  			lastConfigBlock: configBlock,
  1035  			expectedLoggedMessages: map[string]struct{}{
  1036  				"Loaded verifier for channel mychannel from config block at index 99": {},
  1037  			},
  1038  			expectedResult: verifiersByChannel{
  1039  				"mychannel": verifier,
  1040  			},
  1041  		},
  1042  	} {
  1043  		t.Run(testCase.description, func(t *testing.T) {
  1044  			iterator := &deliver_mocks.BlockIterator{}
  1045  			iterator.NextReturnsOnCall(0, testCase.lastBlock, common.Status_SUCCESS)
  1046  			iterator.NextReturnsOnCall(1, testCase.lastConfigBlock, common.Status_SUCCESS)
  1047  
  1048  			ledger := &ledger_mocks.ReadWriter{}
  1049  			ledger.On("Height").Return(testCase.ledgerHeight)
  1050  			ledger.On("Iterator", mock.Anything).Return(iterator, uint64(1))
  1051  
  1052  			ledgerFactory := &server_mocks.Factory{}
  1053  			ledgerFactory.On("GetOrCreate", "mychannel").Return(ledger, testCase.ledgerGetOrCreateErr)
  1054  			ledgerFactory.On("ChannelIDs").Return([]string{"mychannel"})
  1055  
  1056  			verifierFactory := &mocks.VerifierFactory{}
  1057  			verifierFactory.On("VerifierFromConfig", mock.Anything, "mychannel").Return(verifier, testCase.verifierFromConfigErr)
  1058  
  1059  			var onFailureInvoked bool
  1060  			onFailure := func(_ *common.Block) {
  1061  				onFailureInvoked = true
  1062  			}
  1063  
  1064  			verifierLoader := &verifierLoader{
  1065  				onFailure:       onFailure,
  1066  				ledgerFactory:   ledgerFactory,
  1067  				verifierFactory: verifierFactory,
  1068  				logger:          flogging.MustGetLogger("test"),
  1069  			}
  1070  
  1071  			verifierLoader.logger = verifierLoader.logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error {
  1072  				delete(testCase.expectedLoggedMessages, entry.Message)
  1073  				return nil
  1074  			}))
  1075  
  1076  			if testCase.expectedPanic != "" {
  1077  				f := func() {
  1078  					verifierLoader.loadVerifiers()
  1079  				}
  1080  				assert.PanicsWithValue(t, testCase.expectedPanic, f)
  1081  			} else {
  1082  				res := verifierLoader.loadVerifiers()
  1083  				assert.Equal(t, testCase.expectedResult, res)
  1084  			}
  1085  
  1086  			assert.Equal(t, testCase.onFailureInvoked, onFailureInvoked)
  1087  			assert.Empty(t, testCase.expectedLoggedMessages)
  1088  		})
  1089  	}
  1090  }
  1091  
  1092  func TestValidateBootstrapBlock(t *testing.T) {
  1093  	cryptoPath := generateCryptoMaterials(t, cryptogen)
  1094  	defer os.RemoveAll(cryptoPath)
  1095  
  1096  	systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel")
  1097  	systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath)
  1098  	assert.NoError(t, err)
  1099  
  1100  	applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "mychannel", "SampleOrgChannel")
  1101  	applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath)
  1102  	assert.NoError(t, err)
  1103  
  1104  	appBlock := &common.Block{}
  1105  	assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, appBlock))
  1106  
  1107  	systemBlock := &common.Block{}
  1108  	assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, systemBlock))
  1109  
  1110  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
  1111  	assert.NoError(t, err)
  1112  
  1113  	for _, testCase := range []struct {
  1114  		description   string
  1115  		block         *common.Block
  1116  		expectedError string
  1117  	}{
  1118  		{
  1119  			description:   "nil block",
  1120  			expectedError: "nil block",
  1121  		},
  1122  		{
  1123  			description:   "empty block",
  1124  			block:         &common.Block{},
  1125  			expectedError: "empty block data",
  1126  		},
  1127  		{
  1128  			description: "bad envelope",
  1129  			block: &common.Block{
  1130  				Data: &common.BlockData{
  1131  					Data: [][]byte{{1, 2, 3}},
  1132  				},
  1133  			},
  1134  			expectedError: "failed extracting envelope from block: " +
  1135  				"proto: common.Envelope: illegal tag 0 (wire type 1)",
  1136  		},
  1137  		{
  1138  			description:   "application channel block",
  1139  			block:         appBlock,
  1140  			expectedError: "the block isn't a system channel block because it lacks ConsortiumsConfig",
  1141  		},
  1142  		{
  1143  			description: "system channel block",
  1144  			block:       systemBlock,
  1145  		},
  1146  	} {
  1147  		t.Run(testCase.description, func(t *testing.T) {
  1148  			err := ValidateBootstrapBlock(testCase.block, cryptoProvider)
  1149  			if testCase.expectedError == "" {
  1150  				assert.NoError(t, err)
  1151  				return
  1152  			}
  1153  
  1154  			assert.EqualError(t, err, testCase.expectedError)
  1155  		})
  1156  	}
  1157  }
  1158  
  1159  func generateBootstrapBlock(t *testing.T, tempDir, configtxgen, channel, profile string) string {
  1160  	gt := NewGomegaWithT(t)
  1161  	// create a genesis block for the specified channel and profile
  1162  	genesisBlockPath := filepath.Join(tempDir, channel+".block")
  1163  	cmd := exec.Command(
  1164  		configtxgen,
  1165  		"-channelID", channel,
  1166  		"-profile", profile,
  1167  		"-outputBlock", genesisBlockPath,
  1168  		"--configPath", tempDir,
  1169  	)
  1170  	configtxgenProcess, err := gexec.Start(cmd, nil, nil)
  1171  	gt.Expect(err).NotTo(HaveOccurred())
  1172  	gt.Eventually(configtxgenProcess, time.Minute).Should(gexec.Exit(0))
  1173  	gt.Expect(configtxgenProcess.Err).To(gbytes.Say("Writing genesis block"))
  1174  
  1175  	return genesisBlockPath
  1176  }