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

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