github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/common/multichannel/manager_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package multichannel
     8  
     9  import (
    10  	"reflect"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hyperledger/fabric/common/config"
    16  	"github.com/hyperledger/fabric/common/configtx"
    17  	genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
    18  	"github.com/hyperledger/fabric/common/configtx/tool/provisional"
    19  	mockcrypto "github.com/hyperledger/fabric/common/mocks/crypto"
    20  	"github.com/hyperledger/fabric/msp"
    21  	"github.com/hyperledger/fabric/orderer/common/ledger"
    22  	ramledger "github.com/hyperledger/fabric/orderer/common/ledger/ram"
    23  	cb "github.com/hyperledger/fabric/protos/common"
    24  	ab "github.com/hyperledger/fabric/protos/orderer"
    25  	"github.com/hyperledger/fabric/protos/utils"
    26  
    27  	mmsp "github.com/hyperledger/fabric/common/mocks/msp"
    28  	logging "github.com/op/go-logging"
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  var conf, singleMSPConf, noConsortiumConf *genesisconfig.Profile
    33  var genesisBlock, singleMSPGenesisBlock, noConsortiumGenesisBlock *cb.Block
    34  var mockSigningIdentity msp.SigningIdentity
    35  
    36  const NoConsortiumChain = "no-consortium-chain"
    37  
    38  func init() {
    39  	logging.SetLevel(logging.DEBUG, "")
    40  	mockSigningIdentity, _ = mmsp.NewNoopMsp().GetDefaultSigningIdentity()
    41  
    42  	conf = genesisconfig.Load(genesisconfig.SampleInsecureProfile)
    43  	genesisBlock = provisional.New(conf).GenesisBlock()
    44  
    45  	singleMSPConf = genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile)
    46  	singleMSPGenesisBlock = provisional.New(singleMSPConf).GenesisBlock()
    47  
    48  	noConsortiumConf = genesisconfig.Load("SampleNoConsortium")
    49  	noConsortiumGenesisBlock = provisional.New(noConsortiumConf).GenesisBlockForChannel(NoConsortiumChain)
    50  }
    51  
    52  func mockCrypto() *mockCryptoHelper {
    53  	return &mockCryptoHelper{LocalSigner: mockcrypto.FakeLocalSigner}
    54  }
    55  
    56  type mockCryptoHelper struct {
    57  	*mockcrypto.LocalSigner
    58  }
    59  
    60  func (mch mockCryptoHelper) VerifySignature(sd *cb.SignedData) error {
    61  	return nil
    62  }
    63  
    64  func NewRAMLedgerAndFactory(maxSize int) (ledger.Factory, ledger.ReadWriter) {
    65  	rlf := ramledger.New(10)
    66  	rl, err := rlf.GetOrCreate(provisional.TestChainID)
    67  	if err != nil {
    68  		panic(err)
    69  	}
    70  	err = rl.Append(genesisBlock)
    71  	if err != nil {
    72  		panic(err)
    73  	}
    74  	return rlf, rl
    75  }
    76  
    77  func NewRAMLedgerAndFactoryWithMSP() (ledger.Factory, ledger.ReadWriter) {
    78  	rlf := ramledger.New(10)
    79  
    80  	rl, err := rlf.GetOrCreate(provisional.TestChainID)
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  	err = rl.Append(singleMSPGenesisBlock)
    85  	if err != nil {
    86  		panic(err)
    87  	}
    88  	return rlf, rl
    89  }
    90  
    91  func NewRAMLedger(maxSize int) ledger.ReadWriter {
    92  	_, rl := NewRAMLedgerAndFactory(maxSize)
    93  	return rl
    94  }
    95  
    96  // Tests for a normal chain which contains 3 config transactions and other normal transactions to make sure the right one returned
    97  func TestGetConfigTx(t *testing.T) {
    98  	rl := NewRAMLedger(10)
    99  	for i := 0; i < 5; i++ {
   100  		rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, i)}))
   101  	}
   102  	rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx(provisional.TestChainID, 5)}))
   103  	ctx := makeConfigTx(provisional.TestChainID, 6)
   104  	rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{ctx}))
   105  
   106  	block := ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)})
   107  	block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = utils.MarshalOrPanic(&cb.Metadata{Value: utils.MarshalOrPanic(&cb.LastConfig{Index: 7})})
   108  	rl.Append(block)
   109  
   110  	pctx := getConfigTx(rl)
   111  	assert.Equal(t, pctx, ctx, "Did not select most recent config transaction")
   112  }
   113  
   114  // Tests a chain which contains blocks with multi-transactions mixed with config txs, and a single tx which is not a config tx, none count as config blocks so nil should return
   115  func TestGetConfigTxFailure(t *testing.T) {
   116  	rl := NewRAMLedger(10)
   117  	for i := 0; i < 10; i++ {
   118  		rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{
   119  			makeNormalTx(provisional.TestChainID, i),
   120  			makeConfigTx(provisional.TestChainID, i),
   121  		}))
   122  	}
   123  	rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 11)}))
   124  	assert.Panics(t, func() { getConfigTx(rl) }, "Should have panicked because there was no config tx")
   125  
   126  	block := ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 12)})
   127  	block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = []byte("bad metadata")
   128  	assert.Panics(t, func() { getConfigTx(rl) }, "Should have panicked because of bad last config metadata")
   129  }
   130  
   131  // This test checks to make sure the orderer refuses to come up if it cannot find a system channel
   132  func TestNoSystemChain(t *testing.T) {
   133  	lf := ramledger.New(10)
   134  
   135  	consenters := make(map[string]Consenter)
   136  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   137  
   138  	assert.Panics(t, func() { NewManagerImpl(lf, consenters, mockCrypto()) }, "Should have panicked when starting without a system chain")
   139  }
   140  
   141  // This test checks to make sure that the orderer refuses to come up if there are multiple system channels
   142  func TestMultiSystemChannel(t *testing.T) {
   143  	lf := ramledger.New(10)
   144  
   145  	for _, id := range []string{"foo", "bar"} {
   146  		rl, err := lf.GetOrCreate(id)
   147  		assert.NoError(t, err)
   148  
   149  		err = rl.Append(provisional.New(conf).GenesisBlockForChannel(id))
   150  		assert.NoError(t, err)
   151  	}
   152  
   153  	consenters := make(map[string]Consenter)
   154  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   155  
   156  	assert.Panics(t, func() { NewManagerImpl(lf, consenters, mockCrypto()) }, "Two system channels should have caused panic")
   157  }
   158  
   159  // This test essentially brings the entire system up and is ultimately what main.go will replicate
   160  func TestManagerImpl(t *testing.T) {
   161  	lf, rl := NewRAMLedgerAndFactory(10)
   162  
   163  	consenters := make(map[string]Consenter)
   164  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   165  
   166  	manager := NewManagerImpl(lf, consenters, mockCrypto())
   167  
   168  	_, ok := manager.GetChain("Fake")
   169  	assert.False(t, ok, "Should not have found a chain that was not created")
   170  
   171  	chainSupport, ok := manager.GetChain(provisional.TestChainID)
   172  	assert.True(t, ok, "Should have gotten chain which was initialized by ramledger")
   173  
   174  	messages := make([]*cb.Envelope, conf.Orderer.BatchSize.MaxMessageCount)
   175  	for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ {
   176  		messages[i] = makeNormalTx(provisional.TestChainID, i)
   177  	}
   178  
   179  	for _, message := range messages {
   180  		chainSupport.Enqueue(message)
   181  	}
   182  
   183  	it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   184  	defer it.Close()
   185  	select {
   186  	case <-it.ReadyChan():
   187  		block, status := it.Next()
   188  		assert.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block")
   189  		for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ {
   190  			assert.Equal(t, messages[i], utils.ExtractEnvelopeOrPanic(block, i), "Block contents wrong at index %d", i)
   191  		}
   192  	case <-time.After(time.Second):
   193  		t.Fatalf("Block 1 not produced after timeout")
   194  	}
   195  }
   196  
   197  func TestNewChannelConfig(t *testing.T) {
   198  	lf, _ := NewRAMLedgerAndFactoryWithMSP()
   199  
   200  	consenters := make(map[string]Consenter)
   201  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   202  	manager := NewManagerImpl(lf, consenters, mockCrypto())
   203  
   204  	t.Run("BadPayload", func(t *testing.T) {
   205  		_, err := manager.NewChannelConfig(&cb.Envelope{Payload: []byte("bad payload")})
   206  		assert.Error(t, err, "Should not be able to create new channel config from bad payload.")
   207  	})
   208  
   209  	for _, tc := range []struct {
   210  		name    string
   211  		payload *cb.Payload
   212  		regex   string
   213  	}{
   214  		{
   215  			"BadPayloadData",
   216  			&cb.Payload{
   217  				Data: []byte("bad payload data"),
   218  			},
   219  			"^Failing initial channel config creation because of config update envelope unmarshaling error:",
   220  		},
   221  		{
   222  			"BadConfigUpdate",
   223  			&cb.Payload{
   224  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   225  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   226  					ConfigUpdate: []byte("bad config update envelope data"),
   227  				}),
   228  			},
   229  			"^Failing initial channel config creation because of config update unmarshaling error:",
   230  		},
   231  		{
   232  			"EmptyConfigUpdateWriteSet",
   233  			&cb.Payload{
   234  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   235  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   236  					ConfigUpdate: utils.MarshalOrPanic(
   237  						&cb.ConfigUpdate{},
   238  					),
   239  				}),
   240  			},
   241  			"^Config update has an empty writeset$",
   242  		},
   243  		{
   244  			"WriteSetNoGroups",
   245  			&cb.Payload{
   246  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   247  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   248  					ConfigUpdate: utils.MarshalOrPanic(
   249  						&cb.ConfigUpdate{
   250  							WriteSet: &cb.ConfigGroup{},
   251  						},
   252  					),
   253  				}),
   254  			},
   255  			"^Config update has missing application group$",
   256  		},
   257  		{
   258  			"WriteSetNoApplicationGroup",
   259  			&cb.Payload{
   260  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   261  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   262  					ConfigUpdate: utils.MarshalOrPanic(
   263  						&cb.ConfigUpdate{
   264  							WriteSet: &cb.ConfigGroup{
   265  								Groups: map[string]*cb.ConfigGroup{},
   266  							},
   267  						},
   268  					),
   269  				}),
   270  			},
   271  			"^Config update has missing application group$",
   272  		},
   273  		{
   274  			"BadWriteSetApplicationGroupVersion",
   275  			&cb.Payload{
   276  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   277  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   278  					ConfigUpdate: utils.MarshalOrPanic(
   279  						&cb.ConfigUpdate{
   280  							WriteSet: &cb.ConfigGroup{
   281  								Groups: map[string]*cb.ConfigGroup{
   282  									config.ApplicationGroupKey: &cb.ConfigGroup{
   283  										Version: 100,
   284  									},
   285  								},
   286  							},
   287  						},
   288  					),
   289  				}),
   290  			},
   291  			"^Config update for channel creation does not set application group version to 1,",
   292  		},
   293  		{
   294  			"MissingWriteSetConsortiumValue",
   295  			&cb.Payload{
   296  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   297  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   298  					ConfigUpdate: utils.MarshalOrPanic(
   299  						&cb.ConfigUpdate{
   300  							WriteSet: &cb.ConfigGroup{
   301  								Groups: map[string]*cb.ConfigGroup{
   302  									config.ApplicationGroupKey: &cb.ConfigGroup{
   303  										Version: 1,
   304  									},
   305  								},
   306  								Values: map[string]*cb.ConfigValue{},
   307  							},
   308  						},
   309  					),
   310  				}),
   311  			},
   312  			"^Consortium config value missing$",
   313  		},
   314  		{
   315  			"BadWriteSetConsortiumValueValue",
   316  			&cb.Payload{
   317  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   318  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   319  					ConfigUpdate: utils.MarshalOrPanic(
   320  						&cb.ConfigUpdate{
   321  							WriteSet: &cb.ConfigGroup{
   322  								Groups: map[string]*cb.ConfigGroup{
   323  									config.ApplicationGroupKey: &cb.ConfigGroup{
   324  										Version: 1,
   325  									},
   326  								},
   327  								Values: map[string]*cb.ConfigValue{
   328  									config.ConsortiumKey: &cb.ConfigValue{
   329  										Value: []byte("bad consortium value"),
   330  									},
   331  								},
   332  							},
   333  						},
   334  					),
   335  				}),
   336  			},
   337  			"^Error reading unmarshaling consortium name:",
   338  		},
   339  		{
   340  			"UnknownConsortiumName",
   341  			&cb.Payload{
   342  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   343  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   344  					ConfigUpdate: utils.MarshalOrPanic(
   345  						&cb.ConfigUpdate{
   346  							WriteSet: &cb.ConfigGroup{
   347  								Groups: map[string]*cb.ConfigGroup{
   348  									config.ApplicationGroupKey: &cb.ConfigGroup{
   349  										Version: 1,
   350  									},
   351  								},
   352  								Values: map[string]*cb.ConfigValue{
   353  									config.ConsortiumKey: &cb.ConfigValue{
   354  										Value: utils.MarshalOrPanic(
   355  											&cb.Consortium{
   356  												Name: "NotTheNameYouAreLookingFor",
   357  											},
   358  										),
   359  									},
   360  								},
   361  							},
   362  						},
   363  					),
   364  				}),
   365  			},
   366  			"^Unknown consortium name:",
   367  		},
   368  		{
   369  			"Missing consortium members",
   370  			&cb.Payload{
   371  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   372  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   373  					ConfigUpdate: utils.MarshalOrPanic(
   374  						&cb.ConfigUpdate{
   375  							WriteSet: &cb.ConfigGroup{
   376  								Groups: map[string]*cb.ConfigGroup{
   377  									config.ApplicationGroupKey: &cb.ConfigGroup{
   378  										Version: 1,
   379  									},
   380  								},
   381  								Values: map[string]*cb.ConfigValue{
   382  									config.ConsortiumKey: &cb.ConfigValue{
   383  										Value: utils.MarshalOrPanic(
   384  											&cb.Consortium{
   385  												Name: genesisconfig.SampleConsortiumName,
   386  											},
   387  										),
   388  									},
   389  								},
   390  							},
   391  						},
   392  					),
   393  				}),
   394  			},
   395  			"Proposed configuration has no application group members, but consortium contains members",
   396  		},
   397  		{
   398  			"Member not in consortium",
   399  			&cb.Payload{
   400  				Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, 0, "", epoch))},
   401  				Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
   402  					ConfigUpdate: utils.MarshalOrPanic(
   403  						&cb.ConfigUpdate{
   404  							WriteSet: &cb.ConfigGroup{
   405  								Groups: map[string]*cb.ConfigGroup{
   406  									config.ApplicationGroupKey: &cb.ConfigGroup{
   407  										Version: 1,
   408  										Groups: map[string]*cb.ConfigGroup{
   409  											"BadOrgName": &cb.ConfigGroup{},
   410  										},
   411  									},
   412  								},
   413  								Values: map[string]*cb.ConfigValue{
   414  									config.ConsortiumKey: &cb.ConfigValue{
   415  										Value: utils.MarshalOrPanic(
   416  											&cb.Consortium{
   417  												Name: genesisconfig.SampleConsortiumName,
   418  											},
   419  										),
   420  									},
   421  								},
   422  							},
   423  						},
   424  					),
   425  				}),
   426  			},
   427  			"Attempted to include a member which is not in the consortium",
   428  		},
   429  	} {
   430  		t.Run(tc.name, func(t *testing.T) {
   431  			_, err := manager.NewChannelConfig(&cb.Envelope{Payload: utils.MarshalOrPanic(tc.payload)})
   432  			if assert.Error(t, err) {
   433  				assert.Regexp(t, tc.regex, err.Error())
   434  			}
   435  		})
   436  	}
   437  	// SampleConsortium
   438  }
   439  
   440  func TestMismatchedChannelIDs(t *testing.T) {
   441  	innerChannelID := "foo"
   442  	outerChannelID := "bar"
   443  	template := configtx.NewChainCreationTemplate(genesisconfig.SampleConsortiumName, nil)
   444  	configUpdateEnvelope, err := template.Envelope(innerChannelID)
   445  	createTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, outerChannelID, nil, configUpdateEnvelope, msgVersion, epoch)
   446  	assert.NoError(t, err)
   447  
   448  	lf, _ := NewRAMLedgerAndFactory(10)
   449  
   450  	consenters := make(map[string]Consenter)
   451  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   452  
   453  	manager := NewManagerImpl(lf, consenters, mockCrypto())
   454  
   455  	_, err = manager.NewChannelConfig(createTx)
   456  	assert.Error(t, err, "Mismatched channel IDs")
   457  	assert.Regexp(t, "mismatched channel IDs", err.Error())
   458  }
   459  
   460  // This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain
   461  func TestNewChain(t *testing.T) {
   462  	expectedLastConfigBlockNumber := uint64(0)
   463  	expectedLastConfigSeq := uint64(1)
   464  	newChainID := "test-new-chain"
   465  
   466  	lf, rl := NewRAMLedgerAndFactory(10)
   467  
   468  	consenters := make(map[string]Consenter)
   469  	consenters[conf.Orderer.OrdererType] = &mockConsenter{}
   470  
   471  	manager := NewManagerImpl(lf, consenters, mockCrypto())
   472  
   473  	envConfigUpdate, err := configtx.MakeChainCreationTransaction(newChainID, genesisconfig.SampleConsortiumName, mockSigningIdentity)
   474  	assert.NoError(t, err, "Constructing chain creation tx")
   475  
   476  	cm, err := manager.NewChannelConfig(envConfigUpdate)
   477  	assert.NoError(t, err, "Constructing initial channel config")
   478  
   479  	configEnv, err := cm.ProposeConfigUpdate(envConfigUpdate)
   480  	assert.NoError(t, err, "Proposing initial update")
   481  	assert.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq)
   482  
   483  	ingressTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch)
   484  	assert.NoError(t, err, "Creating ingresstx")
   485  
   486  	wrapped := wrapConfigTx(ingressTx)
   487  
   488  	chainSupport, ok := manager.GetChain(manager.SystemChannelID())
   489  	assert.True(t, ok, "Could not find system channel")
   490  
   491  	chainSupport.Enqueue(wrapped)
   492  	func() {
   493  		it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   494  		defer it.Close()
   495  		select {
   496  		case <-it.ReadyChan():
   497  			block, status := it.Next()
   498  			if status != cb.Status_SUCCESS {
   499  				t.Fatalf("Could not retrieve block")
   500  			}
   501  			if len(block.Data.Data) != 1 {
   502  				t.Fatalf("Should have had only one message in the orderer transaction block")
   503  			}
   504  
   505  			assert.Equal(t, wrapped, utils.UnmarshalEnvelopeOrPanic(block.Data.Data[0]), "Orderer config block contains wrong transaction")
   506  		case <-time.After(time.Second):
   507  			t.Fatalf("Block 1 not produced after timeout in system chain")
   508  		}
   509  	}()
   510  
   511  	chainSupport, ok = manager.GetChain(newChainID)
   512  
   513  	if !ok {
   514  		t.Fatalf("Should have gotten new chain which was created")
   515  	}
   516  
   517  	messages := make([]*cb.Envelope, conf.Orderer.BatchSize.MaxMessageCount)
   518  	for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ {
   519  		messages[i] = makeNormalTx(newChainID, i)
   520  	}
   521  
   522  	for _, message := range messages {
   523  		chainSupport.Enqueue(message)
   524  	}
   525  
   526  	it, _ := chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}})
   527  	defer it.Close()
   528  	select {
   529  	case <-it.ReadyChan():
   530  		block, status := it.Next()
   531  		if status != cb.Status_SUCCESS {
   532  			t.Fatalf("Could not retrieve new chain genesis block")
   533  		}
   534  		testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber)
   535  		if len(block.Data.Data) != 1 {
   536  			t.Fatalf("Should have had only one message in the new genesis block")
   537  		}
   538  
   539  		assert.Equal(t, ingressTx, utils.UnmarshalEnvelopeOrPanic(block.Data.Data[0]), "Genesis block contains wrong transaction")
   540  	case <-time.After(time.Second):
   541  		t.Fatalf("Block 1 not produced after timeout in system chain")
   542  	}
   543  
   544  	select {
   545  	case <-it.ReadyChan():
   546  		block, status := it.Next()
   547  		if status != cb.Status_SUCCESS {
   548  			t.Fatalf("Could not retrieve block on new chain")
   549  		}
   550  		testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber)
   551  		for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ {
   552  			if !reflect.DeepEqual(utils.ExtractEnvelopeOrPanic(block, i), messages[i]) {
   553  				t.Errorf("Block contents wrong at index %d in new chain", i)
   554  			}
   555  		}
   556  	case <-time.After(time.Second):
   557  		t.Fatalf("Block 1 not produced after timeout on new chain")
   558  	}
   559  
   560  	testRestartedChainSupport(t, chainSupport, consenters, expectedLastConfigSeq)
   561  }
   562  
   563  func testRestartedChainSupport(t *testing.T, cs ChainSupport, consenters map[string]Consenter, expectedLastConfigSeq uint64) {
   564  	ccs, ok := cs.(*chainSupport)
   565  	assert.True(t, ok, "Casting error")
   566  	rcs := newChainSupport(ccs.filters, ccs.ledgerResources, consenters, mockCrypto())
   567  	assert.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq")
   568  }
   569  
   570  func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) {
   571  	metadataItem := &cb.Metadata{}
   572  	err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadataItem)
   573  	assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
   574  	lastConfig := &cb.LastConfig{}
   575  	err = proto.Unmarshal(metadataItem.Value, lastConfig)
   576  	assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
   577  	assert.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block")
   578  }