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