From 7315556236e8127f55c6d5ef71f1524e4e3f25bf Mon Sep 17 00:00:00 2001 From: Tiffany Harris Date: Thu, 5 Mar 2020 10:38:48 -0500 Subject: [PATCH] [FAB-17582] Use custom API type for FabricMSPConfig proto - Custom API type is more consumer friendly. It accepts x509 certificates and ECDSA keys instead of byte slices and handles PEM encoding them to byte slices for the FabricMSPConfig proto. - General cleanup for application channel creation. - Cleanup for AddOrgToConsortium - Cleanup GetAnchorPeers logic/tests Signed-off-by: Danny Cao Signed-off-by: Tiffany Harris --- pkg/config/application.go | 6 +- pkg/config/application_test.go | 53 ++--- pkg/config/config.go | 19 +- pkg/config/config_test.go | 372 +++++++++++++++------------------ pkg/config/consortiums_test.go | 311 ++++++++++++++++----------- pkg/config/msp.go | 264 +++++++++++++++++++++++ pkg/config/msp_test.go | 141 +++++++++++++ pkg/config/orderer_test.go | 50 ++--- 8 files changed, 823 insertions(+), 393 deletions(-) create mode 100644 pkg/config/msp.go create mode 100644 pkg/config/msp_test.go diff --git a/pkg/config/application.go b/pkg/config/application.go index af15ea639be..56ec09c6f34 100644 --- a/pkg/config/application.go +++ b/pkg/config/application.go @@ -143,8 +143,8 @@ func RemoveAnchorPeer(config *cb.Config, orgName string, anchorPeerToRemove *Anc return fmt.Errorf("could not find anchor peer %s:%d in %s's anchor peer endpoints", anchorPeerToRemove.Host, anchorPeerToRemove.Port, orgName) } -// GetAnchorPeer retrieves existing anchor peers from a application organization. -func GetAnchorPeer(config *cb.Config, orgName string) ([]*AnchorPeer, error) { +// GetAnchorPeers retrieves existing anchor peers from a application organization. +func GetAnchorPeers(config *cb.Config, orgName string) ([]*AnchorPeer, error) { applicationOrgGroup, ok := config.ChannelGroup.Groups[ApplicationGroupKey].Groups[orgName] if !ok { return nil, fmt.Errorf("application org %s does not exist in channel config", orgName) @@ -152,7 +152,7 @@ func GetAnchorPeer(config *cb.Config, orgName string) ([]*AnchorPeer, error) { anchorPeerConfigValue, ok := applicationOrgGroup.Values[AnchorPeersKey] if !ok { - return nil, fmt.Errorf("application org %s does not have anchor peer", orgName) + return nil, fmt.Errorf("application org %s does not have anchor peers", orgName) } anchorPeersProto := &pb.AnchorPeers{} diff --git a/pkg/config/application_test.go b/pkg/config/application_test.go index d89e3c9c9f5..83d52331ead 100644 --- a/pkg/config/application_test.go +++ b/pkg/config/application_test.go @@ -12,7 +12,6 @@ import ( "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" - mb "github.com/hyperledger/fabric-protos-go/msp" pb "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/common/tools/protolator" . "github.com/onsi/gomega" @@ -549,17 +548,14 @@ func TestRemoveAnchorPeerFailure(t *testing.T) { func TestGetAnchorPeer(t *testing.T) { t.Parallel() + gt := NewGomegaWithT(t) channelGroup := newConfigGroup() - applicationGroup := newConfigGroup() - application := baseApplication() - for _, org := range application.Organizations { - orgGroup, err := newOrgConfigGroup(org) - gt.Expect(err).NotTo(HaveOccurred()) - applicationGroup.Groups[org.Name] = orgGroup - } + applicationGroup, err := newApplicationGroup(baseApplication()) + gt.Expect(err).NotTo(HaveOccurred()) + channelGroup.Groups[ApplicationGroupKey] = applicationGroup config := &cb.Config{ ChannelGroup: channelGroup, @@ -567,25 +563,30 @@ func TestGetAnchorPeer(t *testing.T) { expectedAnchorPeer := &AnchorPeer{Host: "host1", Port: 123} - anchorPeers, err := GetAnchorPeer(config, "Org1") + err = AddAnchorPeer(config, "Org1", expectedAnchorPeer) + gt.Expect(err).NotTo(HaveOccurred()) + + anchorPeers, err := GetAnchorPeers(config, "Org1") gt.Expect(err).NotTo(HaveOccurred()) gt.Expect(len(anchorPeers)).To(Equal(1)) gt.Expect(anchorPeers[0]).To(Equal(expectedAnchorPeer)) - } func TestGetAnchorPeerFailures(t *testing.T) { t.Parallel() + gt := NewGomegaWithT(t) channelGroup := newConfigGroup() - applicationGroup := newConfigGroup() + + applicationGroup, err := newApplicationGroup(baseApplication()) + gt.Expect(err).NotTo(HaveOccurred()) orgNoAnchor := &Organization{ - Name: "Org1", - ID: "Org1MSP", - Policies: applicationOrgStandardPolicies(), - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org1", + ID: "Org1MSP", + Policies: applicationOrgStandardPolicies(), + MSP: baseMSP(), } orgGroup, err := newOrgConfigGroup(orgNoAnchor) gt.Expect(err).NotTo(HaveOccurred()) @@ -609,17 +610,17 @@ func TestGetAnchorPeerFailures(t *testing.T) { expectedErr: "application org bad-org does not exist in channel config", }, { - name: "When org does not have an anchor peer", + name: "When org config group does not have an anchor peers value", config: config, orgName: "Org1", - expectedErr: "application org Org1 does not have anchor peer", + expectedErr: "application org Org1 does not have anchor peers", }, } { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() gt := NewGomegaWithT(t) - _, err := GetAnchorPeer(test.config, test.orgName) + _, err := GetAnchorPeers(test.config, test.orgName) gt.Expect(err).To(MatchError(test.expectedErr)) }) } @@ -630,22 +631,10 @@ func baseApplication() *Application { Policies: standardPolicies(), Organizations: []*Organization{ { - Name: "Org1", - ID: "Org1MSP", - Policies: applicationOrgStandardPolicies(), - AnchorPeers: []*AnchorPeer{ - {Host: "host1", Port: 123}, - }, - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org1", }, { - Name: "Org2", - ID: "Org2MSP", - Policies: applicationOrgStandardPolicies(), - AnchorPeers: []*AnchorPeer{ - {Host: "host2", Port: 123}, - }, - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org2", }, }, Capabilities: map[string]bool{ diff --git a/pkg/config/config.go b/pkg/config/config.go index 46f4f5cc6da..863c9ea8648 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -43,10 +43,10 @@ type Policy struct { // Organization is an organization in the channel configuration. type Organization struct { - Name string - ID string - Policies map[string]*Policy - MSPConfig *mb.FabricMSPConfig + Name string + ID string + Policies map[string]*Policy + MSP *MSP AnchorPeers []*AnchorPeer OrdererEndpoints []string @@ -230,7 +230,12 @@ func newOrgConfigGroup(org *Organization) (*cb.ConfigGroup, error) { return nil, err } - conf, err := proto.Marshal(org.MSPConfig) + fabricMSPConfig, err := org.MSP.toProto() + if err != nil { + return nil, fmt.Errorf("converting fabric msp config to proto: %v", err) + } + + conf, err := proto.Marshal(fabricMSPConfig) if err != nil { return nil, fmt.Errorf("marshalling msp config: %v", err) } @@ -247,7 +252,7 @@ func newOrgConfigGroup(org *Organization) (*cb.ConfigGroup, error) { // OrdererEndpoints are orderer org specific and are only added when specified for orderer orgs if len(org.OrdererEndpoints) > 0 { - err = addValue(orgGroup, endpointsValue(org.OrdererEndpoints), AdminsPolicyKey) + err := addValue(orgGroup, endpointsValue(org.OrdererEndpoints), AdminsPolicyKey) if err != nil { return nil, err } @@ -266,7 +271,7 @@ func newOrgConfigGroup(org *Organization) (*cb.ConfigGroup, error) { // This helps prevent a delta from the orderer system channel when computing // more complex channel creation transactions if len(anchorProtos) > 0 { - err = addValue(orgGroup, anchorPeersValue(anchorProtos), AdminsPolicyKey) + err := addValue(orgGroup, anchorPeersValue(anchorProtos), AdminsPolicyKey) if err != nil { return nil, fmt.Errorf("failed to add anchor peers value: %v", err) } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 98af41a278a..16a116b8678 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -14,11 +14,13 @@ import ( "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric/common/tools/protolator" + "github.com/hyperledger/fabric/common/tools/protolator/protoext/ordererext" . "github.com/onsi/gomega" ) func TestSignConfigUpdate(t *testing.T) { t.Parallel() + gt := NewGomegaWithT(t) cert, privateKey := generateCertAndPrivateKey() @@ -43,6 +45,8 @@ func TestSignConfigUpdate(t *testing.T) { func TestNewCreateChannelTx(t *testing.T) { t.Parallel() + gt := NewGomegaWithT(t) + // The TwoOrgsChannel profile is defined in standard_networks.go under the BasicSolo configuration // configtxgen -profile TwoOrgsChannel -channelID testChannel expectedEnvelopeJSON := `{ @@ -200,124 +204,71 @@ func TestNewCreateChannelTx(t *testing.T) { "signature": null }` - tests := []struct { - testName string - profileMod func() *Channel - }{ - { - testName: "When creating new create channel Tx with ImplicitMetaPolicyType", - profileMod: func() *Channel { - return baseProfile() - }, - }, - { - testName: "When creating new create channel Tx with ImplicitMetaPolicyType_ALL", - profileMod: func() *Channel { - profile := baseProfile() - profile.Policies[ReadersPolicyKey].Rule = "ALL Readers" - return profile - }, - }, - { - testName: "When creating new create channel Tx with SignatureTypePolicy", - profileMod: func() *Channel { - profile := baseProfile() - profile.Policies[ReadersPolicyKey].Type = SignaturePolicyType - profile.Policies[ReadersPolicyKey].Rule = "OutOf(1, 'A.member', 'B.member')" - return profile - }, - }, - { - testName: "When creating new create channel Tx with orderer defined in profile", - profileMod: func() *Channel { - profile := baseProfile() - profile.Orderer = &Orderer{ - OrdererType: ConsensusTypeSolo, - Addresses: []string{"1", "2"}, - Policies: standardPolicies(), - } - profile.Orderer.Policies[BlockValidationPolicyKey] = &Policy{ - Type: ImplicitMetaPolicyType, - Rule: "ANY something", - } - return profile - }, - }, - } - - for _, tt := range tests { - tt := tt // capture range variable - t.Run(tt.testName, func(t *testing.T) { - t.Parallel() - gt := NewGomegaWithT(t) - - profile := tt.profileMod() + profile := baseProfile() - // creating a create channel transaction - envelope, err := NewCreateChannelTx(profile) - gt.Expect(err).ToNot(HaveOccurred()) - gt.Expect(envelope).ToNot(BeNil()) + // creating a create channel transaction + envelope, err := NewCreateChannelTx(profile) + gt.Expect(err).ToNot(HaveOccurred()) + gt.Expect(envelope).ToNot(BeNil()) - // Unmarshalling actual and expected envelope to set - // the expected timestamp to the actual timestamp - expectedEnvelope := cb.Envelope{} - err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedEnvelopeJSON), &expectedEnvelope) - gt.Expect(err).ToNot(HaveOccurred()) + // Unmarshalling actual and expected envelope to set + // the expected timestamp to the actual timestamp + expectedEnvelope := cb.Envelope{} + err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedEnvelopeJSON), &expectedEnvelope) + gt.Expect(err).ToNot(HaveOccurred()) - expectedPayload := cb.Payload{} - err = proto.Unmarshal(expectedEnvelope.Payload, &expectedPayload) - gt.Expect(err).NotTo(HaveOccurred()) + expectedPayload := cb.Payload{} + err = proto.Unmarshal(expectedEnvelope.Payload, &expectedPayload) + gt.Expect(err).NotTo(HaveOccurred()) - expectedHeader := cb.ChannelHeader{} - err = proto.Unmarshal(expectedPayload.Header.ChannelHeader, &expectedHeader) - gt.Expect(err).NotTo(HaveOccurred()) + expectedHeader := cb.ChannelHeader{} + err = proto.Unmarshal(expectedPayload.Header.ChannelHeader, &expectedHeader) + gt.Expect(err).NotTo(HaveOccurred()) - expectedData := cb.ConfigUpdateEnvelope{} - err = proto.Unmarshal(expectedPayload.Data, &expectedData) - gt.Expect(err).NotTo(HaveOccurred()) + expectedData := cb.ConfigUpdateEnvelope{} + err = proto.Unmarshal(expectedPayload.Data, &expectedData) + gt.Expect(err).NotTo(HaveOccurred()) - expectedConfigUpdate := cb.ConfigUpdate{} - err = proto.Unmarshal(expectedData.ConfigUpdate, &expectedConfigUpdate) - gt.Expect(err).NotTo(HaveOccurred()) + expectedConfigUpdate := cb.ConfigUpdate{} + err = proto.Unmarshal(expectedData.ConfigUpdate, &expectedConfigUpdate) + gt.Expect(err).NotTo(HaveOccurred()) - actualPayload := cb.Payload{} - err = proto.Unmarshal(envelope.Payload, &actualPayload) - gt.Expect(err).NotTo(HaveOccurred()) + actualPayload := cb.Payload{} + err = proto.Unmarshal(envelope.Payload, &actualPayload) + gt.Expect(err).NotTo(HaveOccurred()) - actualHeader := cb.ChannelHeader{} - err = proto.Unmarshal(actualPayload.Header.ChannelHeader, &actualHeader) - gt.Expect(err).NotTo(HaveOccurred()) + actualHeader := cb.ChannelHeader{} + err = proto.Unmarshal(actualPayload.Header.ChannelHeader, &actualHeader) + gt.Expect(err).NotTo(HaveOccurred()) - actualData := cb.ConfigUpdateEnvelope{} - err = proto.Unmarshal(actualPayload.Data, &actualData) - gt.Expect(err).NotTo(HaveOccurred()) + actualData := cb.ConfigUpdateEnvelope{} + err = proto.Unmarshal(actualPayload.Data, &actualData) + gt.Expect(err).NotTo(HaveOccurred()) - actualConfigUpdate := cb.ConfigUpdate{} - err = proto.Unmarshal(actualData.ConfigUpdate, &actualConfigUpdate) - gt.Expect(err).NotTo(HaveOccurred()) + actualConfigUpdate := cb.ConfigUpdate{} + err = proto.Unmarshal(actualData.ConfigUpdate, &actualConfigUpdate) + gt.Expect(err).NotTo(HaveOccurred()) - gt.Expect(actualConfigUpdate).To(Equal(expectedConfigUpdate)) + gt.Expect(actualConfigUpdate).To(Equal(expectedConfigUpdate)) - // setting timestamps to match in ConfigUpdate - actualTimestamp := actualHeader.Timestamp + // setting timestamps to match in ConfigUpdate + actualTimestamp := actualHeader.Timestamp - expectedHeader.Timestamp = actualTimestamp + expectedHeader.Timestamp = actualTimestamp - expectedData.ConfigUpdate = actualData.ConfigUpdate + expectedData.ConfigUpdate = actualData.ConfigUpdate - // Remarshalling envelopes with updated timestamps - expectedPayload.Data, err = proto.Marshal(&expectedData) - gt.Expect(err).NotTo(HaveOccurred()) + // Remarshalling envelopes with updated timestamps + expectedPayload.Data, err = proto.Marshal(&expectedData) + gt.Expect(err).NotTo(HaveOccurred()) - expectedPayload.Header.ChannelHeader, err = proto.Marshal(&expectedHeader) - gt.Expect(err).NotTo(HaveOccurred()) + expectedPayload.Header.ChannelHeader, err = proto.Marshal(&expectedHeader) + gt.Expect(err).NotTo(HaveOccurred()) - expectedEnvelope.Payload, err = proto.Marshal(&expectedPayload) - gt.Expect(err).NotTo(HaveOccurred()) + expectedEnvelope.Payload, err = proto.Marshal(&expectedPayload) + gt.Expect(err).NotTo(HaveOccurred()) - gt.Expect(proto.Equal(envelope, &expectedEnvelope)).To(BeTrue()) - }) - } + gt.Expect(envelope).To(Equal(&expectedEnvelope)) } func TestNewCreateChannelTxFailure(t *testing.T) { @@ -557,86 +508,126 @@ func TestNewOrgConfigGroup(t *testing.T) { // The organization is from network.BasicSolo Profile // configtxgen -printOrg Org1 expectedPrintOrg := `{ - "groups": {}, - "mod_policy": "Admins", - "policies": { - "Admins": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Admins" - } + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" }, - "version": "0" - }, - "Endorsement": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Endorsement" - } + "Endorsement": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Endorsement" + } + }, + "version": "0" }, - "version": "0" - }, - "LifecycleEndorsement": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Endorsement" - } + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" }, - "version": "0" + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + } }, - "Readers": { - "mod_policy": "Admins", - "policy": { - "type": 3, + "values": { + "Endpoints": { + "mod_policy": "Admins", "value": { - "rule": "ANY", - "sub_policy": "Readers" - } + "addresses": [ + "localhost:123" + ] + }, + "version": "0" }, - "version": "0" - }, - "Writers": { - "mod_policy": "Admins", - "policy": { - "type": 3, + "MSP": { + "mod_policy": "Admins", "value": { - "rule": "ANY", - "sub_policy": "Writers" - } - }, - "version": "0" - } - }, - "values": { - "AnchorPeers": { - "mod_policy": "Admins", - "value": "CgkKBWhvc3QxEHs=", - "version": "0" + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "", + "signature_hash_family": "" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + }, + "intermediate_certs": [], + "name": "", + "organizational_unit_identifiers": [ + { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + ], + "revocation_list": [], + "root_certs": [], + "signing_identity": { + "private_signer": { + "key_identifier": "", + "key_material": null + }, + "public_signer": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + }, + "tls_intermediate_certs": [], + "tls_root_certs": [] + }, + "type": 0 + }, + "version": "0" + } }, - "MSP": { - "mod_policy": "Admins", - "value": "", - "version": "0" - } - }, - "version": "0" -} + "version": "0" + } ` - org := baseProfile().Application.Organizations[0] + org := baseSystemChannelProfile().Orderer.Organizations[0] configGroup, err := newOrgConfigGroup(org) gt.Expect(err).NotTo(HaveOccurred()) buf := bytes.Buffer{} - err = protolator.DeepMarshalJSON(&buf, configGroup) + err = protolator.DeepMarshalJSON(&buf, &ordererext.DynamicOrdererOrgGroup{ConfigGroup: configGroup}) gt.Expect(err).NotTo(HaveOccurred()) gt.Expect(buf.String()).To(MatchJSON(expectedPrintOrg)) @@ -646,40 +637,14 @@ func TestNewOrgConfigGroup(t *testing.T) { func TestNewOrgConfigGroupFailure(t *testing.T) { t.Parallel() - tests := []struct { - name string - organizationMod func(*Organization) - expectedErr string - }{ - { - "When failing to add policies", - func(o *Organization) { - o.Policies = nil - }, - "no policies defined", - }, - { - "When failing to add msp value", - func(o *Organization) { - o.MSPConfig = nil - }, - "marshalling msp config: proto: Marshal called with nil", - }, - } + gt := NewGomegaWithT(t) - for _, tt := range tests { - tt := tt // capture range variable - t.Run(tt.name, func(t *testing.T) { - t.Parallel() + baseOrg := baseSystemChannelProfile().Orderer.Organizations[0] + baseOrg.Policies = nil - gt := NewGomegaWithT(t) - baseOrg := baseProfile().Application.Organizations[0] - tt.organizationMod(baseOrg) - configGroup, err := newOrgConfigGroup(baseOrg) - gt.Expect(err).To(MatchError(tt.expectedErr)) - gt.Expect(configGroup).To(BeNil()) - }) - } + configGroup, err := newOrgConfigGroup(baseOrg) + gt.Expect(configGroup).To(BeNil()) + gt.Expect(err).To(MatchError("no policies defined")) } func TestComputeUpdate(t *testing.T) { @@ -777,6 +742,15 @@ func baseProfile() *Channel { Consortium: "SampleConsortium", Application: baseApplication(), Capabilities: map[string]bool{"V2_0": true}, + } +} + +func baseSystemChannelProfile() *Channel { + return &Channel{ + ChannelID: "testsystemchannel", + Consortiums: baseConsortiums(), + Orderer: baseOrderer(), + Capabilities: map[string]bool{"V2_0": true}, Policies: standardPolicies(), } } diff --git a/pkg/config/consortiums_test.go b/pkg/config/consortiums_test.go index fad4bc10f2e..353159b5da1 100644 --- a/pkg/config/consortiums_test.go +++ b/pkg/config/consortiums_test.go @@ -10,9 +10,7 @@ import ( "bytes" "testing" - "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" - mb "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric/common/tools/protolator" . "github.com/onsi/gomega" ) @@ -88,14 +86,16 @@ func TestAddOrgToConsortium(t *testing.T) { Groups: map[string]*cb.ConfigGroup{ "Consortiums": consortiumsGroup, }, + Values: map[string]*cb.ConfigValue{}, + Policies: map[string]*cb.ConfigPolicy{}, }, } orgToAdd := &Organization{ - Name: "Org1", - ID: "Org1MSP", - Policies: applicationOrgStandardPolicies(), - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org1", + ID: "Org1MSP", + Policies: orgStandardPolicies(), + MSP: baseMSP(), } expectedConfig := ` @@ -111,69 +111,102 @@ func TestAddOrgToConsortium(t *testing.T) { "mod_policy": "Admins", "policies": { "Admins": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Admins" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" }, "Endorsement": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Endorsement" - } - }, - "version": "0" - }, - "LifecycleEndorsement": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Endorsement" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Endorsement" + } + }, + "version": "0" }, "Readers": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "ANY", - "sub_policy": "Readers" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" }, "Writers": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "ANY", - "sub_policy": "Writers" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" } }, "values": { "MSP": { - "mod_policy": "Admins", - "value": { - "config": null, - "type": 0 - }, - "version": "0" + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "", + "signature_hash_family": "" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + }, + "intermediate_certs": [], + "name": "", + "organizational_unit_identifiers": [ + { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + ], + "revocation_list": [], + "root_certs": [], + "signing_identity": { + "private_signer": { + "key_identifier": "", + "key_material": null + }, + "public_signer": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + }, + "tls_intermediate_certs": [], + "tls_root_certs": [] + }, + "type": 0 + }, + "version": "0" } }, "version": "0" @@ -183,58 +216,102 @@ func TestAddOrgToConsortium(t *testing.T) { "mod_policy": "Admins", "policies": { "Admins": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Admins" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" }, "Endorsement": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "MAJORITY", - "sub_policy": "Endorsement" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Endorsement" + } + }, + "version": "0" }, "Readers": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "ANY", - "sub_policy": "Readers" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" }, "Writers": { - "mod_policy": "Admins", - "policy": { - "type": 3, - "value": { - "rule": "ANY", - "sub_policy": "Writers" - } - }, - "version": "0" + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" } }, "values": { "MSP": { - "mod_policy": "Admins", - "value": { - "config": null, - "type": 0 - }, - "version": "0" + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "", + "signature_hash_family": "" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + }, + "intermediate_certs": [], + "name": "", + "organizational_unit_identifiers": [ + { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + ], + "revocation_list": [], + "root_certs": [], + "signing_identity": { + "private_signer": { + "key_identifier": "", + "key_material": null + }, + "public_signer": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + }, + "tls_intermediate_certs": [], + "tls_root_certs": [] + }, + "type": 0 + }, + "version": "0" } }, "version": "0" @@ -248,8 +325,8 @@ func TestAddOrgToConsortium(t *testing.T) { "value": { "type": 3, "value": { - "rule": "ANY", - "sub_policy": "Admins" + "rule": "ANY", + "sub_policy": "Admins" } }, "version": "0" @@ -268,8 +345,8 @@ func TestAddOrgToConsortium(t *testing.T) { "identities": [], "rule": { "n_out_of": { - "n": 0, - "rules": [] + "n": 0, + "rules": [] } }, "version": 0 @@ -291,14 +368,14 @@ func TestAddOrgToConsortium(t *testing.T) { } ` - expectedConfigProto := cb.Config{} - err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedConfig), &expectedConfigProto) + expectedConfigProto := &cb.Config{} + err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedConfig), expectedConfigProto) gt.Expect(err).NotTo(HaveOccurred()) err = AddOrgToConsortium(config, orgToAdd, "Consortium1") gt.Expect(err).NotTo(HaveOccurred()) - gt.Expect(proto.Equal(config, &expectedConfigProto)).To(BeTrue()) + gt.Expect(config).To(Equal(expectedConfigProto)) } func TestAddOrgToConsortiumFailures(t *testing.T) { @@ -307,7 +384,7 @@ func TestAddOrgToConsortiumFailures(t *testing.T) { orgToAdd := &Organization{ Name: "test-org", ID: "test-org-msp-id", - Policies: applicationOrgStandardPolicies(), + Policies: orgStandardPolicies(), } for _, test := range []struct { @@ -378,16 +455,16 @@ func baseConsortiums() []*Consortium { Name: "Consortium1", Organizations: []*Organization{ { - Name: "Org1", - ID: "Org1MSP", - Policies: orgStandardPolicies(), - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org1", + ID: "Org1MSP", + Policies: orgStandardPolicies(), + MSP: baseMSP(), }, { - Name: "Org2", - ID: "Org2MSP", - Policies: orgStandardPolicies(), - MSPConfig: &mb.FabricMSPConfig{}, + Name: "Org2", + ID: "Org2MSP", + Policies: orgStandardPolicies(), + MSP: baseMSP(), }, }, }, diff --git a/pkg/config/msp.go b/pkg/config/msp.go new file mode 100644 index 00000000000..08107e0db77 --- /dev/null +++ b/pkg/config/msp.go @@ -0,0 +1,264 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package config + +import ( + "crypto/ecdsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "fmt" + + mb "github.com/hyperledger/fabric-protos-go/msp" +) + +// MSP is the configuration information for +// a Fabric MSP. +// Here we assume a default certificate validation policy, where +// any certificate signed by any of the listed rootCA certs would +// be considered as valid under this MSP. +// This MSP may or may not come with a signing identity. If it does, +// it can also issue signing identities. If it does not, it can only +// be used to validate and verify certificates. +type MSP struct { + // Name holds the identifier of the MSP; MSP identifier + // is chosen by the application that governs this MSP. + // For example, and assuming the default implementation of MSP, + // that is X.509-based and considers a single Issuer, + // this can refer to the Subject OU field or the Issuer OU field. + Name string + // List of root certificates trusted by this MSP + // they are used upon certificate validation (see + // comment for IntermediateCerts below). + RootCerts []x509.Certificate + // List of intermediate certificates trusted by this MSP; + // they are used upon certificate validation as follows: + // validation attempts to build a path from the certificate + // to be validated (which is at one end of the path) and + // one of the certs in the RootCerts field (which is at + // the other end of the path). If the path is longer than + // 2, certificates in the middle are searched within the + // IntermediateCerts pool. + IntermediateCerts []x509.Certificate + // Identity denoting the administrator of this MSP. + Admins []x509.Certificate + // Identity revocation list. + RevocationList []pkix.CertificateList + // SigningIdentity holds information on the signing identity + // this peer is to use, and which is to be imported by the + // MSP defined before. + SigningIdentity *SigningIdentityInfo + // OrganizationalUnitIdentifiers holds one or more + // fabric organizational unit identifiers that belong to + // this MSP configuration. + OrganizationalUnitIdentifiers []*OUIdentifier + // CryptoConfig contains the configuration parameters + // for the cryptographic algorithms used by this MSP. + CryptoConfig *CryptoConfig + // List of TLS root certificates trusted by this MSP. + // They are returned by GetTLSRootCerts. + TLSRootCerts []x509.Certificate + // List of TLS intermediate certificates trusted by this MSP; + // They are returned by GetTLSIntermediateCerts. + TLSIntermediateCerts []x509.Certificate + // fabric_node_ous contains the configuration to distinguish clients from peers from orderers + // based on the OUs. + NodeOus *NodeOUs +} + +// SigningIdentityInfo represents the configuration information +// related to the signing identity the peer is to use for generating +// endorsements. +type SigningIdentityInfo struct { + // PublicSigner carries the public information of the signing + // identity. For an X.509 provider this would be represented by + // an X.509 certificate. + PublicSigner x509.Certificate + // PrivateSigner denotes a reference to the private key of the + // peer's signing identity. + PrivateSigner *KeyInfo +} + +// KeyInfo represents a (secret) key that is either already stored +// in the bccsp/keystore or key material to be imported to the +// bccsp key-store. In later versions it may contain also a +// keystore identifier. +type KeyInfo struct { + // Identifier of the key inside the default keystore; this for + // the case of Software BCCSP as well as the HSM BCCSP would be + // the SKI of the key. + KeyIdentifier string + // KeyMaterial (optional) for the key to be imported; this is + // properly encoded key bytes, prefixed by the type of the key. + KeyMaterial *ecdsa.PrivateKey +} + +// OUIdentifier represents an organizational unit and +// its related chain of trust identifier. +type OUIdentifier struct { + // Certificate represents the second certificate in a certification chain. + // (Notice that the first certificate in a certification chain is supposed + // to be the certificate of an identity). + // It must correspond to the certificate of root or intermediate CA + // recognized by the MSP this message belongs to. + // Starting from this certificate, a certification chain is computed + // and bound to the OrganizationUnitIdentifier specified. + Certificate x509.Certificate + // OrganizationUnitIdentifier defines the organizational unit under the + // MSP identified with MSPIdentifier. + OrganizationalUnitIdentifier string +} + +// CryptoConfig contains configuration parameters +// for the cryptographic algorithms used by the MSP +// this configuration refers to. +type CryptoConfig struct { + // SignatureHashFamily is a string representing the hash family to be used + // during sign and verify operations. + // Allowed values are "SHA2" and "SHA3". + SignatureHashFamily string + // IdentityIdentifierHashFunction is a string representing the hash function + // to be used during the computation of the identity identifier of an MSP identity. + // Allowed values are "SHA256", "SHA384" and "SHA3_256", "SHA3_384". + IdentityIdentifierHashFunction string +} + +// NodeOUs contains configuration to tell apart clients from peers from orderers +// based on OUs. If NodeOUs recognition is enabled then an msp identity +// that does not contain any of the specified OU will be considered invalid. +type NodeOUs struct { + // If true then an msp identity that does not contain any of the specified OU will be considered invalid. + Enable bool + // OU Identifier of the clients. + ClientOuIdentifier *OUIdentifier + // OU Identifier of the peers. + PeerOuIdentifier *OUIdentifier + // OU Identifier of the admins. + AdminOuIdentifier *OUIdentifier + // OU Identifier of the orderers. + OrdererOuIdentifier *OUIdentifier +} + +// toProto converts an MSP configuration to an mb.FabricMSPConfig proto. +// It pem encodes x509 certificates and ECDSA private keys to byte slices. +func (m *MSP) toProto() (*mb.FabricMSPConfig, error) { + var err error + + // KeyMaterial is an optional EDCSA private key + keyMaterial := []byte{} + if m.SigningIdentity.PrivateSigner.KeyMaterial != nil { + keyMaterial, err = pemEncodeECDSAPrivateKey(m.SigningIdentity.PrivateSigner.KeyMaterial) + if err != nil { + return nil, fmt.Errorf("pem encode X.509 private key: %v", err) + } + } + + crl, err := buildPemEncodedCRL(m.RevocationList) + if err != nil { + return nil, fmt.Errorf("building pem encoded crl: %v", err) + } + + signingIdentity := &mb.SigningIdentityInfo{ + PublicSigner: pemEncodeX509Certificate(m.SigningIdentity.PublicSigner), + PrivateSigner: &mb.KeyInfo{ + KeyIdentifier: m.SigningIdentity.PrivateSigner.KeyIdentifier, + KeyMaterial: keyMaterial, + }, + } + + ouIdentifiers := buildOUIdentifiers(m.OrganizationalUnitIdentifiers) + + fabricNodeOUs := &mb.FabricNodeOUs{ + Enable: m.NodeOus.Enable, + ClientOuIdentifier: &mb.FabricOUIdentifier{ + Certificate: pemEncodeX509Certificate(m.NodeOus.ClientOuIdentifier.Certificate), + OrganizationalUnitIdentifier: m.NodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier, + }, + PeerOuIdentifier: &mb.FabricOUIdentifier{ + Certificate: pemEncodeX509Certificate(m.NodeOus.PeerOuIdentifier.Certificate), + OrganizationalUnitIdentifier: m.NodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier, + }, + AdminOuIdentifier: &mb.FabricOUIdentifier{ + Certificate: pemEncodeX509Certificate(m.NodeOus.AdminOuIdentifier.Certificate), + OrganizationalUnitIdentifier: m.NodeOus.AdminOuIdentifier.OrganizationalUnitIdentifier, + }, + OrdererOuIdentifier: &mb.FabricOUIdentifier{ + Certificate: pemEncodeX509Certificate(m.NodeOus.OrdererOuIdentifier.Certificate), + OrganizationalUnitIdentifier: m.NodeOus.OrdererOuIdentifier.OrganizationalUnitIdentifier, + }, + } + + return &mb.FabricMSPConfig{ + Name: m.Name, + RootCerts: buildPemEncodedCertListFromX509(m.RootCerts), + IntermediateCerts: buildPemEncodedCertListFromX509(m.IntermediateCerts), + Admins: buildPemEncodedCertListFromX509(m.Admins), + RevocationList: crl, + SigningIdentity: signingIdentity, + OrganizationalUnitIdentifiers: ouIdentifiers, + CryptoConfig: &mb.FabricCryptoConfig{ + SignatureHashFamily: m.CryptoConfig.SignatureHashFamily, + IdentityIdentifierHashFunction: m.CryptoConfig.IdentityIdentifierHashFunction, + }, + TlsRootCerts: buildPemEncodedCertListFromX509(m.TLSRootCerts), + TlsIntermediateCerts: buildPemEncodedCertListFromX509(m.TLSIntermediateCerts), + FabricNodeOus: fabricNodeOUs, + }, nil +} + +func buildOUIdentifiers(identifiers []*OUIdentifier) []*mb.FabricOUIdentifier { + fabricIdentifiers := []*mb.FabricOUIdentifier{} + + for _, identifier := range identifiers { + fabricOUIdentifier := &mb.FabricOUIdentifier{ + Certificate: pemEncodeX509Certificate(identifier.Certificate), + OrganizationalUnitIdentifier: identifier.OrganizationalUnitIdentifier, + } + + fabricIdentifiers = append(fabricIdentifiers, fabricOUIdentifier) + } + + return fabricIdentifiers +} + +func buildPemEncodedCRL(crls []pkix.CertificateList) ([][]byte, error) { + pemEncodedCRL := [][]byte{} + + for _, crl := range crls { + asn1MarshalledBytes, err := asn1.Marshal(crl) + if err != nil { + return nil, fmt.Errorf("asn1 marshalling: %v", err) + } + + pemEncodedCRL = append(pemEncodedCRL, pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: asn1MarshalledBytes})) + } + + return pemEncodedCRL, nil +} + +func buildPemEncodedCertListFromX509(certList []x509.Certificate) [][]byte { + certs := [][]byte{} + for _, cert := range certList { + certs = append(certs, pemEncodeX509Certificate(cert)) + } + + return certs +} + +func pemEncodeX509Certificate(cert x509.Certificate) []byte { + return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) +} + +func pemEncodeECDSAPrivateKey(priv *ecdsa.PrivateKey) ([]byte, error) { + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return nil, fmt.Errorf("marshalling PKCS8 private key: %v", err) + } + + return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}), nil +} diff --git a/pkg/config/msp_test.go b/pkg/config/msp_test.go new file mode 100644 index 00000000000..516ed374c7d --- /dev/null +++ b/pkg/config/msp_test.go @@ -0,0 +1,141 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package config + +import ( + "bytes" + "crypto/ecdsa" + "crypto/x509" + "crypto/x509/pkix" + "testing" + + mb "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric/common/tools/protolator" + . "github.com/onsi/gomega" +) + +func TestMSPToProto(t *testing.T) { + t.Parallel() + + gt := NewGomegaWithT(t) + + msp := baseMSP() + + expectedFabricMSPConfigProtoJSON := ` +{ + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "", + "signature_hash_family": "" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + }, + "intermediate_certs": [], + "name": "", + "organizational_unit_identifiers": [ + { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "" + } + ], + "revocation_list": [], + "root_certs": [], + "signing_identity": { + "private_signer": { + "key_identifier": "", + "key_material": "" + }, + "public_signer": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + }, + "tls_intermediate_certs": [], + "tls_root_certs": [] +} +` + + expectedFabricMSPConfigProto := &mb.FabricMSPConfig{} + err := protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedFabricMSPConfigProtoJSON), expectedFabricMSPConfigProto) + gt.Expect(err).NotTo(HaveOccurred()) + + fabricMSPConfigProto, err := msp.toProto() + gt.Expect(err).NotTo(HaveOccurred()) + gt.Expect(fabricMSPConfigProto).To(Equal(expectedFabricMSPConfigProto)) +} + +func TestMSPToProtoFailure(t *testing.T) { + t.Parallel() + + gt := NewGomegaWithT(t) + + fabricMSPConfig := baseMSP() + fabricMSPConfig.SigningIdentity.PrivateSigner.KeyMaterial = &ecdsa.PrivateKey{} + + fabricMSPConfigProto, err := fabricMSPConfig.toProto() + gt.Expect(err).To(MatchError("pem encode X.509 private key: marshalling PKCS8 private key: x509: unknown curve while marshaling to PKCS#8")) + gt.Expect(fabricMSPConfigProto).To(BeNil()) +} + +func baseMSP() *MSP { + return &MSP{ + Name: "", + RootCerts: []x509.Certificate{}, + IntermediateCerts: []x509.Certificate{}, + Admins: []x509.Certificate{}, + RevocationList: []pkix.CertificateList{}, + SigningIdentity: &SigningIdentityInfo{ + PrivateSigner: &KeyInfo{}, + }, + OrganizationalUnitIdentifiers: []*OUIdentifier{ + { + Certificate: x509.Certificate{ + Raw: []byte{}, + }, + }, + }, + CryptoConfig: &CryptoConfig{}, + TLSRootCerts: []x509.Certificate{}, + TLSIntermediateCerts: []x509.Certificate{}, + NodeOus: &NodeOUs{ + ClientOuIdentifier: &OUIdentifier{ + Certificate: x509.Certificate{ + Raw: []byte{}, + }, + }, + PeerOuIdentifier: &OUIdentifier{ + Certificate: x509.Certificate{ + Raw: []byte{}, + }, + }, + AdminOuIdentifier: &OUIdentifier{ + Certificate: x509.Certificate{ + Raw: []byte{}, + }, + }, + OrdererOuIdentifier: &OUIdentifier{ + Certificate: x509.Certificate{ + Raw: []byte{}, + }, + }, + }, + } +} diff --git a/pkg/config/orderer_test.go b/pkg/config/orderer_test.go index ad27df4582e..611dc0e6eb5 100644 --- a/pkg/config/orderer_test.go +++ b/pkg/config/orderer_test.go @@ -12,7 +12,6 @@ import ( "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" - mb "github.com/hyperledger/fabric-protos-go/msp" ob "github.com/hyperledger/fabric-protos-go/orderer" eb "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" . "github.com/onsi/gomega" @@ -46,9 +45,8 @@ func TestNewOrdererGroup(t *testing.T) { gt.Expect(err).NotTo(HaveOccurred()) // OrdererGroup checks - gt.Expect(len(ordererGroup.Groups)).To(Equal(2)) - gt.Expect(ordererGroup.Groups["Org1"]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org2"]).NotTo(BeNil()) + gt.Expect(len(ordererGroup.Groups)).To(Equal(1)) + gt.Expect(ordererGroup.Groups["OrdererOrg"]).NotTo(BeNil()) gt.Expect(len(ordererGroup.Values)).To(Equal(tt.numOrdererGroupValues)) gt.Expect(ordererGroup.Values[BatchSizeKey]).NotTo(BeNil()) gt.Expect(ordererGroup.Values[BatchTimeoutKey]).NotTo(BeNil()) @@ -66,24 +64,15 @@ func TestNewOrdererGroup(t *testing.T) { gt.Expect(ordererGroup.Policies[BlockValidationPolicyKey]).NotTo(BeNil()) // OrdererOrgGroup checks - gt.Expect(len(ordererGroup.Groups["Org1"].Groups)).To(Equal(0)) - gt.Expect(len(ordererGroup.Groups["Org1"].Values)).To(Equal(2)) - gt.Expect(ordererGroup.Groups["Org1"].Values[MSPKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org1"].Values[EndpointsKey]).NotTo(BeNil()) - gt.Expect(len(ordererGroup.Groups["Org1"].Policies)).To(Equal(4)) - gt.Expect(ordererGroup.Groups["Org1"].Policies[AdminsPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org1"].Policies[ReadersPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org1"].Policies[WritersPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org1"].Policies[EndorsementPolicyKey]).NotTo(BeNil()) - gt.Expect(len(ordererGroup.Groups["Org2"].Groups)).To(Equal(0)) - gt.Expect(len(ordererGroup.Groups["Org2"].Values)).To(Equal(2)) - gt.Expect(ordererGroup.Groups["Org2"].Values[MSPKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org2"].Values[EndpointsKey]).NotTo(BeNil()) - gt.Expect(len(ordererGroup.Groups["Org2"].Policies)).To(Equal(4)) - gt.Expect(ordererGroup.Groups["Org2"].Policies[AdminsPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org2"].Policies[ReadersPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org2"].Policies[WritersPolicyKey]).NotTo(BeNil()) - gt.Expect(ordererGroup.Groups["Org2"].Policies[EndorsementPolicyKey]).NotTo(BeNil()) + gt.Expect(len(ordererGroup.Groups["OrdererOrg"].Groups)).To(Equal(0)) + gt.Expect(len(ordererGroup.Groups["OrdererOrg"].Values)).To(Equal(2)) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Values[MSPKey]).NotTo(BeNil()) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Values[EndpointsKey]).NotTo(BeNil()) + gt.Expect(len(ordererGroup.Groups["OrdererOrg"].Policies)).To(Equal(4)) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Policies[AdminsPolicyKey]).NotTo(BeNil()) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Policies[ReadersPolicyKey]).NotTo(BeNil()) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Policies[WritersPolicyKey]).NotTo(BeNil()) + gt.Expect(ordererGroup.Groups["OrdererOrg"].Policies[EndorsementPolicyKey]).NotTo(BeNil()) }) } } @@ -154,7 +143,7 @@ func TestNewOrdererGroupFailure(t *testing.T) { ordererMod: func(o *Orderer) { o.Organizations[0].Policies = nil }, - err: errors.New("org group 'Org1': no policies defined"), + err: errors.New("org group 'OrdererOrg': no policies defined"), }, } @@ -285,22 +274,13 @@ func baseOrderer() *Orderer { OrdererType: ConsensusTypeSolo, Organizations: []*Organization{ { - Name: "Org1", - ID: "Org1MSP", + Name: "OrdererOrg", + ID: "OrdererOrgMSP", Policies: orgStandardPolicies(), OrdererEndpoints: []string{ "localhost:123", }, - MSPConfig: &mb.FabricMSPConfig{}, - }, - { - Name: "Org2", - ID: "Org2MSP", - Policies: orgStandardPolicies(), - OrdererEndpoints: []string{ - "localhost:123", - }, - MSPConfig: &mb.FabricMSPConfig{}, + MSP: baseMSP(), }, }, Capabilities: map[string]bool{