From 4bcdec8a4ecd9bbb8eb414ac1d7a0d6e430bba5f Mon Sep 17 00:00:00 2001 From: Will Lahti Date: Thu, 25 Jun 2020 16:54:23 -0400 Subject: [PATCH] Add function to create application channel genesis block (#31) FAB-18027 Signed-off-by: Will Lahti --- configtx/application.go | 24 +- configtx/application_test.go | 65 ++- configtx/config.go | 187 +++++---- configtx/config_test.go | 773 ++++++++++++++++++++++++++++++++++- 4 files changed, 932 insertions(+), 117 deletions(-) diff --git a/configtx/application.go b/configtx/application.go index 8c2074c..b802ee7 100644 --- a/configtx/application.go +++ b/configtx/application.go @@ -434,9 +434,10 @@ func (a *ApplicationOrg) CreateMSPCRL(signingIdentity *SigningIdentity, certs .. return msp.newMSPCRL(signingIdentity, certs...) } -// newApplicationGroup returns the application component of the channel configuration. +// newApplicationGroupTemplate returns the application component of the channel +// configuration with only the names of the application organizations. // By default, it sets the mod_policy of all elements to "Admins". -func newApplicationGroup(application Application) (*cb.ConfigGroup, error) { +func newApplicationGroupTemplate(application Application) (*cb.ConfigGroup, error) { var err error applicationGroup := newConfigGroup() @@ -467,6 +468,25 @@ func newApplicationGroup(application Application) (*cb.ConfigGroup, error) { return applicationGroup, nil } +// newApplicationGroup returns the application component of the channel +// configuration with the entire configuration for application organizations. +// By default, it sets the mod_policy of all elements to "Admins". +func newApplicationGroup(application Application) (*cb.ConfigGroup, error) { + applicationGroup, err := newApplicationGroupTemplate(application) + if err != nil { + return nil, err + } + + for _, org := range application.Organizations { + applicationGroup.Groups[org.Name], err = newOrgConfigGroup(org) + if err != nil { + return nil, fmt.Errorf("org group '%s': %v", org.Name, err) + } + } + + return applicationGroup, nil +} + // aclValues returns the config definition for an application's resources based ACL definitions. // It is a value for the /Channel/Application/. func aclValues(acls map[string]string) *standardConfigValue { diff --git a/configtx/application_test.go b/configtx/application_test.go index d71330b..64a3b0e 100644 --- a/configtx/application_test.go +++ b/configtx/application_test.go @@ -19,7 +19,6 @@ import ( "github.com/hyperledger/fabric-config/protolator" "github.com/hyperledger/fabric-config/protolator/protoext/peerext" cb "github.com/hyperledger/fabric-protos-go/common" - mb "github.com/hyperledger/fabric-protos-go/msp" . "github.com/onsi/gomega" ) @@ -100,7 +99,7 @@ func TestNewApplicationGroup(t *testing.T) { } ` - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) expectedApplication := &cb.ConfigGroup{} @@ -199,7 +198,7 @@ func TestNewApplicationGroupFailure(t *testing.T) { application, _ := baseApplication(t) tt.applicationMod(&application) - configGrp, err := newApplicationGroup(application) + configGrp, err := newApplicationGroupTemplate(application) gt.Expect(err).To(MatchError(tt.expectedErr)) gt.Expect(configGrp).To(BeNil()) }) @@ -213,7 +212,7 @@ func TestAddAnchorPeer(t *testing.T) { baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -376,7 +375,7 @@ func TestRemoveAnchorPeer(t *testing.T) { baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -531,7 +530,7 @@ func TestRemoveAnchorPeerFailure(t *testing.T) { baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) applicationGroup.Groups["Org1"].Values = tt.configValues @@ -560,7 +559,7 @@ func TestAnchorPeers(t *testing.T) { channelGroup := newConfigGroup() application, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) channelGroup.Groups[ApplicationGroupKey] = applicationGroup @@ -629,7 +628,7 @@ func TestSetACL(t *testing.T) { channelGroup := newConfigGroup() baseApplication, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplication) + applicationGroup, err := newApplicationGroupTemplate(baseApplication) channelGroup.Groups[ApplicationGroupKey] = applicationGroup config := &cb.Config{ @@ -693,7 +692,7 @@ func TestRemoveACL(t *testing.T) { baseApplication, _ := baseApplication(t) baseApplication.ACLs["acl2"] = "acl2Value" baseApplication.ACLs["acl3"] = "acl3Value" - applicationGroup, err := newApplicationGroup(baseApplication) + applicationGroup, err := newApplicationGroupTemplate(baseApplication) channelGroup.Groups[ApplicationGroupKey] = applicationGroup config := &cb.Config{ @@ -904,7 +903,7 @@ func TestSetApplicationOrgFailures(t *testing.T) { gt := NewGomegaWithT(t) application, _ := baseApplication(t) - appGroup, err := newApplicationGroup(application) + appGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -930,7 +929,7 @@ func TestApplicationConfiguration(t *testing.T) { gt := NewGomegaWithT(t) baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -986,7 +985,7 @@ func TestApplicationConfigurationFailure(t *testing.T) { gt := NewGomegaWithT(t) baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1016,7 +1015,7 @@ func TestApplicationACLs(t *testing.T) { gt := NewGomegaWithT(t) baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1040,7 +1039,7 @@ func TestApplicationACLsFailure(t *testing.T) { gt := NewGomegaWithT(t) baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1068,7 +1067,7 @@ func TestApplicationCapabilities(t *testing.T) { gt := NewGomegaWithT(t) baseApplicationConf, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(baseApplicationConf) + applicationGroup, err := newApplicationGroupTemplate(baseApplicationConf) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1277,7 +1276,7 @@ func TestAddApplicationCapability(t *testing.T) { gt := NewGomegaWithT(t) baseApp, _ := baseApplication(t) - appGroup, err := newApplicationGroup(baseApp) + appGroup, err := newApplicationGroupTemplate(baseApp) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1341,7 +1340,7 @@ func TestAddApplicationCapabilityFailures(t *testing.T) { gt := NewGomegaWithT(t) baseApp, _ := baseApplication(t) - appGroup, err := newApplicationGroup(baseApp) + appGroup, err := newApplicationGroupTemplate(baseApp) gt.Expect(err).NotTo(HaveOccurred()) tt.applicationGroup(appGroup) @@ -1367,7 +1366,7 @@ func TestRemoveApplicationCapability(t *testing.T) { gt := NewGomegaWithT(t) baseApp, _ := baseApplication(t) - appGroup, err := newApplicationGroup(baseApp) + appGroup, err := newApplicationGroupTemplate(baseApp) gt.Expect(err).NotTo(HaveOccurred()) config := &cb.Config{ @@ -1505,7 +1504,7 @@ func TestRemoveApplicationCapabilityFailures(t *testing.T) { gt := NewGomegaWithT(t) baseApp, _ := baseApplication(t) - appGroup, err := newApplicationGroup(baseApp) + appGroup, err := newApplicationGroupTemplate(baseApp) gt.Expect(err).NotTo(HaveOccurred()) tt.applicationGroup(appGroup) @@ -1743,7 +1742,7 @@ func TestSetApplicationPolicy(t *testing.T) { channelGroup := newConfigGroup() application, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) channelGroup.Groups[ApplicationGroupKey] = applicationGroup @@ -1788,7 +1787,7 @@ func TestSetApplicationPolicyFailures(t *testing.T) { channelGroup := newConfigGroup() application, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) channelGroup.Groups[ApplicationGroupKey] = applicationGroup @@ -1812,7 +1811,7 @@ func TestRemoveApplicationPolicy(t *testing.T) { channelGroup := newConfigGroup() application, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) applicationGroup.Policies["TestPolicy"] = applicationGroup.Policies[AdminsPolicyKey] @@ -1854,7 +1853,7 @@ func TestRemoveApplicationPolicyFailures(t *testing.T) { channelGroup := newConfigGroup() application, _ := baseApplication(t) - applicationGroup, err := newApplicationGroup(application) + applicationGroup, err := newApplicationGroupTemplate(application) gt.Expect(err).NotTo(HaveOccurred()) applicationGroup.Policies[EndorsementPolicyKey] = &cb.ConfigPolicy{ @@ -1879,28 +1878,10 @@ func TestApplicationMSP(t *testing.T) { gt := NewGomegaWithT(t) - expectedMSP, _ := baseMSP(t) - application, _ := baseApplication(t) applicationGroup, err := newApplicationGroup(application) gt.Expect(err).NotTo(HaveOccurred()) - // We need to add the base MSP config to the base application since - // newApplicationGroup doesn't apply MSP configuration - applicationOrgGroup := applicationGroup.Groups["Org1"] - fabricMSPConfig, err := expectedMSP.toProto() - gt.Expect(err).NotTo(HaveOccurred()) - - conf, err := proto.Marshal(fabricMSPConfig) - gt.Expect(err).NotTo(HaveOccurred()) - - mspConfig := &mb.MSPConfig{ - Config: conf, - } - - err = setValue(applicationOrgGroup, mspValue(mspConfig), AdminsPolicyKey) - gt.Expect(err).NotTo(HaveOccurred()) - config := &cb.Config{ ChannelGroup: &cb.ConfigGroup{ Groups: map[string]*cb.ConfigGroup{ @@ -1913,7 +1894,7 @@ func TestApplicationMSP(t *testing.T) { msp, err := c.Application().Organization("Org1").MSP() gt.Expect(err).NotTo(HaveOccurred()) - gt.Expect(msp).To(Equal(expectedMSP)) + gt.Expect(msp).To(Equal(application.Organizations[0].MSP)) } func TestSetApplicationMSPFailure(t *testing.T) { diff --git a/configtx/config.go b/configtx/config.go index cd53f24..3ae3669 100644 --- a/configtx/config.go +++ b/configtx/config.go @@ -172,11 +172,9 @@ func NewMarshaledCreateChannelTx(channelConfig Channel, channelID string) ([]byt return marshaledUpdate, nil } -// NewSystemChannelGenesisBlock creates a genesis block using the provided consortiums and orderer -// configuration and returns a block. +// NewSystemChannelGenesisBlock creates a genesis block using the provided +// consortiums and orderer configuration and returns a block. func NewSystemChannelGenesisBlock(channelConfig Channel, channelID string) (*cb.Block, error) { - var err error - if channelID == "" { return nil, errors.New("system channel ID is required") } @@ -186,7 +184,7 @@ func NewSystemChannelGenesisBlock(channelConfig Channel, channelID string) (*cb. return nil, fmt.Errorf("creating system channel group: %v", err) } - block, err := newSystemChannelBlock(systemChannelGroup, channelID) + block, err := newGenesisBlock(systemChannelGroup, channelID) if err != nil { return nil, fmt.Errorf("creating system channel genesis block: %v", err) } @@ -194,40 +192,70 @@ func NewSystemChannelGenesisBlock(channelConfig Channel, channelID string) (*cb. return block, nil } -// newChannelGroup defines the root of the channel configuration. -func newChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { - var err error +// NewApplicationChannelGenesisBlock creates a genesis block using the provided +// application and orderer configuration and returns a block. +func NewApplicationChannelGenesisBlock(channelConfig Channel, channelID string) (*cb.Block, error) { + if channelID == "" { + return nil, errors.New("application channel ID is required") + } - channelGroup := newConfigGroup() + applicationChannelGroup, err := newApplicationChannelGroup(channelConfig) + if err != nil { + return nil, fmt.Errorf("creating application channel group: %v", err) + } - if channelConfig.Consortium == "" { - return nil, errors.New("consortium is not defined in channel config") + block, err := newGenesisBlock(applicationChannelGroup, channelID) + if err != nil { + return nil, fmt.Errorf("creating application channel genesis block: %v", err) } - err = setValue(channelGroup, consortiumValue(channelConfig.Consortium), "") + return block, nil +} + +// newSystemChannelGroup defines the root of the system channel configuration. +func newSystemChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { + channelGroup, err := newChannelGroupWithOrderer(channelConfig) if err != nil { return nil, err } - channelGroup.Groups[ApplicationGroupKey], err = newApplicationGroup(channelConfig.Application) + consortiumsGroup, err := newConsortiumsGroup(channelConfig.Consortiums) if err != nil { - return nil, fmt.Errorf("failed to create application group: %v", err) + return nil, err } + channelGroup.Groups[ConsortiumsGroupKey] = consortiumsGroup channelGroup.ModPolicy = AdminsPolicyKey return channelGroup, nil } -// newSystemChannelGroup defines the root of the system channel configuration. -func newSystemChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { - var err error +// newApplicationChannelGroup defines the root of the application +// channel configuration. +func newApplicationChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { + channelGroup, err := newChannelGroupWithOrderer(channelConfig) + if err != nil { + return nil, err + } + + applicationGroup, err := newApplicationGroup(channelConfig.Application) + if err != nil { + return nil, err + } + + channelGroup.Groups[ApplicationGroupKey] = applicationGroup + channelGroup.ModPolicy = AdminsPolicyKey + + return channelGroup, nil +} + +func newChannelGroupWithOrderer(channelConfig Channel) (*cb.ConfigGroup, error) { channelGroup := newConfigGroup() - err = setPolicies(channelGroup, channelConfig.Policies, AdminsPolicyKey) + err := setPolicies(channelGroup, channelConfig.Policies, AdminsPolicyKey) if err != nil { - return nil, fmt.Errorf("failed to set system channel policies: %v", err) + return nil, fmt.Errorf("setting channel policies: %v", err) } err = setValue(channelGroup, hashingAlgorithmValue(), AdminsPolicyKey) @@ -255,15 +283,65 @@ func newSystemChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { } channelGroup.Groups[OrdererGroupKey] = ordererGroup - consortiumsGroup, err := newConsortiumsGroup(channelConfig.Consortiums) + return channelGroup, nil +} + +// newGenesisBlock generates a genesis block from the config group and +// channel ID. The block number is always zero. +func newGenesisBlock(cg *cb.ConfigGroup, channelID string) (*cb.Block, error) { + payloadChannelHeader := channelHeader(cb.HeaderType_CONFIG, msgVersion, channelID, epoch) + nonce, err := newNonce() if err != nil { - return nil, err + return nil, fmt.Errorf("creating nonce: %v", err) + } + payloadSignatureHeader := &cb.SignatureHeader{Creator: nil, Nonce: nonce} + payloadChannelHeader.TxId = computeTxID(payloadSignatureHeader.Nonce, payloadSignatureHeader.Creator) + payloadHeader, err := payloadHeader(payloadChannelHeader, payloadSignatureHeader) + if err != nil { + return nil, fmt.Errorf("construct payload header: %v", err) + } + payloadData, err := proto.Marshal(&cb.ConfigEnvelope{Config: &cb.Config{ChannelGroup: cg}}) + if err != nil { + return nil, fmt.Errorf("marshaling payload data: %v", err) + } + payload := &cb.Payload{Header: payloadHeader, Data: payloadData} + envelopePayload, err := proto.Marshal(payload) + if err != nil { + return nil, fmt.Errorf("marshaling envelope payload: %v", err) + } + envelope := &cb.Envelope{Payload: envelopePayload, Signature: nil} + blockData, err := proto.Marshal(envelope) + if err != nil { + return nil, fmt.Errorf("marshaling envelope: %v", err) } - channelGroup.Groups[ConsortiumsGroupKey] = consortiumsGroup - channelGroup.ModPolicy = AdminsPolicyKey + block := newBlock(0, nil) + block.Data = &cb.BlockData{Data: [][]byte{blockData}} + block.Header.DataHash = blockDataHash(block.Data) - return channelGroup, nil + lastConfigValue, err := proto.Marshal(&cb.LastConfig{Index: 0}) + if err != nil { + return nil, fmt.Errorf("marshaling metadata last config value: %v", err) + } + lastConfigMetadata, err := proto.Marshal(&cb.Metadata{Value: lastConfigValue}) + if err != nil { + return nil, fmt.Errorf("marshaling metadata last config: %v", err) + } + block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = lastConfigMetadata + + signatureValue, err := proto.Marshal(&cb.OrdererBlockMetadata{ + LastConfig: &cb.LastConfig{Index: 0}, + }) + if err != nil { + return nil, fmt.Errorf("marshaling metadata signature value: %v", err) + } + signatureMetadata, err := proto.Marshal(&cb.Metadata{Value: signatureValue}) + if err != nil { + return nil, fmt.Errorf("marshaling metadata signature: %v", err) + } + block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = signatureMetadata + + return block, nil } // setValue sets the value as ConfigValue in the ConfigGroup. @@ -338,62 +416,27 @@ func defaultConfigTemplate(channelConfig Channel) (*cb.ConfigGroup, error) { return channelGroup, nil } -// newSystemChannelBlock generates a genesis block by the config group and system channel ID -// the block num is always zero -func newSystemChannelBlock(cg *cb.ConfigGroup, channelID string) (*cb.Block, error) { - payloadChannelHeader := channelHeader(cb.HeaderType_CONFIG, msgVersion, channelID, epoch) - nonce, err := newNonce() - if err != nil { - return nil, fmt.Errorf("try to get nonce: %v", err) - } - payloadSignatureHeader := &cb.SignatureHeader{Creator: nil, Nonce: nonce} - payloadChannelHeader.TxId = computeTxID(payloadSignatureHeader.Nonce, payloadSignatureHeader.Creator) - payloadHeader, err := payloadHeader(payloadChannelHeader, payloadSignatureHeader) - if err != nil { - return nil, fmt.Errorf("construct payload header: %v", err) - } - payloadData, err := proto.Marshal(&cb.ConfigEnvelope{Config: &cb.Config{ChannelGroup: cg}}) - if err != nil { - return nil, fmt.Errorf("marshaling payload data: %v", err) - } - payload := &cb.Payload{Header: payloadHeader, Data: payloadData} - envelopePayload, err := proto.Marshal(payload) - if err != nil { - return nil, fmt.Errorf("marshaling envelope payload: %v", err) - } - envelope := &cb.Envelope{Payload: envelopePayload, Signature: nil} - blockData, err := proto.Marshal(envelope) - if err != nil { - return nil, fmt.Errorf("marshaling envelope: %v", err) - } - - block := newBlock(0, nil) - block.Data = &cb.BlockData{Data: [][]byte{blockData}} - block.Header.DataHash = blockDataHash(block.Data) +// newChannelGroup defines the root of the channel configuration. +func newChannelGroup(channelConfig Channel) (*cb.ConfigGroup, error) { + channelGroup := newConfigGroup() - lastConfigValue, err := proto.Marshal(&cb.LastConfig{Index: 0}) - if err != nil { - return nil, fmt.Errorf("marshaling metadata last config value: %v", err) - } - lastConfigMetadata, err := proto.Marshal(&cb.Metadata{Value: lastConfigValue}) - if err != nil { - return nil, fmt.Errorf("marshaling metadata last config: %v", err) + if channelConfig.Consortium == "" { + return nil, errors.New("consortium is not defined in channel config") } - block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = lastConfigMetadata - signatureValue, err := proto.Marshal(&cb.OrdererBlockMetadata{ - LastConfig: &cb.LastConfig{Index: 0}, - }) + err := setValue(channelGroup, consortiumValue(channelConfig.Consortium), "") if err != nil { - return nil, fmt.Errorf("marshaling metadata signature value: %v", err) + return nil, err } - signatureMetadata, err := proto.Marshal(&cb.Metadata{Value: signatureValue}) + + channelGroup.Groups[ApplicationGroupKey], err = newApplicationGroupTemplate(channelConfig.Application) if err != nil { - return nil, fmt.Errorf("marshaling metadata signature: %v", err) + return nil, fmt.Errorf("failed to create application group: %v", err) } - block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = signatureMetadata - return block, nil + channelGroup.ModPolicy = AdminsPolicyKey + + return channelGroup, nil } // newChannelCreateConfigUpdate generates a ConfigUpdate which can be sent to the orderer to create a new channel. diff --git a/configtx/config_test.go b/configtx/config_test.go index 198b4ee..8e8e640 100644 --- a/configtx/config_test.go +++ b/configtx/config_test.go @@ -1116,7 +1116,7 @@ func TestNewSystemChannelGenesisBlockFailure(t *testing.T) { err: errors.New("creating system channel group: capabilities is not defined in channel config"), }, { - testName: "When creating the default config template without consortiums", + testName: "When creating the default config template without orderer", profileMod: func() Channel { profile, _, _ := baseSystemChannelProfile(t) profile.Orderer = Orderer{} @@ -1143,6 +1143,766 @@ func TestNewSystemChannelGenesisBlockFailure(t *testing.T) { } } +func TestNewApplicationChannelGenesisBlock(t *testing.T) { + t.Parallel() + + gt := NewGomegaWithT(t) + + profile, _, _ := baseApplicationChannelProfile(t) + + block, err := NewApplicationChannelGenesisBlock(profile, "testapplicationchannel") + gt.Expect(err).ToNot(HaveOccurred()) + gt.Expect(block).ToNot(BeNil()) + gt.Expect(block.Header.Number).To(Equal(uint64(0))) + + org1CertBase64, org1CrlBase64 := certCRLBase64(t, profile.Application.Organizations[0].MSP) + org2CertBase64, org2CrlBase64 := certCRLBase64(t, profile.Application.Organizations[1].MSP) + ordererOrgCertBase64, ordererOrgCrlBase64 := certCRLBase64(t, profile.Orderer.Organizations[0].MSP) + + expectBlockJSON := fmt.Sprintf(` +{ + "data": { + "data": [ + { + "payload": { + "data": { + "config": { + "channel_group": { + "groups": { + "Application": { + "groups": { + "Org1": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "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" + }, + "Readers": { + "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" + } + }, + "values": { + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [ + "%[1]s" + ], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA3" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "%[1]s", + "organizational_unit_identifier": "OUID" + }, + "client_ou_identifier": { + "certificate": "%[1]s", + "organizational_unit_identifier": "OUID" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "%[1]s", + "organizational_unit_identifier": "OUID" + }, + "peer_ou_identifier": { + "certificate": "%[1]s", + "organizational_unit_identifier": "OUID" + } + }, + "intermediate_certs": [ + "%[1]s" + ], + "name": "MSPID", + "organizational_unit_identifiers": [ + { + "certificate": "%[1]s", + "organizational_unit_identifier": "OUID" + } + ], + "revocation_list": [ + "%[2]s" + ], + "root_certs": [ + "%[1]s" + ], + "signing_identity": null, + "tls_intermediate_certs": [ + "%[1]s" + ], + "tls_root_certs": [ + "%[1]s" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + }, + "Org2": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "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" + }, + "Readers": { + "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" + } + }, + "values": { + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [ + "%[3]s" + ], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA3" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "%[3]s", + "organizational_unit_identifier": "OUID" + }, + "client_ou_identifier": { + "certificate": "%[3]s", + "organizational_unit_identifier": "OUID" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "%[3]s", + "organizational_unit_identifier": "OUID" + }, + "peer_ou_identifier": { + "certificate": "%[3]s", + "organizational_unit_identifier": "OUID" + } + }, + "intermediate_certs": [ + "%[3]s" + ], + "name": "MSPID", + "organizational_unit_identifiers": [ + { + "certificate": "%[3]s", + "organizational_unit_identifier": "OUID" + } + ], + "revocation_list": [ + "%[4]s" + ], + "root_certs": [ + "%[3]s" + ], + "signing_identity": null, + "tls_intermediate_certs": [ + "%[3]s" + ], + "tls_root_certs": [ + "%[3]s" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" + }, + "Readers": { + "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" + } + }, + "values": { + "ACLs": { + "mod_policy": "Admins", + "value": { + "acls": { + "acl1": { + "policy_ref": "hi" + } + } + }, + "version": "0" + }, + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V1_3": {} + } + }, + "version": "0" + } + }, + "version": "0" + }, + "Orderer": { + "groups": { + "OrdererOrg": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "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" + }, + "Readers": { + "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" + } + }, + "values": { + "Endpoints": { + "mod_policy": "Admins", + "value": { + "addresses": [ + "localhost:123" + ] + }, + "version": "0" + }, + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [ + "%[5]s" + ], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA3" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "%[5]s", + "organizational_unit_identifier": "OUID" + }, + "client_ou_identifier": { + "certificate": "%[5]s", + "organizational_unit_identifier": "OUID" + }, + "enable": false, + "orderer_ou_identifier": { + "certificate": "%[5]s", + "organizational_unit_identifier": "OUID" + }, + "peer_ou_identifier": { + "certificate": "%[5]s", + "organizational_unit_identifier": "OUID" + } + }, + "intermediate_certs": [ + "%[5]s" + ], + "name": "MSPID", + "organizational_unit_identifiers": [ + { + "certificate": "%[5]s", + "organizational_unit_identifier": "OUID" + } + ], + "revocation_list": [ + "%[6]s" + ], + "root_certs": [ + "%[5]s" + ], + "signing_identity": null, + "tls_intermediate_certs": [ + "%[5]s" + ], + "tls_root_certs": [ + "%[5]s" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" + }, + "BlockValidation": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + }, + "Readers": { + "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" + } + }, + "values": { + "BatchSize": { + "mod_policy": "Admins", + "value": { + "absolute_max_bytes": 100, + "max_message_count": 100, + "preferred_max_bytes": 100 + }, + "version": "0" + }, + "BatchTimeout": { + "mod_policy": "Admins", + "value": { + "timeout": "0s" + }, + "version": "0" + }, + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V1_3": {} + } + }, + "version": "0" + }, + "ChannelRestrictions": { + "mod_policy": "Admins", + "value": null, + "version": "0" + }, + "ConsensusType": { + "mod_policy": "Admins", + "value": { + "metadata": null, + "state": "STATE_NORMAL", + "type": "solo" + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" + }, + "Readers": { + "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" + } + }, + "values": { + "BlockDataHashingStructure": { + "mod_policy": "Admins", + "value": { + "width": 4294967295 + }, + "version": "0" + }, + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V2_0": {} + } + }, + "version": "0" + }, + "HashingAlgorithm": { + "mod_policy": "Admins", + "value": { + "name": "SHA256" + }, + "version": "0" + } + }, + "version": "0" + }, + "sequence": "0" + }, + "last_update": null + }, + "header": { + "channel_header": { + "channel_id": "testapplicationchannel", + "epoch": "0", + "extension": null, + "timestamp": "2020-06-25T17:39:55Z", + "tls_cert_hash": null, + "tx_id": "93fcf9cd1e2524021f6ea592801a8b15d5262d54b350c7fe8b6b760a062b7390", + "type": 1, + "version": 0 + }, + "signature_header": { + "creator": null, + "nonce": "yXFTP7Wz7bAtIMpzFB+WaLe45fYIXjl8" + } + } + }, + "signature": null + } + ] + }, + "header": { + "data_hash": "2FX2z5r8jRx6Jt5QKHt6Ch/eU0ay1bZPrncOL1Q7pIE=", + "number": "0", + "previous_hash": null + }, + "metadata": { + "metadata": [ + "CgIKAA==", + "", + "", + "", + "" + ] + } +} +`, org1CertBase64, org1CrlBase64, org2CertBase64, org2CrlBase64, ordererOrgCertBase64, ordererOrgCrlBase64) + + expectedBlock := &cb.Block{} + err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectBlockJSON), expectedBlock) + gt.Expect(err).ToNot(HaveOccurred()) + + expectedEnvelope := &cb.Envelope{} + err = proto.Unmarshal(expectedBlock.Data.Data[0], expectedEnvelope) + gt.Expect(err).NotTo(HaveOccurred()) + + expectedPayload := &cb.Payload{} + err = proto.Unmarshal(expectedEnvelope.Payload, expectedPayload) + gt.Expect(err).NotTo(HaveOccurred()) + + expectedData := &cb.ConfigEnvelope{} + err = proto.Unmarshal(expectedPayload.Data, expectedData) + gt.Expect(err).NotTo(HaveOccurred()) + + actualEnvelope := &cb.Envelope{} + err = proto.Unmarshal(block.Data.Data[0], actualEnvelope) + gt.Expect(err).NotTo(HaveOccurred()) + + actualPayload := &cb.Payload{} + err = proto.Unmarshal(actualEnvelope.Payload, actualPayload) + gt.Expect(err).NotTo(HaveOccurred()) + + actualData := &cb.ConfigEnvelope{} + err = proto.Unmarshal(actualPayload.Data, actualData) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Expect(actualData).To(Equal(expectedData)) + + expectedChannelHeader := &cb.ChannelHeader{} + err = proto.Unmarshal(expectedPayload.Header.ChannelHeader, expectedChannelHeader) + gt.Expect(err).NotTo(HaveOccurred()) + + actualChannelHeader := &cb.ChannelHeader{} + err = proto.Unmarshal(actualPayload.Header.ChannelHeader, actualChannelHeader) + gt.Expect(err).NotTo(HaveOccurred()) + expectedChannelHeader.Timestamp = actualChannelHeader.Timestamp + expectedChannelHeader.TxId = actualChannelHeader.TxId + + gt.Expect(actualChannelHeader).To(Equal(expectedChannelHeader)) +} + +func TestNewApplicationChannelGenesisBlockFailure(t *testing.T) { + t.Parallel() + + tests := []struct { + testName string + profileMod func() Channel + channelID string + err error + }{ + { + testName: "When channel ID is not specified in config", + profileMod: func() Channel { + profile, _, _ := baseApplicationChannelProfile(t) + return profile + }, + channelID: "", + err: errors.New("application channel ID is required"), + }, + { + testName: "When creating the default application config template with empty orderer endpoints", + profileMod: func() Channel { + profile, _, _ := baseApplicationChannelProfile(t) + profile.Orderer.Organizations[0].OrdererEndpoints = []string{} + return profile + }, + channelID: "testapplicationchannel", + err: errors.New("creating application channel group: orderer endpoints are not defined for org OrdererOrg"), + }, + { + testName: "When creating the default config template with empty capabilities", + profileMod: func() Channel { + profile, _, _ := baseApplicationChannelProfile(t) + profile.Capabilities = []string{} + return profile + }, + channelID: "testapplicationchannel", + err: errors.New("creating application channel group: capabilities is not defined in channel config"), + }, + { + testName: "When creating the default config template without application", + profileMod: func() Channel { + profile, _, _ := baseApplicationChannelProfile(t) + profile.Application = Application{} + return profile + }, + channelID: "testapplicationchannel", + err: errors.New("creating application channel group: no policies defined"), + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.testName, func(t *testing.T) { + t.Parallel() + + gt := NewGomegaWithT(t) + + profile := tt.profileMod() + + block, err := NewApplicationChannelGenesisBlock(profile, tt.channelID) + gt.Expect(block).To(BeNil()) + gt.Expect(err).To(MatchError(tt.err)) + }) + } +} + func TestNewEnvelopeFailures(t *testing.T) { t.Parallel() @@ -1382,6 +2142,17 @@ func baseSystemChannelProfile(t *testing.T) (Channel, []*ecdsa.PrivateKey, *ecds }, consortiumsPrivKey, ordererPrivKeys[0] } +func baseApplicationChannelProfile(t *testing.T) (Channel, []*ecdsa.PrivateKey, *ecdsa.PrivateKey) { + application, applicationPrivKey := baseApplication(t) + orderer, ordererPrivKeys := baseSoloOrderer(t) + return Channel{ + Application: application, + Orderer: orderer, + Capabilities: []string{"V2_0"}, + Policies: standardPolicies(), + }, applicationPrivKey, ordererPrivKeys[0] +} + func standardPolicies() map[string]Policy { return map[string]Policy{ ReadersPolicyKey: {