github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/channelconfig"
    23  	"github.com/osdi23p228/fabric/common/configtx"
    24  	"github.com/osdi23p228/fabric/common/configtx/test"
    25  	"github.com/osdi23p228/fabric/discovery/support/config"
    26  	"github.com/osdi23p228/fabric/discovery/support/mocks"
    27  	"github.com/osdi23p228/fabric/internal/configtxgen/encoder"
    28  	"github.com/osdi23p228/fabric/internal/configtxgen/genesisconfig"
    29  	"github.com/osdi23p228/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/osdi23p228/fabric/cmd/cryptogen")
    74  	assert.NoError(t, err)
    75  	defer os.Remove(cryptogen)
    76  
    77  	idemixgen, err := gexec.Build("github.com/osdi23p228/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  	assert.NoError(t, err)
   120  
   121  	actualKeys := make(map[string]struct{})
   122  	for key := range res.Orderers {
   123  		actualKeys[key] = struct{}{}
   124  	}
   125  
   126  	for key := range res.Msps {
   127  		actualKeys[key] = struct{}{}
   128  	}
   129  
   130  	// Note that Org3MSP is an idemix org, but it shouldn't be listed here
   131  	// because peers can't have idemix credentials
   132  	expected := map[string]struct{}{
   133  		"OrdererMSP": {},
   134  		"Org1MSP":    {},
   135  		"Org2MSP":    {},
   136  	}
   137  	assert.Equal(t, expected, actualKeys)
   138  }
   139  
   140  func TestSupportGreenPath(t *testing.T) {
   141  	fakeBlockGetter := &mocks.ConfigBlockGetter{}
   142  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, nil)
   143  
   144  	cs := config.NewDiscoverySupport(fakeBlockGetter)
   145  	res, err := cs.Config("test")
   146  	assert.Nil(t, res)
   147  	assert.Equal(t, "could not get last config block for channel test", err.Error())
   148  
   149  	block, err := test.MakeGenesisBlock("test")
   150  	assert.NoError(t, err)
   151  	assert.NotNil(t, block)
   152  
   153  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, block)
   154  	res, err = cs.Config("test")
   155  	assert.NoError(t, err)
   156  	assert.NotNil(t, res)
   157  }
   158  
   159  func TestSupportBadConfig(t *testing.T) {
   160  	fakeBlockGetter := &mocks.ConfigBlockGetter{}
   161  	cs := config.NewDiscoverySupport(fakeBlockGetter)
   162  
   163  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, &common.Block{
   164  		Data: &common.BlockData{},
   165  	})
   166  	res, err := cs.Config("test")
   167  	assert.Contains(t, err.Error(), "no transactions in block")
   168  	assert.Nil(t, res)
   169  
   170  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(1, &common.Block{
   171  		Data: &common.BlockData{
   172  			Data: [][]byte{{1, 2, 3}},
   173  		},
   174  	})
   175  	res, err = cs.Config("test")
   176  	assert.Contains(t, err.Error(), "failed unmarshaling envelope")
   177  	assert.Nil(t, res)
   178  
   179  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(2, blockWithPayload())
   180  	res, err = cs.Config("test")
   181  	assert.Contains(t, err.Error(), "failed unmarshaling payload")
   182  	assert.Nil(t, res)
   183  
   184  	fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(3, blockWithConfigEnvelope())
   185  	res, err = cs.Config("test")
   186  	assert.Contains(t, err.Error(), "failed unmarshaling config envelope")
   187  	assert.Nil(t, res)
   188  }
   189  
   190  func TestValidateConfigEnvelope(t *testing.T) {
   191  	tests := []struct {
   192  		name          string
   193  		ce            *common.ConfigEnvelope
   194  		containsError string
   195  	}{
   196  		{
   197  			name:          "nil Config field",
   198  			ce:            &common.ConfigEnvelope{},
   199  			containsError: "field Config is nil",
   200  		},
   201  		{
   202  			name: "nil ChannelGroup field",
   203  			ce: &common.ConfigEnvelope{
   204  				Config: &common.Config{},
   205  			},
   206  			containsError: "field Config.ChannelGroup is nil",
   207  		},
   208  		{
   209  			name: "nil Groups field",
   210  			ce: &common.ConfigEnvelope{
   211  				Config: &common.Config{
   212  					ChannelGroup: &common.ConfigGroup{},
   213  				},
   214  			},
   215  			containsError: "field Config.ChannelGroup.Groups is nil",
   216  		},
   217  		{
   218  			name: "no orderer group key",
   219  			ce: &common.ConfigEnvelope{
   220  				Config: &common.Config{
   221  					ChannelGroup: &common.ConfigGroup{
   222  						Groups: map[string]*common.ConfigGroup{
   223  							channelconfig.ApplicationGroupKey: {},
   224  						},
   225  					},
   226  				},
   227  			},
   228  			containsError: "key Config.ChannelGroup.Groups[Orderer] is missing",
   229  		},
   230  		{
   231  			name: "no application group key",
   232  			ce: &common.ConfigEnvelope{
   233  				Config: &common.Config{
   234  					ChannelGroup: &common.ConfigGroup{
   235  						Groups: map[string]*common.ConfigGroup{
   236  							channelconfig.OrdererGroupKey: {
   237  								Groups: map[string]*common.ConfigGroup{},
   238  							},
   239  						},
   240  					},
   241  				},
   242  			},
   243  			containsError: "key Config.ChannelGroup.Groups[Application] is missing",
   244  		},
   245  		{
   246  			name: "no groups key in orderer group",
   247  			ce: &common.ConfigEnvelope{
   248  				Config: &common.Config{
   249  					ChannelGroup: &common.ConfigGroup{
   250  						Groups: map[string]*common.ConfigGroup{
   251  							channelconfig.ApplicationGroupKey: {
   252  								Groups: map[string]*common.ConfigGroup{},
   253  							},
   254  							channelconfig.OrdererGroupKey: {},
   255  						},
   256  					},
   257  				},
   258  			},
   259  			containsError: "key Config.ChannelGroup.Groups[Orderer].Groups is nil",
   260  		},
   261  		{
   262  			name: "no groups key in application group",
   263  			ce: &common.ConfigEnvelope{
   264  				Config: &common.Config{
   265  					ChannelGroup: &common.ConfigGroup{
   266  						Groups: map[string]*common.ConfigGroup{
   267  							channelconfig.ApplicationGroupKey: {},
   268  							channelconfig.OrdererGroupKey: {
   269  								Groups: map[string]*common.ConfigGroup{},
   270  							},
   271  						},
   272  					},
   273  				},
   274  			},
   275  			containsError: "key Config.ChannelGroup.Groups[Application].Groups is nil",
   276  		},
   277  		{
   278  			name: "no Values in ChannelGroup",
   279  			ce: &common.ConfigEnvelope{
   280  				Config: &common.Config{
   281  					ChannelGroup: &common.ConfigGroup{
   282  						Groups: map[string]*common.ConfigGroup{
   283  							channelconfig.ApplicationGroupKey: {
   284  								Groups: map[string]*common.ConfigGroup{},
   285  							},
   286  							channelconfig.OrdererGroupKey: {
   287  								Groups: map[string]*common.ConfigGroup{},
   288  							},
   289  						},
   290  					},
   291  				},
   292  			},
   293  			containsError: "field Config.ChannelGroup.Values is nil",
   294  		},
   295  	}
   296  
   297  	for _, test := range tests {
   298  		test := test
   299  		t.Run(test.name, func(t *testing.T) {
   300  			err := config.ValidateConfigEnvelope(test.ce)
   301  			assert.Contains(t, test.containsError, err.Error())
   302  		})
   303  	}
   304  
   305  }
   306  
   307  func TestOrdererEndpoints(t *testing.T) {
   308  	t.Run("Global endpoints", func(t *testing.T) {
   309  		block, err := test.MakeGenesisBlock("mychannel")
   310  		assert.NoError(t, err)
   311  
   312  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   313  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   314  
   315  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   316  
   317  		injectGlobalOrdererEndpoint(t, block, "globalEndpoint:7050")
   318  
   319  		res, err := cs.Config("test")
   320  		assert.NoError(t, err)
   321  		assert.Equal(t, map[string]*discovery.Endpoints{
   322  			"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "globalEndpoint", Port: 7050}}},
   323  		}, res.Orderers)
   324  	})
   325  
   326  	t.Run("Per org endpoints alongside global endpoints", func(t *testing.T) {
   327  		block, err := test.MakeGenesisBlock("mychannel")
   328  		assert.NoError(t, err)
   329  
   330  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   331  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   332  
   333  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   334  
   335  		injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "anotherOrg")
   336  		injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg")
   337  
   338  		res, err := cs.Config("test")
   339  		assert.NoError(t, err)
   340  		assert.Equal(t, map[string]*discovery.Endpoints{
   341  			"SampleOrg":  {Endpoint: []*discovery.Endpoint{{Host: "127.0.0.1", Port: 7050}}},
   342  			"anotherOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
   343  			"aBadOrg":    {},
   344  		}, res.Orderers)
   345  	})
   346  
   347  	t.Run("Per org endpoints without global endpoints", func(t *testing.T) {
   348  		block, err := test.MakeGenesisBlock("mychannel")
   349  		assert.NoError(t, err)
   350  
   351  		fakeBlockGetter := &mocks.ConfigBlockGetter{}
   352  		cs := config.NewDiscoverySupport(fakeBlockGetter)
   353  
   354  		fakeBlockGetter.GetCurrConfigBlockReturnsOnCall(0, block)
   355  
   356  		removeGlobalEndpoints(t, block)
   357  		injectAdditionalEndpointPair(t, block, "perOrgEndpoint:7050", "SampleOrg")
   358  		injectAdditionalEndpointPair(t, block, "endpointWithoutAPortName", "aBadOrg")
   359  
   360  		res, err := cs.Config("test")
   361  		assert.NoError(t, err)
   362  		assert.Equal(t, map[string]*discovery.Endpoints{
   363  			"SampleOrg": {Endpoint: []*discovery.Endpoint{{Host: "perOrgEndpoint", Port: 7050}}},
   364  			"aBadOrg":   {},
   365  		}, res.Orderers)
   366  	})
   367  }
   368  
   369  func removeGlobalEndpoints(t *testing.T, block *common.Block) {
   370  	// Unwrap the layers until we reach the orderer addresses
   371  	env, err := protoutil.ExtractEnvelope(block, 0)
   372  	assert.NoError(t, err)
   373  	payload := protoutil.UnmarshalPayloadOrPanic(env.Payload)
   374  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   375  	assert.NoError(t, err)
   376  	// Remove the orderer addresses
   377  	delete(confEnv.Config.ChannelGroup.Values, channelconfig.OrdererAddressesKey)
   378  	// And put it back into the block
   379  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   380  	env.Payload = protoutil.MarshalOrPanic(payload)
   381  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   382  }
   383  
   384  func injectGlobalOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) {
   385  	ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint})
   386  	// Unwrap the layers until we reach the orderer addresses
   387  	env, err := protoutil.ExtractEnvelope(block, 0)
   388  	assert.NoError(t, err)
   389  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   390  	assert.NoError(t, err)
   391  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   392  	assert.NoError(t, err)
   393  	// Replace the orderer addresses
   394  	confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()] = &common.ConfigValue{
   395  		Value:     protoutil.MarshalOrPanic(ordererAddresses.Value()),
   396  		ModPolicy: "/Channel/Orderer/Admins",
   397  	}
   398  	// Remove the per org addresses, if applicable
   399  	ordererGrps := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
   400  	for _, grp := range ordererGrps {
   401  		if grp.Values[channelconfig.EndpointsKey] == nil {
   402  			continue
   403  		}
   404  		grp.Values[channelconfig.EndpointsKey].Value = nil
   405  	}
   406  	// And put it back into the block
   407  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   408  	env.Payload = protoutil.MarshalOrPanic(payload)
   409  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   410  }
   411  
   412  func injectAdditionalEndpointPair(t *testing.T, block *common.Block, endpoint string, orgName string) {
   413  	// Unwrap the layers until we reach the orderer addresses
   414  	env, err := protoutil.ExtractEnvelope(block, 0)
   415  	assert.NoError(t, err)
   416  	payload, err := protoutil.UnmarshalPayload(env.Payload)
   417  	assert.NoError(t, err)
   418  	confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data)
   419  	assert.NoError(t, err)
   420  	ordererGrp := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups
   421  	// Get the first orderer org config
   422  	var firstOrdererConfig *common.ConfigGroup
   423  	for _, grp := range ordererGrp {
   424  		firstOrdererConfig = grp
   425  		break
   426  	}
   427  	// Duplicate it.
   428  	secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup)
   429  	ordererGrp[orgName] = secondOrdererConfig
   430  	// Reach the FabricMSPConfig buried in it.
   431  	mspConfig := &msp.MSPConfig{}
   432  	err = proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig)
   433  	assert.NoError(t, err)
   434  
   435  	fabricConfig := &msp.FabricMSPConfig{}
   436  	err = proto.Unmarshal(mspConfig.Config, fabricConfig)
   437  	assert.NoError(t, err)
   438  
   439  	// Rename it.
   440  	fabricConfig.Name = orgName
   441  
   442  	// Pack the MSP config back into the config
   443  	secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{
   444  		Config: protoutil.MarshalOrPanic(fabricConfig),
   445  		Type:   mspConfig.Type,
   446  	})
   447  
   448  	// Inject the endpoint
   449  	ordererOrgProtos := &common.OrdererAddresses{
   450  		Addresses: []string{endpoint},
   451  	}
   452  	secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos)
   453  
   454  	// Fold everything back into the block
   455  	payload.Data = protoutil.MarshalOrPanic(confEnv)
   456  	env.Payload = protoutil.MarshalOrPanic(payload)
   457  	block.Data.Data[0] = protoutil.MarshalOrPanic(env)
   458  }