/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package channelconfig import ( "bytes" "fmt" "io/ioutil" "testing" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-config/protolator" cb "github.com/hyperledger/fabric-protos-go/common" mspprotos "github.com/hyperledger/fabric-protos-go/msp" ab "github.com/hyperledger/fabric-protos-go/orderer" "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" pb "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/bccsp/sw" "github.com/hyperledger/fabric/common/capabilities" "github.com/hyperledger/fabric/protoutil" "github.com/stretchr/testify/require" ) // The tests in this file are all relatively pointless, as all of this function is exercised // in the normal startup path and things will break horribly if they are broken. // There's additionally really nothing to test without simply re-implementing the function // in the test, which also provides no value. But, not including these produces an artificially // low code coverage count, so here they are. func basicTest(t *testing.T, sv *StandardConfigValue) { require.NotNil(t, sv) require.NotEmpty(t, sv.Key()) require.NotNil(t, sv.Value()) } func TestUtilsBasic(t *testing.T) { basicTest(t, ConsortiumValue("foo")) basicTest(t, HashingAlgorithmValue()) basicTest(t, BlockDataHashingStructureValue()) basicTest(t, OrdererAddressesValue([]string{"foo:1", "bar:2"})) basicTest(t, ConsensusTypeValue("foo", []byte("bar"))) basicTest(t, BatchSizeValue(1, 2, 3)) basicTest(t, BatchTimeoutValue("1s")) basicTest(t, ChannelRestrictionsValue(7)) basicTest(t, MSPValue(&mspprotos.MSPConfig{})) basicTest(t, CapabilitiesValue(map[string]bool{"foo": true, "bar": false})) basicTest(t, AnchorPeersValue([]*pb.AnchorPeer{{}, {}})) basicTest(t, ChannelCreationPolicyValue(&cb.Policy{})) basicTest(t, ACLValues(map[string]string{"foo": "fooval", "bar": "barval"})) } // createCfgBlockWithSupportedCapabilities will create a config block that contains valid capabilities and should be accepted by the peer func createCfgBlockWithSupportedCapabilities(t *testing.T) *cb.Block { // Create a config config := &cb.Config{ Sequence: 0, ChannelGroup: protoutil.NewConfigGroup(), } // construct the config for top group config.ChannelGroup.Version = 0 config.ChannelGroup.ModPolicy = AdminsPolicyKey config.ChannelGroup.Values[BlockDataHashingStructureKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{ Width: defaultBlockDataHashingStructureWidth, }), ModPolicy: AdminsPolicyKey, } topCapabilities := make(map[string]bool) topCapabilities[capabilities.ChannelV1_1] = true config.ChannelGroup.Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(topCapabilities).Value()), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[ConsortiumKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.Consortium{ Name: "testConsortium", }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[HashingAlgorithmKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{ Name: defaultHashingAlgorithm, }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[OrdererAddressesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{ Addresses: []string{"orderer.example.com"}, }), ModPolicy: AdminsPolicyKey, } // construct the config for Application group config.ChannelGroup.Groups[ApplicationGroupKey] = protoutil.NewConfigGroup() config.ChannelGroup.Groups[ApplicationGroupKey].Version = 0 config.ChannelGroup.Groups[ApplicationGroupKey].ModPolicy = AdminsPolicyKey config.ChannelGroup.Groups[ApplicationGroupKey].Policies[ReadersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[ApplicationGroupKey].Policies[WritersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[ApplicationGroupKey].Policies[AdminsPolicyKey] = &cb.ConfigPolicy{} appCapabilities := make(map[string]bool) appCapabilities[capabilities.ApplicationV1_1] = true config.ChannelGroup.Groups[ApplicationGroupKey].Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(appCapabilities).Value()), ModPolicy: AdminsPolicyKey, } // construct the config for Orderer group config.ChannelGroup.Groups[OrdererGroupKey] = protoutil.NewConfigGroup() config.ChannelGroup.Groups[OrdererGroupKey].Version = 0 config.ChannelGroup.Groups[OrdererGroupKey].ModPolicy = AdminsPolicyKey config.ChannelGroup.Groups[OrdererGroupKey].Policies[ReadersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Policies[WritersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Policies[AdminsPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchSizeKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.BatchSize{ MaxMessageCount: 65535, AbsoluteMaxBytes: 1024000000, PreferredMaxBytes: 1024000000, }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchTimeoutKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.BatchTimeout{ Timeout: "2s", }), ModPolicy: AdminsPolicyKey, } ordererCapabilities := make(map[string]bool) ordererCapabilities[capabilities.OrdererV1_1] = true config.ChannelGroup.Groups[OrdererGroupKey].Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(ordererCapabilities).Value()), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Groups[OrdererGroupKey].Values[ConsensusTypeKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.ConsensusType{ Type: "solo", }), ModPolicy: AdminsPolicyKey, } env := &cb.Envelope{ Payload: protoutil.MarshalOrPanic(&cb.Payload{ Header: &cb.Header{ ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ ChannelId: "testChain", Type: int32(cb.HeaderType_CONFIG), }), }, Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{ Config: config, }), }), } configBlock := &cb.Block{ Data: &cb.BlockData{ Data: [][]byte{[]byte(protoutil.MarshalOrPanic(env))}, }, } return configBlock } // createCfgBlockWithUnSupportedCapabilities will create a config block that contains mismatched capabilities and should be rejected by the peer func createCfgBlockWithUnsupportedCapabilities(t *testing.T) *cb.Block { // Create a config config := &cb.Config{ Sequence: 0, ChannelGroup: protoutil.NewConfigGroup(), } // construct the config for top group config.ChannelGroup.Version = 0 config.ChannelGroup.ModPolicy = AdminsPolicyKey config.ChannelGroup.Values[BlockDataHashingStructureKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.BlockDataHashingStructure{ Width: defaultBlockDataHashingStructureWidth, }), ModPolicy: AdminsPolicyKey, } topCapabilities := make(map[string]bool) topCapabilities["INCOMPATIBLE_CAPABILITIES"] = true config.ChannelGroup.Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(topCapabilities).Value()), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[ConsortiumKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.Consortium{ Name: "testConsortium", }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[HashingAlgorithmKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.HashingAlgorithm{ Name: defaultHashingAlgorithm, }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Values[OrdererAddressesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(&cb.OrdererAddresses{ Addresses: []string{"orderer.example.com"}, }), ModPolicy: AdminsPolicyKey, } // construct the config for Application group config.ChannelGroup.Groups[ApplicationGroupKey] = protoutil.NewConfigGroup() config.ChannelGroup.Groups[ApplicationGroupKey].Version = 0 config.ChannelGroup.Groups[ApplicationGroupKey].ModPolicy = AdminsPolicyKey config.ChannelGroup.Groups[ApplicationGroupKey].Policies[ReadersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[ApplicationGroupKey].Policies[WritersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[ApplicationGroupKey].Policies[AdminsPolicyKey] = &cb.ConfigPolicy{} appCapabilities := make(map[string]bool) appCapabilities["INCOMPATIBLE_CAPABILITIES"] = true config.ChannelGroup.Groups[ApplicationGroupKey].Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(appCapabilities).Value()), ModPolicy: AdminsPolicyKey, } // construct the config for Orderer group config.ChannelGroup.Groups[OrdererGroupKey] = protoutil.NewConfigGroup() config.ChannelGroup.Groups[OrdererGroupKey].Version = 0 config.ChannelGroup.Groups[OrdererGroupKey].ModPolicy = AdminsPolicyKey config.ChannelGroup.Groups[OrdererGroupKey].Policies[ReadersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Policies[WritersPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Policies[AdminsPolicyKey] = &cb.ConfigPolicy{} config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchSizeKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.BatchSize{ MaxMessageCount: 65535, AbsoluteMaxBytes: 1024000000, PreferredMaxBytes: 1024000000, }), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Groups[OrdererGroupKey].Values[BatchTimeoutKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.BatchTimeout{ Timeout: "2s", }), ModPolicy: AdminsPolicyKey, } ordererCapabilities := make(map[string]bool) ordererCapabilities["INCOMPATIBLE_CAPABILITIES"] = true config.ChannelGroup.Groups[OrdererGroupKey].Values[CapabilitiesKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic(CapabilitiesValue(ordererCapabilities).Value()), ModPolicy: AdminsPolicyKey, } config.ChannelGroup.Groups[OrdererGroupKey].Values[ConsensusTypeKey] = &cb.ConfigValue{ Value: protoutil.MarshalOrPanic( &ab.ConsensusType{ Type: "solo", }), ModPolicy: AdminsPolicyKey, } env := &cb.Envelope{ Payload: protoutil.MarshalOrPanic(&cb.Payload{ Header: &cb.Header{ ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ ChannelId: "testChain", Type: int32(cb.HeaderType_CONFIG), }), }, Data: protoutil.MarshalOrPanic(&cb.ConfigEnvelope{ Config: config, }), }), } configBlock := &cb.Block{ Data: &cb.BlockData{ Data: [][]byte{[]byte(protoutil.MarshalOrPanic(env))}, }, } return configBlock } func TestValidateCapabilities(t *testing.T) { cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) require.NoError(t, err) // Test config block with valid capabilities requirement cfgBlock := createCfgBlockWithSupportedCapabilities(t) err = ValidateCapabilities(cfgBlock, cryptoProvider) require.NoError(t, err) // Test config block with invalid capabilities requirement cfgBlock = createCfgBlockWithUnsupportedCapabilities(t) err = ValidateCapabilities(cfgBlock, cryptoProvider) require.EqualError(t, err, "Channel capability INCOMPATIBLE_CAPABILITIES is required but not supported") } func TestExtractMSPIDsForApplicationOrgs(t *testing.T) { // load test_configblock.json that contains the application group // and other properties needed to build channel config and extract MSPIDs blockData, err := ioutil.ReadFile("testdata/test_configblock.json") require.NoError(t, err) block := &cb.Block{} protolator.DeepUnmarshalJSON(bytes.NewBuffer(blockData), block) cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) require.NoError(t, err) mspids, err := ExtractMSPIDsForApplicationOrgs(block, cryptoProvider) require.NoError(t, err) require.ElementsMatch(t, mspids, []string{"Org1MSP", "Org2MSP"}) } func TestMarshalEtcdRaftMetadata(t *testing.T) { md := &etcdraft.ConfigMetadata{ Consenters: []*etcdraft.Consenter{ { Host: "node-1.example.com", Port: 7050, ClientTlsCert: []byte("testdata/tls-client-1.pem"), ServerTlsCert: []byte("testdata/tls-server-1.pem"), }, { Host: "node-2.example.com", Port: 7050, ClientTlsCert: []byte("testdata/tls-client-2.pem"), ServerTlsCert: []byte("testdata/tls-server-2.pem"), }, { Host: "node-3.example.com", Port: 7050, ClientTlsCert: []byte("testdata/tls-client-3.pem"), ServerTlsCert: []byte("testdata/tls-server-3.pem"), }, }, } packed, err := MarshalEtcdRaftMetadata(md) require.Nil(t, err, "marshalling should succeed") require.NotNil(t, packed) packed, err = MarshalEtcdRaftMetadata(md) require.Nil(t, err, "marshalling should succeed a second time because we did not mutate ourselves") require.NotNil(t, packed) unpacked := &etcdraft.ConfigMetadata{} require.Nil(t, proto.Unmarshal(packed, unpacked), "unmarshalling should succeed") var outputCerts, inputCerts [3][]byte for i := range unpacked.GetConsenters() { outputCerts[i] = []byte(unpacked.GetConsenters()[i].GetClientTlsCert()) inputCerts[i], _ = ioutil.ReadFile(fmt.Sprintf("testdata/tls-client-%d.pem", i+1)) } for i := 0; i < len(inputCerts)-1; i++ { require.NotEqual(t, outputCerts[i+1], outputCerts[i], "expected extracted certs to differ from each other") } }