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