github.com/anjalikarhana/fabric@v2.1.1+incompatible/orderer/common/multichannel/registrar_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  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    17  	"github.com/hyperledger/fabric/bccsp/sw"
    18  	"github.com/hyperledger/fabric/common/channelconfig"
    19  	"github.com/hyperledger/fabric/common/ledger/blockledger"
    20  	"github.com/hyperledger/fabric/common/ledger/blockledger/fileledger"
    21  	"github.com/hyperledger/fabric/common/metrics/disabled"
    22  	"github.com/hyperledger/fabric/common/policies"
    23  	"github.com/hyperledger/fabric/core/config/configtest"
    24  	"github.com/hyperledger/fabric/internal/configtxgen/encoder"
    25  	"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
    26  	"github.com/hyperledger/fabric/internal/pkg/identity"
    27  	"github.com/hyperledger/fabric/orderer/common/blockcutter"
    28  	"github.com/hyperledger/fabric/orderer/common/localconfig"
    29  	"github.com/hyperledger/fabric/orderer/common/multichannel/mocks"
    30  	"github.com/hyperledger/fabric/orderer/consensus"
    31  	"github.com/hyperledger/fabric/protoutil"
    32  	"github.com/pkg/errors"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  //go:generate counterfeiter -o mocks/resources.go --fake-name Resources . resources
    38  
    39  type resources interface {
    40  	channelconfig.Resources
    41  }
    42  
    43  //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig
    44  
    45  type ordererConfig interface {
    46  	channelconfig.Orderer
    47  }
    48  
    49  //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities
    50  
    51  type ordererCapabilities interface {
    52  	channelconfig.OrdererCapabilities
    53  }
    54  
    55  //go:generate counterfeiter -o mocks/channel_config.go --fake-name ChannelConfig . channelConfig
    56  
    57  type channelConfig interface {
    58  	channelconfig.Channel
    59  }
    60  
    61  //go:generate counterfeiter -o mocks/channel_capabilities.go --fake-name ChannelCapabilities . channelCapabilities
    62  
    63  type channelCapabilities interface {
    64  	channelconfig.ChannelCapabilities
    65  }
    66  
    67  //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer
    68  
    69  type signerSerializer interface {
    70  	identity.SignerSerializer
    71  }
    72  
    73  func mockCrypto() *mocks.SignerSerializer {
    74  	return &mocks.SignerSerializer{}
    75  }
    76  
    77  func newLedgerAndFactory(dir string, chainID string, genesisBlockSys *cb.Block) (blockledger.Factory, blockledger.ReadWriter) {
    78  	rlf, err := fileledger.New(dir, &disabled.Provider{})
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  
    83  	rl, err := rlf.GetOrCreate(chainID)
    84  	if err != nil {
    85  		panic(err)
    86  	}
    87  
    88  	if genesisBlockSys != nil {
    89  		err = rl.Append(genesisBlockSys)
    90  		if err != nil {
    91  			panic(err)
    92  		}
    93  	}
    94  	return rlf, rl
    95  }
    96  
    97  func testMessageOrderAndRetrieval(maxMessageCount uint32, chainID string, chainSupport *ChainSupport, lr blockledger.ReadWriter, t *testing.T) {
    98  	messages := make([]*cb.Envelope, maxMessageCount)
    99  	for i := uint32(0); i < maxMessageCount; i++ {
   100  		messages[i] = makeNormalTx(chainID, int(i))
   101  	}
   102  	for _, message := range messages {
   103  		chainSupport.Order(message, 0)
   104  	}
   105  	it, _ := lr.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   106  	defer it.Close()
   107  	block, status := it.Next()
   108  	assert.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block")
   109  	for i := uint32(0); i < maxMessageCount; i++ {
   110  		assert.True(t, proto.Equal(messages[i], protoutil.ExtractEnvelopeOrPanic(block, int(i))), "Block contents wrong at index %d", i)
   111  	}
   112  }
   113  
   114  func TestConfigTx(t *testing.T) {
   115  	// system channel
   116  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   117  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   118  
   119  	// Tests for a normal channel which contains 3 config transactions and other
   120  	// normal transactions to make sure the right one returned
   121  	t.Run("GetConfigTx - ok", func(t *testing.T) {
   122  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   123  		require.NoError(t, err)
   124  		defer os.RemoveAll(tmpdir)
   125  
   126  		_, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   127  		for i := 0; i < 5; i++ {
   128  			rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", i)}))
   129  		}
   130  		rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx("testchannelid", 5)}))
   131  		ctx := makeConfigTx("testchannelid", 6)
   132  		rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{ctx}))
   133  
   134  		// block with LAST_CONFIG metadata in SIGNATURES field
   135  		block := blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", 7)})
   136  		blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{
   137  			LastConfig: &cb.LastConfig{Index: 7},
   138  		})
   139  		block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: blockSignatureValue})
   140  		rl.Append(block)
   141  
   142  		pctx := configTx(rl)
   143  		assert.True(t, proto.Equal(pctx, ctx), "Did not select most recent config transaction")
   144  	})
   145  }
   146  
   147  func TestNewRegistrar(t *testing.T) {
   148  	//system channel
   149  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   150  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   151  
   152  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   153  	assert.NoError(t, err)
   154  
   155  	// This test checks to make sure the orderer refuses to come up if it cannot find a system channel
   156  	t.Run("No system chain - failure", func(t *testing.T) {
   157  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   158  		require.NoError(t, err)
   159  		defer os.RemoveAll(tmpdir)
   160  
   161  		lf, err := fileledger.New(tmpdir, &disabled.Provider{})
   162  		require.NoError(t, err)
   163  
   164  		consenters := make(map[string]consensus.Consenter)
   165  		consenters[confSys.Orderer.OrdererType] = &mockConsenter{}
   166  
   167  		assert.NotPanics(t, func() {
   168  			NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider).Initialize(consenters)
   169  		}, "Should not panic when starting without a system channel")
   170  	})
   171  
   172  	// This test checks to make sure that the orderer refuses to come up if there are multiple system channels
   173  	t.Run("Multiple system chains - failure", func(t *testing.T) {
   174  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   175  		require.NoError(t, err)
   176  		defer os.RemoveAll(tmpdir)
   177  
   178  		lf, err := fileledger.New(tmpdir, &disabled.Provider{})
   179  		require.NoError(t, err)
   180  
   181  		for _, id := range []string{"foo", "bar"} {
   182  			rl, err := lf.GetOrCreate(id)
   183  			assert.NoError(t, err)
   184  
   185  			err = rl.Append(encoder.New(confSys).GenesisBlockForChannel(id))
   186  			assert.NoError(t, err)
   187  		}
   188  
   189  		consenters := make(map[string]consensus.Consenter)
   190  		consenters[confSys.Orderer.OrdererType] = &mockConsenter{}
   191  
   192  		assert.Panics(t, func() {
   193  			NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider).Initialize(consenters)
   194  		}, "Two system channels should have caused panic")
   195  	})
   196  
   197  	// This test essentially brings the entire system up and is ultimately what main.go will replicate
   198  	t.Run("Correct flow", func(t *testing.T) {
   199  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   200  		require.NoError(t, err)
   201  		defer os.RemoveAll(tmpdir)
   202  
   203  		lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   204  
   205  		consenters := make(map[string]consensus.Consenter)
   206  		consenters[confSys.Orderer.OrdererType] = &mockConsenter{}
   207  
   208  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider)
   209  		manager.Initialize(consenters)
   210  
   211  		chainSupport := manager.GetChain("Fake")
   212  		assert.Nilf(t, chainSupport, "Should not have found a chain that was not created")
   213  
   214  		chainSupport = manager.GetChain("testchannelid")
   215  		assert.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger")
   216  
   217  		testMessageOrderAndRetrieval(confSys.Orderer.BatchSize.MaxMessageCount, "testchannelid", chainSupport, rl, t)
   218  	})
   219  }
   220  
   221  func TestCreateChain(t *testing.T) {
   222  	//system channel
   223  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   224  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   225  
   226  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   227  	assert.NoError(t, err)
   228  
   229  	t.Run("Create chain", func(t *testing.T) {
   230  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   231  		require.NoError(t, err)
   232  		defer os.RemoveAll(tmpdir)
   233  
   234  		lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   235  
   236  		consenters := make(map[string]consensus.Consenter)
   237  		consenters[confSys.Orderer.OrdererType] = &mockConsenter{}
   238  
   239  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider)
   240  		manager.Initialize(consenters)
   241  
   242  		ledger, err := lf.GetOrCreate("mychannel")
   243  		assert.NoError(t, err)
   244  
   245  		genesisBlock := encoder.New(confSys).GenesisBlockForChannel("mychannel")
   246  		ledger.Append(genesisBlock)
   247  
   248  		// Before creating the chain, it doesn't exist
   249  		assert.Nil(t, manager.GetChain("mychannel"))
   250  		// After creating the chain, it exists
   251  		manager.CreateChain("mychannel")
   252  		chain := manager.GetChain("mychannel")
   253  		assert.NotNil(t, chain)
   254  		// A subsequent creation, replaces the chain.
   255  		manager.CreateChain("mychannel")
   256  		chain2 := manager.GetChain("mychannel")
   257  		assert.NotNil(t, chain2)
   258  		// They are not the same
   259  		assert.NotEqual(t, chain, chain2)
   260  		// The old chain is halted
   261  		_, ok := <-chain.Chain.(*mockChain).queue
   262  		assert.False(t, ok)
   263  		// The new chain is not halted: Close the channel to prove that.
   264  		close(chain2.Chain.(*mockChain).queue)
   265  	})
   266  
   267  	// This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain
   268  	t.Run("New chain", func(t *testing.T) {
   269  		expectedLastConfigSeq := uint64(1)
   270  		newChainID := "test-new-chain"
   271  
   272  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   273  		require.NoError(t, err)
   274  		defer os.RemoveAll(tmpdir)
   275  
   276  		lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   277  
   278  		consenters := make(map[string]consensus.Consenter)
   279  		consenters[confSys.Orderer.OrdererType] = &mockConsenter{}
   280  
   281  		manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider)
   282  		manager.Initialize(consenters)
   283  		orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir())
   284  		orglessChannelConf.Application.Organizations = nil
   285  		envConfigUpdate, err := encoder.MakeChannelCreationTransaction(newChainID, mockCrypto(), orglessChannelConf)
   286  		assert.NoError(t, err, "Constructing chain creation tx")
   287  
   288  		res, err := manager.NewChannelConfig(envConfigUpdate)
   289  		assert.NoError(t, err, "Constructing initial channel config")
   290  
   291  		configEnv, err := res.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate)
   292  		assert.NoError(t, err, "Proposing initial update")
   293  		assert.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq)
   294  
   295  		ingressTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch)
   296  		assert.NoError(t, err, "Creating ingresstx")
   297  
   298  		wrapped := wrapConfigTx(ingressTx)
   299  
   300  		chainSupport := manager.GetChain(manager.SystemChannelID())
   301  		assert.NotNilf(t, chainSupport, "Could not find system channel")
   302  
   303  		chainSupport.Configure(wrapped, 0)
   304  		func() {
   305  			it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   306  			defer it.Close()
   307  			block, status := it.Next()
   308  			if status != cb.Status_SUCCESS {
   309  				t.Fatalf("Could not retrieve block")
   310  			}
   311  			if len(block.Data.Data) != 1 {
   312  				t.Fatalf("Should have had only one message in the orderer transaction block")
   313  			}
   314  
   315  			assert.True(t, proto.Equal(wrapped, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Orderer config block contains wrong transaction")
   316  		}()
   317  
   318  		chainSupport = manager.GetChain(newChainID)
   319  		if chainSupport == nil {
   320  			t.Fatalf("Should have gotten new chain which was created")
   321  		}
   322  
   323  		messages := make([]*cb.Envelope, confSys.Orderer.BatchSize.MaxMessageCount)
   324  		for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ {
   325  			messages[i] = makeNormalTx(newChainID, i)
   326  		}
   327  
   328  		for _, message := range messages {
   329  			chainSupport.Order(message, 0)
   330  		}
   331  
   332  		it, _ := chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}})
   333  		defer it.Close()
   334  		block, status := it.Next()
   335  		if status != cb.Status_SUCCESS {
   336  			t.Fatalf("Could not retrieve new chain genesis block")
   337  		}
   338  		if len(block.Data.Data) != 1 {
   339  			t.Fatalf("Should have had only one message in the new genesis block")
   340  		}
   341  
   342  		assert.True(t, proto.Equal(ingressTx, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Genesis block contains wrong transaction")
   343  
   344  		block, status = it.Next()
   345  		if status != cb.Status_SUCCESS {
   346  			t.Fatalf("Could not retrieve block on new chain")
   347  		}
   348  		for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ {
   349  			if !proto.Equal(protoutil.ExtractEnvelopeOrPanic(block, i), messages[i]) {
   350  				t.Errorf("Block contents wrong at index %d in new chain", i)
   351  			}
   352  		}
   353  
   354  		cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   355  		assert.NoError(t, err)
   356  		rcs := newChainSupport(manager, chainSupport.ledgerResources, consenters, mockCrypto(), blockcutter.NewMetrics(&disabled.Provider{}), cryptoProvider)
   357  		assert.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq")
   358  	})
   359  }
   360  
   361  func TestResourcesCheck(t *testing.T) {
   362  	mockOrderer := &mocks.OrdererConfig{}
   363  	mockOrdererCaps := &mocks.OrdererCapabilities{}
   364  	mockOrderer.CapabilitiesReturns(mockOrdererCaps)
   365  	mockChannel := &mocks.ChannelConfig{}
   366  	mockChannelCaps := &mocks.ChannelCapabilities{}
   367  	mockChannel.CapabilitiesReturns(mockChannelCaps)
   368  
   369  	mockResources := &mocks.Resources{}
   370  	mockResources.PolicyManagerReturns(&policies.ManagerImpl{})
   371  
   372  	t.Run("GoodResources", func(t *testing.T) {
   373  		mockResources.OrdererConfigReturns(mockOrderer, true)
   374  		mockResources.ChannelConfigReturns(mockChannel)
   375  
   376  		err := checkResources(mockResources)
   377  		assert.NoError(t, err)
   378  	})
   379  
   380  	t.Run("MissingOrdererConfigPanic", func(t *testing.T) {
   381  		mockResources.OrdererConfigReturns(nil, false)
   382  
   383  		err := checkResources(mockResources)
   384  		assert.Error(t, err)
   385  		assert.Regexp(t, "config does not contain orderer config", err.Error())
   386  	})
   387  
   388  	t.Run("MissingOrdererCapability", func(t *testing.T) {
   389  		mockResources.OrdererConfigReturns(mockOrderer, true)
   390  		mockOrdererCaps.SupportedReturns(errors.New("An error"))
   391  
   392  		err := checkResources(mockResources)
   393  		assert.Error(t, err)
   394  		assert.Regexp(t, "config requires unsupported orderer capabilities:", err.Error())
   395  
   396  		// reset
   397  		mockOrdererCaps.SupportedReturns(nil)
   398  	})
   399  
   400  	t.Run("MissingChannelCapability", func(t *testing.T) {
   401  		mockChannelCaps.SupportedReturns(errors.New("An error"))
   402  
   403  		err := checkResources(mockResources)
   404  		assert.Error(t, err)
   405  		assert.Regexp(t, "config requires unsupported channel capabilities:", err.Error())
   406  	})
   407  
   408  	t.Run("MissingOrdererConfigPanic", func(t *testing.T) {
   409  		mockResources.OrdererConfigReturns(nil, false)
   410  
   411  		assert.Panics(t, func() {
   412  			checkResourcesOrPanic(mockResources)
   413  		})
   414  	})
   415  }
   416  
   417  // The registrar's BroadcastChannelSupport implementation should reject message types which should not be processed directly.
   418  func TestBroadcastChannelSupport(t *testing.T) {
   419  	// system channel
   420  	confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir())
   421  	genesisBlockSys := encoder.New(confSys).GenesisBlock()
   422  
   423  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   424  	assert.NoError(t, err)
   425  
   426  	t.Run("Rejection", func(t *testing.T) {
   427  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   428  		require.NoError(t, err)
   429  		defer os.RemoveAll(tmpdir)
   430  
   431  		ledgerFactory, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
   432  		mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: &mockConsenter{}}
   433  		registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider)
   434  		registrar.Initialize(mockConsenters)
   435  		randomValue := 1
   436  		configTx := makeConfigTx("testchannelid", randomValue)
   437  		_, _, _, err = registrar.BroadcastChannelSupport(configTx)
   438  		assert.Error(t, err, "Messages of type HeaderType_CONFIG should return an error.")
   439  	})
   440  
   441  	t.Run("No system channel", func(t *testing.T) {
   442  		tmpdir, err := ioutil.TempDir("", "registrar_test-")
   443  		require.NoError(t, err)
   444  		defer os.RemoveAll(tmpdir)
   445  
   446  		ledgerFactory, _ := newLedgerAndFactory(tmpdir, "", nil)
   447  		mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: &mockConsenter{}}
   448  		config := localconfig.TopLevel{}
   449  		config.General.BootstrapMethod = "none"
   450  		config.General.GenesisFile = ""
   451  		registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider)
   452  		registrar.Initialize(mockConsenters)
   453  		configTx := makeConfigTxFull("testchannelid", 1)
   454  		_, _, _, err = registrar.BroadcastChannelSupport(configTx)
   455  		assert.Error(t, err)
   456  		assert.Equal(t, "channel creation request not allowed because the orderer system channel is not yet defined", err.Error())
   457  	})
   458  }