github.com/yimialmonte/fabric@v2.1.1+incompatible/discovery/support/config/support_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package config_test
     8  
     9  import (
    10  	"crypto/rand"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"testing"
    17  
    18  	"github.com/golang/protobuf/proto"
    19  	"github.com/hyperledger/fabric-protos-go/common"
    20  	"github.com/hyperledger/fabric-protos-go/discovery"
    21  	"github.com/hyperledger/fabric-protos-go/msp"
    22  	"github.com/hyperledger/fabric/common/channelconfig"
    23  	"github.com/hyperledger/fabric/common/configtx"
    24  	"github.com/hyperledger/fabric/common/configtx/test"
    25  	"github.com/hyperledger/fabric/discovery/support/config"
    26  	"github.com/hyperledger/fabric/discovery/support/mocks"
    27  	"github.com/hyperledger/fabric/internal/configtxgen/encoder"
    28  	"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
    29  	"github.com/hyperledger/fabric/protoutil"
    30  	"github.com/onsi/gomega/gexec"
    31  	"github.com/stretchr/testify/assert"
    32  )
    33  
    34  func blockWithPayload() *common.Block {
    35  	env := &common.Envelope{
    36  		Payload: []byte{1, 2, 3},
    37  	}
    38  	b, _ := proto.Marshal(env)
    39  	return &common.Block{
    40  		Data: &common.BlockData{
    41  			Data: [][]byte{b},
    42  		},
    43  	}
    44  }
    45  
    46  func blockWithConfigEnvelope() *common.Block {
    47  	pl := &common.Payload{
    48  		Data: []byte{1, 2, 3},
    49  	}
    50  	plBytes, _ := proto.Marshal(pl)
    51  	env := &common.Envelope{
    52  		Payload: plBytes,
    53  	}
    54  	b, _ := proto.Marshal(env)
    55  	return &common.Block{
    56  		Data: &common.BlockData{
    57  			Data: [][]byte{b},
    58  		},
    59  	}
    60  }
    61  
    62  func TestMSPIDMapping(t *testing.T) {
    63  	randString := func() string {
    64  		buff := make([]byte, 10)
    65  		rand.Read(buff)
    66  		return hex.EncodeToString(buff)
    67  	}
    68  
    69  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("TestMSPIDMapping_%s", randString()))
    70  	os.Mkdir(dir, 0700)
    71  	defer os.RemoveAll(dir)
    72  
    73  	cryptogen, err := gexec.Build("github.com/hyperledger/fabric/cmd/cryptogen")
    74  	assert.NoError(t, err)
    75  	defer os.Remove(cryptogen)
    76  
    77  	idemixgen, err := gexec.Build("github.com/hyperledger/fabric/cmd/idemixgen")
    78  	assert.NoError(t, err)
    79  	defer os.Remove(idemixgen)
    80  
    81  	cryptoConfigDir := filepath.Join(dir, "crypto-config")
    82  	b, err := exec.Command(cryptogen, "generate", fmt.Sprintf("--output=%s", cryptoConfigDir)).CombinedOutput()
    83  	assert.NoError(t, err, string(b))
    84  
    85  	idemixConfigDir := filepath.Join(dir, "crypto-config", "idemix")
    86  	b, err = exec.Command(idemixgen, "ca-keygen", fmt.Sprintf("--output=%s", idemixConfigDir)).CombinedOutput()
    87  	assert.NoError(t, err, string(b))
    88  
    89  	profileConfig := genesisconfig.Load("TwoOrgsChannel", "testdata/")
    90  	ordererConfig := genesisconfig.Load("TwoOrgsOrdererGenesis", "testdata/")
    91  	profileConfig.Orderer = ordererConfig.Orderer
    92  
    93  	// Override the MSP directory with our randomly generated and populated path
    94  	for _, org := range ordererConfig.Orderer.Organizations {
    95  		org.MSPDir = filepath.Join(cryptoConfigDir, "ordererOrganizations", "example.com", "msp")
    96  		org.Name = randString()
    97  	}
    98  
    99  	// Randomize organization names
   100  	for _, org := range profileConfig.Application.Organizations {
   101  		org.Name = randString()
   102  		// Non bccsp-msp orgs don't have the crypto material produced by cryptogen,
   103  		// we need to use the idemix crypto folder instead.
   104  		if org.MSPType != "bccsp" {
   105  			org.MSPDir = filepath.Join(idemixConfigDir)
   106  			continue
   107  		}
   108  		org.MSPDir = filepath.Join(cryptoConfigDir, "peerOrganizations", "org1.example.com", "msp")
   109  	}
   110  
   111  	gen := encoder.New(profileConfig)
   112  	block := gen.GenesisBlockForChannel("mychannel")
   113  
   114  	fakeBlockGetter := &mocks.ConfigBlockGetter{}
   115  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   116  
   117  	cs := config.NewDiscoverySupport(fakeBlockGetter)
   118  	res, err := cs.Config("mychannel")
   119  
   120  	actualKeys := make(map[string]struct{})
   121  	for key := range res.Orderers {
   122  		actualKeys[key] = struct{}{}
   123  	}
   124  
   125  	for key := range res.Msps {
   126  		actualKeys[key] = struct{}{}
   127  	}
   128  
   129  	// Note that Org3MSP is an idemix org, but it shouldn't be listed here
   130  	// because peers can't have idemix credentials
   131  	expected := map[string]struct{}{
   132  		"OrdererMSP": {},
   133  		"Org1MSP":    {},
   134  		"Org2MSP":    {},
   135  	}
   136  	assert.Equal(t, expected, actualKeys)
   137  }
   138  
   139  func TestSupportGreenPath(t *testing.T) {
   140  	fakeBlockGetter := &mocks.ConfigBlockGetter{}
   141  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, nil)
   142  
   143  	cs := config.NewDiscoverySupport(fakeBlockGetter)
   144  	res, err := cs.Config("test")
   145  	assert.Nil(t, res)
   146  	assert.Equal(t, "could not get last config block for channel test", err.Error())
   147  
   148  	block, err := test.MakeGenesisBlock("test")
   149  	assert.NoError(t, err)
   150  	assert.NotNil(t, block)
   151  
   152  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, block)
   153  	res, err = cs.Config("test")
   154  	assert.NoError(t, err)
   155  	assert.NotNil(t, res)
   156  }
   157  
   158  func TestSupportBadConfig(t *testing.T) {
   159  	fakeBlockGetter := &mocks.ConfigBlockGetter{}
   160  	cs := config.NewDiscoverySupport(fakeBlockGetter)
   161  
   162  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, &common.Block{
   163  		Data: &common.BlockData{},
   164  	})
   165  	res, err := cs.Config("test")
   166  	assert.Contains(t, err.Error(), "no transactions in block")
   167  	assert.Nil(t, res)
   168  
   169  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, &common.Block{
   170  		Data: &common.BlockData{
   171  			Data: [][]byte{{1, 2, 3}},
   172  		},
   173  	})
   174  	res, err = cs.Config("test")
   175  	assert.Contains(t, err.Error(), "failed unmarshaling envelope")
   176  	assert.Nil(t, res)
   177  
   178  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(2, blockWithPayload())
   179  	res, err = cs.Config("test")
   180  	assert.Contains(t, err.Error(), "failed unmarshaling payload")
   181  	assert.Nil(t, res)
   182  
   183  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(3, blockWithConfigEnvelope())
   184  	res, err = cs.Config("test")
   185  	assert.Contains(t, err.Error(), "failed unmarshaling config envelope")
   186  	assert.Nil(t, res)
   187  }
   188  
   189  func TestValidateConfigEnvelope(t *testing.T) {
   190  	tests := []struct {
   191  		name          string
   192  		ce            *common.ConfigEnvelope
   193  		containsError string
   194  	}{
   195  		{
   196  			name:          "nil Config field",
   197  			ce:            &common.ConfigEnvelope{},
   198  			containsError: "field Config is nil",
   199  		},
   200  		{
   201  			name: "nil ChannelGroup field",
   202  			ce: &common.ConfigEnvelope{
   203  				Config: &common.Config{},
   204  			},
   205  			containsError: "field Config.ChannelGroup is nil",
   206  		},
   207  		{
   208  			name: "nil Groups field",
   209  			ce: &common.ConfigEnvelope{
   210  				Config: &common.Config{
   211  					ChannelGroup: &common.ConfigGroup{},
   212  				},
   213  			},
   214  			containsError: "field Config.ChannelGroup.Groups is nil",
   215  		},
   216  		{
   217  			name: "no orderer group key",
   218  			ce: &common.ConfigEnvelope{
   219  				Config: &common.Config{
   220  					ChannelGroup: &common.ConfigGroup{
   221  						Groups: map[string]*common.ConfigGroup{
   222  							channelconfig.ApplicationGroupKey: {},
   223  						},
   224  					},
   225  				},
   226  			},
   227  			containsError: "key Config.ChannelGroup.Groups[Orderer] is missing",
   228  		},
   229  		{
   230  			name: "no application group key",
   231  			ce: &common.ConfigEnvelope{
   232  				Config: &common.Config{
   233  					ChannelGroup: &common.ConfigGroup{
   234  						Groups: map[string]*common.ConfigGroup{
   235  							channelconfig.OrdererGroupKey: {
   236  								Groups: map[string]*common.ConfigGroup{},
   237  							},
   238  						},
   239  					},
   240  				},
   241  			},
   242  			containsError: "key Config.ChannelGroup.Groups[Application] is missing",
   243  		},
   244  		{
   245  			name: "no groups key in orderer group",
   246  			ce: &common.ConfigEnvelope{
   247  				Config: &common.Config{
   248  					ChannelGroup: &common.ConfigGroup{
   249  						Groups: map[string]*common.ConfigGroup{
   250  							channelconfig.ApplicationGroupKey: {
   251  								Groups: map[string]*common.ConfigGroup{},
   252  							},
   253  							channelconfig.OrdererGroupKey: {},
   254  						},
   255  					},
   256  				},
   257  			},
   258  			containsError: "key Config.ChannelGroup.Groups[Orderer].Groups is nil",
   259  		},
   260  		{
   261  			name: "no groups key in application group",
   262  			ce: &common.ConfigEnvelope{
   263  				Config: &common.Config{
   264  					ChannelGroup: &common.ConfigGroup{
   265  						Groups: map[string]*common.ConfigGroup{
   266  							channelconfig.ApplicationGroupKey: {},
   267  							channelconfig.OrdererGroupKey: {
   268  								Groups: map[string]*common.ConfigGroup{},
   269  							},
   270  						},
   271  					},
   272  				},
   273  			},
   274  			containsError: "key Config.ChannelGroup.Groups[Application].Groups is nil",
   275  		},
   276  		{
   277  			name: "no Values in ChannelGroup",
   278  			ce: &common.ConfigEnvelope{
   279  				Config: &common.Config{
   280  					ChannelGroup: &common.ConfigGroup{
   281  						Groups: map[string]*common.ConfigGroup{
   282  							channelconfig.ApplicationGroupKey: {
   283  								Groups: map[string]*common.ConfigGroup{},
   284  							},
   285  							channelconfig.OrdererGroupKey: {
   286  								Groups: map[string]*common.ConfigGroup{},
   287  							},
   288  						},
   289  					},
   290  				},
   291  			},
   292  			containsError: "field Config.ChannelGroup.Values is nil",
   293  		},
   294  	}
   295  
   296  	for _, test := range tests {
   297  		test := test
   298  		t.Run(test.name, func(t *testing.T) {
   299  			err := config.ValidateConfigEnvelope(test.ce)
   300  			assert.Contains(t, test.containsError, err.Error())
   301  		})
   302  	}
   303  
   304  }
   305  
   306  func TestOrdererEndpoints(t *testing.T) {
   307  	t.Run("Global endpoints", func(t *testing.T) {
   308  		block, err := test.MakeGenesisBlock("mychannel")
   309  		assert.NoError(t, err)
   310  
   311  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   312  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   313  
   314  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   315  
   316  		injectGlobalOrdererEndpoint(t, block, "globalEndpoint:7050")
   317  
   318  		res, err := cs.Config("test")
   319  		assert.NoError(t, err)
   320  		assert.Equal(t, map[string]*discovery.Endpoints{
   321  			"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "globalEndpoint", Port: 7050}}},
   322  		}, res.Orderers)
   323  	})
   324  
   325  	t.Run("Per org endpoints alongside global endpoints", func(t *testing.T) {
   326  		block, err := test.MakeGenesisBlock("mychannel")
   327  		assert.NoError(t, err)
   328  
   329  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   330  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   331  
   332  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   333  
   334  		injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "anotherOrg")
   335  		injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg")
   336  
   337  		res, err := cs.Config("test")
   338  		assert.NoError(t, err)
   339  		assert.Equal(t, map[string]*discovery.Endpoints{
   340  			"SampleOrg":  {Endpoint: []*discovery.Endpoint{{Host: "127.0.0.1", Port: 7050}}},
   341  			"anotherOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
   342  			"aBadOrg":    {},
   343  		}, res.Orderers)
   344  	})
   345  
   346  	t.Run("Per org endpoints without global endpoints", func(t *testing.T) {
   347  		block, err := test.MakeGenesisBlock("mychannel")
   348  		assert.NoError(t, err)
   349  
   350  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   351  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   352  
   353  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   354  
   355  		removeGlobalEndpoints(t, block)
   356  		injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "SampleOrg")
   357  		injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg")
   358  
   359  		res, err := cs.Config("test")
   360  		assert.NoError(t, err)
   361  		assert.Equal(t, map[string]*discovery.Endpoints{
   362  			"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
   363  			"aBadOrg":   {},
   364  		}, res.Orderers)
   365  	})
   366  }
   367  
   368  func removeGlobalEndpoints(t *testing.T, block *common.Block) {
   369  	// Unwrap the layers until we reach the orderer addresses
   370  	env, err := protoutil.ExtractEnvelope(block, 0)
   371  	assert.NoError(t, err)
   372  	payload := protoutil.UnmarshalPayloadOrPanic(env.Payload)
   373  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   374  	assert.NoError(t, err)
   375  	// Remove the orderer addresses
   376  	delete(confEnv.Config.ChannelGroup.Values, channelconfig.OrdererAddressesKey)
   377  	// And put it back into the block
   378  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   379  	env.Payload = protoutil.MarshalOrPanic(payload)
   380  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   381  }
   382  
   383  func injectGlobalOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) {
   384  	ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint})
   385  	// Unwrap the layers until we reach the orderer addresses
   386  	env, err := protoutil.ExtractEnvelope(block, 0)
   387  	assert.NoError(t, err)
   388  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   389  	assert.NoError(t, err)
   390  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   391  	assert.NoError(t, err)
   392  	// Replace the orderer addresses
   393  	confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()].Value = protoutil.MarshalOrPanic(ordererAddresses.Value())
   394  	// Remove the per org addresses, if applicable
   395  	ordererGrps := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
   396  	for _, grp := range ordererGrps {
   397  		if grp.Values[channelconfig.EndpointsKey] == nil {
   398  			continue
   399  		}
   400  		grp.Values[channelconfig.EndpointsKey].Value = nil
   401  	}
   402  	// And put it back into the block
   403  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   404  	env.Payload = protoutil.MarshalOrPanic(payload)
   405  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   406  }
   407  
   408  func injectAdditionalEndpointPair(t *testing.T, block *common.Block, endpoint string, orgName string) {
   409  	// Unwrap the layers until we reach the orderer addresses
   410  	env, err := protoutil.ExtractEnvelope(block, 0)
   411  	assert.NoError(t, err)
   412  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   413  	assert.NoError(t, err)
   414  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   415  	assert.NoError(t, err)
   416  	ordererGrp := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
   417  	// Get the first orderer org config
   418  	var firstOrdererConfig *common.ConfigGroup
   419  	for _, grp := range ordererGrp {
   420  		firstOrdererConfig = grp
   421  		break
   422  	}
   423  	// Duplicate it.
   424  	secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup)
   425  	ordererGrp[orgName] = secondOrdererConfig
   426  	// Reach the FabricMSPConfig buried in it.
   427  	mspConfig := &msp.MSPConfig{}
   428  	err = proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig)
   429  	assert.NoError(t, err)
   430  
   431  	fabricConfig := &msp.FabricMSPConfig{}
   432  	err = proto.Unmarshal(mspConfig.Config, fabricConfig)
   433  	assert.NoError(t, err)
   434  
   435  	// Rename it.
   436  	fabricConfig.Name = orgName
   437  
   438  	// Pack the MSP config back into the config
   439  	secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{
   440  		Config: protoutil.MarshalOrPanic(fabricConfig),
   441  		Type:   mspConfig.Type,
   442  	})
   443  
   444  	// Inject the endpoint
   445  	ordererOrgProtos := &common.OrdererAddresses{
   446  		Addresses: []string{endpoint},
   447  	}
   448  	secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos)
   449  
   450  	// Fold everything back into the block
   451  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   452  	env.Payload = protoutil.MarshalOrPanic(payload)
   453  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   454  }