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

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package multichannel
     8  
     9  import (
    10  	"io/ioutil"
    11  	"os"
    12  	"testing"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hechain20/hechain/bccsp"
    16  	"github.com/hechain20/hechain/bccsp/sw"
    17  	"github.com/hechain20/hechain/common/channelconfig"
    18  	"github.com/hechain20/hechain/common/configtx"
    19  	"github.com/hechain20/hechain/common/ledger/blockledger"
    20  	"github.com/hechain20/hechain/common/ledger/blockledger/fileledger"
    21  	"github.com/hechain20/hechain/common/metrics/disabled"
    22  	"github.com/hechain20/hechain/core/config/configtest"
    23  	"github.com/hechain20/hechain/internal/configtxgen/encoder"
    24  	"github.com/hechain20/hechain/internal/configtxgen/genesisconfig"
    25  	"github.com/hechain20/hechain/internal/pkg/identity"
    26  	"github.com/hechain20/hechain/orderer/common/blockcutter/mock"
    27  	"github.com/hechain20/hechain/orderer/common/multichannel/mocks"
    28  	"github.com/hechain20/hechain/protoutil"
    29  	cb "github.com/hyperledger/fabric-protos-go/common"
    30  	"github.com/hyperledger/fabric-protos-go/orderer"
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  //go:generate counterfeiter -o mocks/configtx_validator.go --fake-name ConfigTXValidator . configtxValidator
    35  
    36  type configtxValidator interface {
    37  	configtx.Validator
    38  }
    39  
    40  type mockBlockWriterSupport struct {
    41  	*mocks.ConfigTXValidator
    42  	identity.SignerSerializer
    43  	blockledger.ReadWriter
    44  	fakeConfig *mock.OrdererConfig
    45  	bccsp      bccsp.BCCSP
    46  }
    47  
    48  func (mbws mockBlockWriterSupport) Update(bundle *channelconfig.Bundle) {}
    49  
    50  func (mbws mockBlockWriterSupport) CreateBundle(channelID string, config *cb.Config) (*channelconfig.Bundle, error) {
    51  	return channelconfig.NewBundle(channelID, config, mbws.bccsp)
    52  }
    53  
    54  func (mbws mockBlockWriterSupport) SharedConfig() channelconfig.Orderer {
    55  	return mbws.fakeConfig
    56  }
    57  
    58  func TestCreateBlock(t *testing.T) {
    59  	seedBlock := protoutil.NewBlock(7, []byte("lasthash"))
    60  	seedBlock.Data.Data = [][]byte{[]byte("somebytes")}
    61  
    62  	bw := &BlockWriter{lastBlock: seedBlock}
    63  	block := bw.CreateNextBlock([]*cb.Envelope{
    64  		{Payload: []byte("some other bytes")},
    65  	})
    66  
    67  	require.Equal(t, seedBlock.Header.Number+1, block.Header.Number)
    68  	require.Equal(t, protoutil.BlockDataHash(block.Data), block.Header.DataHash)
    69  	require.Equal(t, protoutil.BlockHeaderHash(seedBlock.Header), block.Header.PreviousHash)
    70  }
    71  
    72  func TestBlockSignature(t *testing.T) {
    73  	dir, err := ioutil.TempDir("", "file-ledger")
    74  	require.NoError(t, err)
    75  	defer os.RemoveAll(dir)
    76  
    77  	rlf, err := fileledger.New(dir, &disabled.Provider{})
    78  	require.NoError(t, err)
    79  
    80  	l, err := rlf.GetOrCreate("mychannel")
    81  	require.NoError(t, err)
    82  	lastBlock := protoutil.NewBlock(0, nil)
    83  	l.Append(lastBlock)
    84  
    85  	bw := &BlockWriter{
    86  		lastConfigBlockNum: 42,
    87  		support: &mockBlockWriterSupport{
    88  			SignerSerializer:  mockCrypto(),
    89  			ConfigTXValidator: &mocks.ConfigTXValidator{},
    90  			ReadWriter:        l,
    91  		},
    92  		lastBlock: protoutil.NewBlock(1, protoutil.BlockHeaderHash(lastBlock.Header)),
    93  	}
    94  
    95  	consensusMetadata := []byte("bar")
    96  	bw.commitBlock(consensusMetadata)
    97  
    98  	it, seq := l.Iterator(&orderer.SeekPosition{Type: &orderer.SeekPosition_Newest{}})
    99  	require.Equal(t, uint64(1), seq)
   100  	committedBlock, status := it.Next()
   101  	require.Equal(t, cb.Status_SUCCESS, status)
   102  
   103  	md := protoutil.GetMetadataFromBlockOrPanic(committedBlock, cb.BlockMetadataIndex_SIGNATURES)
   104  
   105  	expectedMetadataValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   106  		LastConfig:        &cb.LastConfig{Index: 42},
   107  		ConsenterMetadata: protoutil.MarshalOrPanic(&cb.Metadata{Value: consensusMetadata}),
   108  	})
   109  
   110  	require.Equal(t, expectedMetadataValue, md.Value, "Value contains the consensus metadata and the last config")
   111  	require.NotNil(t, md.Signatures, "Should have signature")
   112  }
   113  
   114  func TestBlockLastConfig(t *testing.T) {
   115  	lastConfigSeq := uint64(6)
   116  	newConfigSeq := lastConfigSeq + 1
   117  	newBlockNum := uint64(9)
   118  
   119  	mockValidator := &mocks.ConfigTXValidator{}
   120  	mockValidator.SequenceReturns(newConfigSeq)
   121  	bw := &BlockWriter{
   122  		support: &mockBlockWriterSupport{
   123  			SignerSerializer:  mockCrypto(),
   124  			ConfigTXValidator: mockValidator,
   125  		},
   126  		lastConfigSeq: lastConfigSeq,
   127  	}
   128  
   129  	block := protoutil.NewBlock(newBlockNum, []byte("foo"))
   130  	bw.addLastConfig(block)
   131  
   132  	require.Equal(t, newBlockNum, bw.lastConfigBlockNum)
   133  	require.Equal(t, newConfigSeq, bw.lastConfigSeq)
   134  
   135  	md := protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_LAST_CONFIG)
   136  	require.NotNil(t, md.Value, "Value not be empty in this case")
   137  	require.Nil(t, md.Signatures, "Should not have signature")
   138  
   139  	lc := protoutil.GetLastConfigIndexFromBlockOrPanic(block)
   140  	require.Equal(t, newBlockNum, lc)
   141  }
   142  
   143  func TestWriteConfigBlock(t *testing.T) {
   144  	// TODO, use assert.PanicsWithValue once available
   145  	t.Run("EmptyBlock", func(t *testing.T) {
   146  		require.Panics(t, func() { (&BlockWriter{}).WriteConfigBlock(&cb.Block{}, nil) })
   147  	})
   148  	t.Run("BadPayload", func(t *testing.T) {
   149  		require.Panics(t, func() {
   150  			(&BlockWriter{}).WriteConfigBlock(&cb.Block{
   151  				Data: &cb.BlockData{
   152  					Data: [][]byte{
   153  						protoutil.MarshalOrPanic(&cb.Envelope{Payload: []byte("bad")}),
   154  					},
   155  				},
   156  			}, nil)
   157  		})
   158  	})
   159  	t.Run("MissingHeader", func(t *testing.T) {
   160  		require.Panics(t, func() {
   161  			(&BlockWriter{}).WriteConfigBlock(&cb.Block{
   162  				Data: &cb.BlockData{
   163  					Data: [][]byte{
   164  						protoutil.MarshalOrPanic(&cb.Envelope{
   165  							Payload: protoutil.MarshalOrPanic(&cb.Payload{}),
   166  						}),
   167  					},
   168  				},
   169  			}, nil)
   170  		})
   171  	})
   172  	t.Run("BadChannelHeader", func(t *testing.T) {
   173  		require.Panics(t, func() {
   174  			(&BlockWriter{}).WriteConfigBlock(&cb.Block{
   175  				Data: &cb.BlockData{
   176  					Data: [][]byte{
   177  						protoutil.MarshalOrPanic(&cb.Envelope{
   178  							Payload: protoutil.MarshalOrPanic(&cb.Payload{
   179  								Header: &cb.Header{
   180  									ChannelHeader: []byte("bad"),
   181  								},
   182  							}),
   183  						}),
   184  					},
   185  				},
   186  			}, nil)
   187  		})
   188  	})
   189  	t.Run("BadChannelHeaderType", func(t *testing.T) {
   190  		require.Panics(t, func() {
   191  			(&BlockWriter{}).WriteConfigBlock(&cb.Block{
   192  				Data: &cb.BlockData{
   193  					Data: [][]byte{
   194  						protoutil.MarshalOrPanic(&cb.Envelope{
   195  							Payload: protoutil.MarshalOrPanic(&cb.Payload{
   196  								Header: &cb.Header{
   197  									ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{}),
   198  								},
   199  							}),
   200  						}),
   201  					},
   202  				},
   203  			}, nil)
   204  		})
   205  	})
   206  }
   207  
   208  func TestGoodWriteConfig(t *testing.T) {
   209  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   210  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   211  
   212  	tmpdir, err := ioutil.TempDir("", "file-ledger")
   213  	require.NoError(t, err)
   214  	defer os.RemoveAll(tmpdir)
   215  
   216  	_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   217  
   218  	fakeConfig := &mock.OrdererConfig{}
   219  	fakeConfig.ConsensusTypeReturns("solo")
   220  
   221  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   222  	require.NoError(t, err)
   223  
   224  	mockValidator := &mocks.ConfigTXValidator{}
   225  	mockValidator.ChannelIDReturns("testchannelid")
   226  	bw := newBlockWriter(genesisBlockSys, nil,
   227  		&mockBlockWriterSupport{
   228  			SignerSerializer:  mockCrypto(),
   229  			ReadWriter:        l,
   230  			ConfigTXValidator: mockValidator,
   231  			fakeConfig:        fakeConfig,
   232  			bccsp:             cryptoProvider,
   233  		},
   234  	)
   235  
   236  	ctx := makeConfigTxFull("testchannelid", 1)
   237  	block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
   238  	block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   239  	consenterMetadata := []byte("foo")
   240  	bw.WriteConfigBlock(block, consenterMetadata)
   241  
   242  	// Wait for the commit to complete
   243  	bw.committingBlock.Lock()
   244  	bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
   245  
   246  	cBlock := blockledger.GetBlock(l, block.Header.Number)
   247  	require.Equal(t, block.Header, cBlock.Header)
   248  	require.Equal(t, block.Data, cBlock.Data)
   249  
   250  	omd, err := protoutil.GetConsenterMetadataFromBlock(block)
   251  	require.NoError(t, err)
   252  	require.Equal(t, consenterMetadata, omd.Value)
   253  }
   254  
   255  func TestWriteConfigSynchronously(t *testing.T) {
   256  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   257  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   258  
   259  	tmpdir, err := ioutil.TempDir("", "file-ledger")
   260  	require.NoError(t, err)
   261  	defer os.RemoveAll(tmpdir)
   262  
   263  	_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   264  
   265  	fakeConfig := &mock.OrdererConfig{}
   266  	fakeConfig.ConsensusTypeReturns("solo")
   267  
   268  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   269  	require.NoError(t, err)
   270  
   271  	mockValidator := &mocks.ConfigTXValidator{}
   272  	mockValidator.ChannelIDReturns("testchannelid")
   273  	bw := newBlockWriter(genesisBlockSys, nil,
   274  		&mockBlockWriterSupport{
   275  			SignerSerializer:  mockCrypto(),
   276  			ReadWriter:        l,
   277  			ConfigTXValidator: mockValidator,
   278  			fakeConfig:        fakeConfig,
   279  			bccsp:             cryptoProvider,
   280  		},
   281  	)
   282  
   283  	ctx := makeConfigTxFull("testchannelid", 1)
   284  	block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
   285  	block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   286  	consenterMetadata := []byte("foo")
   287  	bw.WriteConfigBlock(block, consenterMetadata)
   288  
   289  	cBlock, err := blockledger.GetBlockByNumber(l, block.Header.Number)
   290  	require.Nil(t, err)
   291  	require.Equal(t, block.Header, cBlock.Header)
   292  	require.Equal(t, block.Data, cBlock.Data)
   293  
   294  	omd, err := protoutil.GetConsenterMetadataFromBlock(block)
   295  	require.NoError(t, err)
   296  	require.Equal(t, consenterMetadata, omd.Value)
   297  }
   298  
   299  func TestMigrationWriteConfig(t *testing.T) {
   300  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   301  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   302  
   303  	tmpdir, err := ioutil.TempDir("", "file-ledger")
   304  	require.NoError(t, err)
   305  	defer os.RemoveAll(tmpdir)
   306  
   307  	_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   308  
   309  	fakeConfig := &mock.OrdererConfig{}
   310  	fakeConfig.ConsensusTypeReturns("solo")
   311  	fakeConfig.ConsensusStateReturns(orderer.ConsensusType_STATE_MAINTENANCE)
   312  
   313  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   314  	require.NoError(t, err)
   315  
   316  	mockValidator := &mocks.ConfigTXValidator{}
   317  	mockValidator.ChannelIDReturns("testchannelid")
   318  	bw := newBlockWriter(genesisBlockSys, nil,
   319  		&mockBlockWriterSupport{
   320  			SignerSerializer:  mockCrypto(),
   321  			ReadWriter:        l,
   322  			ConfigTXValidator: mockValidator,
   323  			fakeConfig:        fakeConfig,
   324  			bccsp:             cryptoProvider,
   325  		},
   326  	)
   327  
   328  	ctx := makeConfigTxMig("testchannelid", 1)
   329  	block := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
   330  	block.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   331  	consenterMetadata := []byte("foo")
   332  
   333  	bw.WriteConfigBlock(block, consenterMetadata)
   334  
   335  	// Wait for the commit to complete
   336  	bw.committingBlock.Lock()
   337  	bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
   338  
   339  	cBlock := blockledger.GetBlock(l, block.Header.Number)
   340  	require.Equal(t, block.Header, cBlock.Header)
   341  	require.Equal(t, block.Data, cBlock.Data)
   342  
   343  	omd := protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_ORDERER)
   344  	require.Equal(t, []byte(nil), omd.Value)
   345  }
   346  
   347  func TestRaceWriteConfig(t *testing.T) {
   348  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   349  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   350  
   351  	tmpdir, err := ioutil.TempDir("", "file-ledger")
   352  	require.NoError(t, err)
   353  	defer os.RemoveAll(tmpdir)
   354  
   355  	_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   356  
   357  	fakeConfig := &mock.OrdererConfig{}
   358  	fakeConfig.ConsensusTypeReturns("solo")
   359  
   360  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   361  	require.NoError(t, err)
   362  
   363  	mockValidator := &mocks.ConfigTXValidator{}
   364  	bw := newBlockWriter(genesisBlockSys, nil,
   365  		&mockBlockWriterSupport{
   366  			SignerSerializer:  mockCrypto(),
   367  			ReadWriter:        l,
   368  			ConfigTXValidator: mockValidator,
   369  			fakeConfig:        fakeConfig,
   370  			bccsp:             cryptoProvider,
   371  		},
   372  	)
   373  
   374  	ctx := makeConfigTxFull("testchannelid", 1)
   375  	block1 := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
   376  	block1.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   377  	consenterMetadata1 := []byte("foo")
   378  	mockValidator.SequenceReturnsOnCall(1, 1)
   379  
   380  	ctx = makeConfigTxFull("testchannelid", 1)
   381  	block2 := protoutil.NewBlock(2, protoutil.BlockHeaderHash(block1.Header))
   382  	block2.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   383  	consenterMetadata2 := []byte("bar")
   384  	mockValidator.SequenceReturnsOnCall(2, 2)
   385  
   386  	bw.WriteConfigBlock(block1, consenterMetadata1)
   387  	bw.WriteConfigBlock(block2, consenterMetadata2)
   388  
   389  	// Wait for the commit to complete
   390  	bw.committingBlock.Lock()
   391  	bw.committingBlock.Unlock() //lint:ignore SA2001 syncpoint
   392  
   393  	cBlock := blockledger.GetBlock(l, block1.Header.Number)
   394  	require.Equal(t, block1.Header, cBlock.Header)
   395  	require.Equal(t, block1.Data, cBlock.Data)
   396  	expectedLastConfigBlockNumber := block1.Header.Number
   397  	testLastConfigBlockNumber(t, block1, expectedLastConfigBlockNumber)
   398  
   399  	cBlock = blockledger.GetBlock(l, block2.Header.Number)
   400  	require.Equal(t, block2.Header, cBlock.Header)
   401  	require.Equal(t, block2.Data, cBlock.Data)
   402  	expectedLastConfigBlockNumber = block2.Header.Number
   403  	testLastConfigBlockNumber(t, block2, expectedLastConfigBlockNumber)
   404  
   405  	omd, err := protoutil.GetConsenterMetadataFromBlock(block1)
   406  	require.NoError(t, err)
   407  	require.Equal(t, consenterMetadata1, omd.Value)
   408  }
   409  
   410  func TestRaceWriteBlocks(t *testing.T) {
   411  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   412  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   413  
   414  	tmpdir, err := ioutil.TempDir("", "file-ledger")
   415  	require.NoError(t, err)
   416  	defer os.RemoveAll(tmpdir)
   417  
   418  	_, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   419  
   420  	fakeConfig := &mock.OrdererConfig{}
   421  	fakeConfig.ConsensusTypeReturns("solo")
   422  
   423  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   424  	require.NoError(t, err)
   425  
   426  	mockValidator := &mocks.ConfigTXValidator{}
   427  	bw := newBlockWriter(genesisBlockSys, nil,
   428  		&mockBlockWriterSupport{
   429  			SignerSerializer:  mockCrypto(),
   430  			ReadWriter:        l,
   431  			ConfigTXValidator: mockValidator,
   432  			fakeConfig:        fakeConfig,
   433  			bccsp:             cryptoProvider,
   434  		},
   435  	)
   436  
   437  	ctx := makeConfigTxFull("testchannelid", 1)
   438  	block1 := protoutil.NewBlock(1, protoutil.BlockHeaderHash(genesisBlockSys.Header))
   439  	block1.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   440  	consenterMetadata1 := []byte("foo")
   441  	mockValidator.SequenceReturnsOnCall(1, 1)
   442  
   443  	ctx = makeConfigTxFull("testchannelid", 1)
   444  	block2 := protoutil.NewBlock(2, protoutil.BlockHeaderHash(block1.Header))
   445  	block2.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   446  	consenterMetadata2 := []byte("bar")
   447  	mockValidator.SequenceReturnsOnCall(2, 2)
   448  
   449  	ctx = makeConfigTxFull("testchannelid", 1)
   450  	block3 := protoutil.NewBlock(3, protoutil.BlockHeaderHash(block2.Header))
   451  	block3.Data.Data = [][]byte{protoutil.MarshalOrPanic(ctx)}
   452  	consenterMetadata3 := []byte("3")
   453  	mockValidator.SequenceReturnsOnCall(3, 3)
   454  
   455  	bw.WriteBlock(block1, consenterMetadata1)
   456  	bw.WriteBlock(block2, consenterMetadata2)
   457  	bw.WriteConfigBlock(block3, consenterMetadata3)
   458  
   459  	cBlock, err := blockledger.GetBlockByNumber(l, block1.Header.Number)
   460  	require.Nil(t, err)
   461  	require.Equal(t, block1.Header, cBlock.Header)
   462  	require.Equal(t, block1.Data, cBlock.Data)
   463  
   464  	cBlock, err = blockledger.GetBlockByNumber(l, block2.Header.Number)
   465  	require.Nil(t, err)
   466  	require.Equal(t, block2.Header, cBlock.Header)
   467  	require.Equal(t, block2.Data, cBlock.Data)
   468  
   469  	cBlock, err = blockledger.GetBlockByNumber(l, block3.Header.Number)
   470  	require.Nil(t, err)
   471  	require.Equal(t, block3.Header, cBlock.Header)
   472  	require.Equal(t, block3.Data, cBlock.Data)
   473  
   474  	expectedLastConfigBlockNumber := block3.Header.Number
   475  	testLastConfigBlockNumber(t, block3, expectedLastConfigBlockNumber)
   476  }
   477  
   478  func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) {
   479  	metadata := &cb.Metadata{}
   480  	err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], metadata)
   481  	require.NoError(t, err, "Block should carry SIGNATURES metadata item")
   482  	obm := &cb.OrdererBlockMetadata{}
   483  	err = proto.Unmarshal(metadata.Value, obm)
   484  	require.NoError(t, err, "Block SIGNATURES should carry OrdererBlockMetadata")
   485  	require.Equal(t, expectedBlockNumber, obm.LastConfig.Index, "SIGNATURES value should point to last config block")
   486  
   487  	metadata = &cb.Metadata{}
   488  	err = proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadata)
   489  	require.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
   490  	lastConfig := &cb.LastConfig{}
   491  	err = proto.Unmarshal(metadata.Value, lastConfig)
   492  	require.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
   493  	require.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block")
   494  }