github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/orderer/common/onboarding/onboarding_test.go (about)

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