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