diff --git a/common/cauthdsl/cauthdsl.go b/common/cauthdsl/cauthdsl.go index 9439695b663..48836816a91 100644 --- a/common/cauthdsl/cauthdsl.go +++ b/common/cauthdsl/cauthdsl.go @@ -41,7 +41,7 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial } return func(signedData []*cb.SignedData, used []bool) bool { - cauthdslLogger.Debugf("Gate evaluation starts: (%s)", t) + cauthdslLogger.Debugf("Gate evaluation starts: (%v)", t) verified := int32(0) _used := make([]bool, len(used)) for _, policy := range policies { @@ -53,20 +53,20 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial } if verified >= t.NOutOf.N { - cauthdslLogger.Debugf("Gate evaluation succeeds: (%s)", t) + cauthdslLogger.Debugf("Gate evaluation succeeds: (%v)", t) } else { - cauthdslLogger.Debugf("Gate evaluation fails: (%s)", t) + cauthdslLogger.Debugf("Gate evaluation fails: (%v)", t) } return verified >= t.NOutOf.N }, nil case *cb.SignaturePolicy_SignedBy: if t.SignedBy < 0 || t.SignedBy >= int32(len(identities)) { - return nil, fmt.Errorf("Identity index out of range, requested %d, but identies length is %d", t.SignedBy, len(identities)) + return nil, fmt.Errorf("Identity index out of range, requested %v, but identies length is %d", t.SignedBy, len(identities)) } signedByID := identities[t.SignedBy] return func(signedData []*cb.SignedData, used []bool) bool { - cauthdslLogger.Debugf("Principal evaluation starts: (%s) (used %s)", t, used) + cauthdslLogger.Debugf("Principal evaluation starts: (%v) (used %v)", t, used) for i, sd := range signedData { if used[i] { continue @@ -78,15 +78,18 @@ func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal, deserial } err = identity.SatisfiesPrincipal(signedByID) if err == nil { - err := identity.Verify(sd.Data, sd.Signature) + cauthdslLogger.Debugf("Principal matched by identity: (%v) for %v", t, sd.Identity) + err = identity.Verify(sd.Data, sd.Signature) if err == nil { - cauthdslLogger.Debugf("Principal evaluation succeeds: (%s) (used %s)", t, used) + cauthdslLogger.Debugf("Principal evaluation succeeds: (%v) (used %v)", t, used) used[i] = true return true } + } else { + cauthdslLogger.Debugf("Identity (%v) does not satisfy principal: %s", sd.Identity, err) } } - cauthdslLogger.Debugf("Principal evaluation fails: (%s)", t, used) + cauthdslLogger.Debugf("Principal evaluation fails: (%v) %v", t, used) return false }, nil default: diff --git a/common/configtx/template.go b/common/configtx/template.go index e298e1b6b75..6dc681c6fc3 100644 --- a/common/configtx/template.go +++ b/common/configtx/template.go @@ -20,10 +20,11 @@ import ( "fmt" "github.com/hyperledger/fabric/common/config" + configmsp "github.com/hyperledger/fabric/common/config/msp" + "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/msp" cb "github.com/hyperledger/fabric/protos/common" - ab "github.com/hyperledger/fabric/protos/orderer" "github.com/hyperledger/fabric/protos/utils" "github.com/golang/protobuf/proto" @@ -195,28 +196,51 @@ type channelCreationTemplate struct { orgs []string } -// NewChainCreationTemplate takes a CreationPolicy and a Template to produce a +// NewChainCreationTemplate takes a consortium name and a Template to produce a // Template which outputs an appropriately constructed list of ConfigUpdateEnvelopes. -func NewChainCreationTemplate(creationPolicy string, template Template) Template { - result := cb.NewConfigGroup() - result.Groups[config.OrdererGroupKey] = cb.NewConfigGroup() - result.Groups[config.OrdererGroupKey].Values[CreationPolicyKey] = &cb.ConfigValue{ - Value: utils.MarshalOrPanic(&ab.CreationPolicy{ - Policy: creationPolicy, - }), +func NewChainCreationTemplate(consortiumName string, orgs []string) Template { + return &channelCreationTemplate{ + consortiumName: consortiumName, + orgs: orgs, + } +} + +func (cct *channelCreationTemplate) Envelope(channelID string) (*cb.ConfigUpdateEnvelope, error) { + rSet := config.TemplateConsortium(cct.consortiumName) + wSet := config.TemplateConsortium(cct.consortiumName) + + rSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup() + wSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup() + + for _, org := range cct.orgs { + rSet.Groups[config.ApplicationGroupKey].Groups[org] = cb.NewConfigGroup() + wSet.Groups[config.ApplicationGroupKey].Groups[org] = cb.NewConfigGroup() } - return NewCompositeTemplate(NewSimpleTemplate(result), template) + + wSet.Groups[config.ApplicationGroupKey].ModPolicy = configmsp.AdminsPolicyKey + wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.AdminsPolicyKey, cb.ImplicitMetaPolicy_MAJORITY) + wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.WritersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.WritersPolicyKey, cb.ImplicitMetaPolicy_ANY) + wSet.Groups[config.ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.ReadersPolicyKey, cb.ImplicitMetaPolicy_ANY) + wSet.Groups[config.ApplicationGroupKey].Version = 1 + + return &cb.ConfigUpdateEnvelope{ + ConfigUpdate: utils.MarshalOrPanic(&cb.ConfigUpdate{ + ChannelId: channelID, + ReadSet: rSet, + WriteSet: wSet, + }), + }, nil } // MakeChainCreationTransaction is a handy utility function for creating new chain transactions using the underlying Template framework -func MakeChainCreationTransaction(creationPolicy string, chainID string, signer msp.SigningIdentity, templates ...Template) (*cb.Envelope, error) { +func MakeChainCreationTransaction(channelID string, consortium string, signer msp.SigningIdentity, orgs ...string) (*cb.Envelope, error) { sSigner, err := signer.Serialize() if err != nil { return nil, fmt.Errorf("Serialization of identity failed, err %s", err) } - newChainTemplate := NewChainCreationTemplate(creationPolicy, NewCompositeTemplate(templates...)) - newConfigUpdateEnv, err := newChainTemplate.Envelope(chainID) + newChainTemplate := NewChainCreationTemplate(consortium, orgs) + newConfigUpdateEnv, err := newChainTemplate.Envelope(channelID) if err != nil { return nil, err } @@ -229,7 +253,7 @@ func MakeChainCreationTransaction(creationPolicy string, chainID string, signer return nil, err } - payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, chainID, epoch) + payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, channelID, epoch) payloadSignatureHeader := utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic()) utils.SetTxID(payloadChannelHeader, payloadSignatureHeader) payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader) diff --git a/common/configtx/template_test.go b/common/configtx/template_test.go index 3e60290a25c..4441890555d 100644 --- a/common/configtx/template_test.go +++ b/common/configtx/template_test.go @@ -20,11 +20,10 @@ import ( "fmt" "testing" - "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/config" cb "github.com/hyperledger/fabric/protos/common" - ab "github.com/hyperledger/fabric/protos/orderer" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" ) @@ -105,13 +104,9 @@ func TestModPolicySettingTemplate(t *testing.T) { } func TestNewChainTemplate(t *testing.T) { - simple := NewSimpleTemplate( - simpleGroup(0), - simpleGroup(1), - ) - - creationPolicy := "Test" - nct := NewChainCreationTemplate(creationPolicy, simple) + consortiumName := "Test" + orgs := []string{"org1", "org2", "org3"} + nct := NewChainCreationTemplate(consortiumName, orgs) newChainID := "foo" configEnv, err := nct.Envelope(newChainID) @@ -119,24 +114,22 @@ func TestNewChainTemplate(t *testing.T) { t.Fatalf("Error creation a chain creation config") } - configNext, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate) + configUpdate, err := UnmarshalConfigUpdate(configEnv.ConfigUpdate) if err != nil { t.Fatalf("Should not have errored: %s", err) } - assert.Equal(t, len(configNext.WriteSet.Values), 2, "Not the right number of config values") + consortiumProto := &cb.Consortium{} + err = proto.Unmarshal(configUpdate.WriteSet.Values[config.ConsortiumKey].Value, consortiumProto) + assert.NoError(t, err) + assert.Equal(t, consortiumName, consortiumProto.Name, "Should have set correct consortium name") - for i := 0; i < 2; i++ { - _, ok := configNext.WriteSet.Values[fmt.Sprintf("%d", i)] - assert.True(t, ok, "Expected to find %d but did not", i) - } + assert.Equal(t, configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version, uint64(1)) - configValue, ok := configNext.WriteSet.Groups[config.OrdererGroupKey].Values[CreationPolicyKey] - assert.True(t, ok, "Did not find creation policy") + assert.Len(t, configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups, len(orgs)) - creationPolicyMessage := new(ab.CreationPolicy) - if err := proto.Unmarshal(configValue.Value, creationPolicyMessage); err != nil { - t.Fatal("Should not have errored:", err) + for _, org := range orgs { + _, ok := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org] + assert.True(t, ok, "Expected to find %s but did not", org) } - assert.Equal(t, creationPolicy, creationPolicyMessage.Policy, "Policy names don't match") } diff --git a/common/configtx/tool/configtxgen/main.go b/common/configtx/tool/configtxgen/main.go index eee8f7c31cb..dcfa382491a 100644 --- a/common/configtx/tool/configtxgen/main.go +++ b/common/configtx/tool/configtxgen/main.go @@ -24,12 +24,15 @@ import ( "io/ioutil" "github.com/hyperledger/fabric/bccsp/factory" + "github.com/hyperledger/fabric/common/config" + mspconfig "github.com/hyperledger/fabric/common/config/msp" "github.com/hyperledger/fabric/common/configtx" genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" "github.com/hyperledger/fabric/common/configtx/tool/provisional" "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/msp" cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/golang/protobuf/proto" @@ -38,7 +41,8 @@ import ( var logger = flogging.MustGetLogger("common/configtx/tool") -func doOutputBlock(pgen provisional.Generator, channelID string, outputBlock string) error { +func doOutputBlock(config *genesisconfig.Profile, channelID string, outputBlock string) error { + pgen := provisional.New(config) logger.Info("Generating genesis block") genesisBlock := pgen.GenesisBlockForChannel(channelID) logger.Info("Writing genesis block") @@ -49,14 +53,22 @@ func doOutputBlock(pgen provisional.Generator, channelID string, outputBlock str return nil } -func doOutputChannelCreateTx(pgen provisional.Generator, channelID string, outputChannelCreateTx string) error { +func doOutputChannelCreateTx(conf *genesisconfig.Profile, channelID string, outputChannelCreateTx string) error { logger.Info("Generating new channel configtx") // TODO, use actual MSP eventually signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity() if err != nil { return fmt.Errorf("Error getting signing identity: %s", err) } - configtx, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, channelID, signer, pgen.ChannelTemplate()) + + // XXX we ignore the non-application org names here, once the tool supports configuration updates + // we should come up with a cleaner way to handle this, but leaving as is for the moment to not break + // backwards compatibility + var orgNames []string + for _, org := range conf.Application.Organizations { + orgNames = append(orgNames, org.Name) + } + configtx, err := configtx.MakeChainCreationTransaction(channelID, conf.Consortium, signer, orgNames...) if err != nil { return fmt.Errorf("Error generating configtx: %s", err) } @@ -68,6 +80,79 @@ func doOutputChannelCreateTx(pgen provisional.Generator, channelID string, outpu return nil } +func doOutputAnchorPeersUpdate(conf *genesisconfig.Profile, channelID string, outputAnchorPeersUpdate string, asOrg string) error { + logger.Info("Generating anchor peer update") + if asOrg == "" { + return fmt.Errorf("Must specify an organization to update the anchor peer for") + } + + var org *genesisconfig.Organization + for _, iorg := range conf.Application.Organizations { + if iorg.Name == asOrg { + org = iorg + } + } + + if org == nil { + return fmt.Errorf("No org matching: %s", asOrg) + } + + anchorPeers := make([]*pb.AnchorPeer, len(org.AnchorPeers)) + for i, anchorPeer := range org.AnchorPeers { + anchorPeers[i] = &pb.AnchorPeer{ + Host: anchorPeer.Host, + Port: int32(anchorPeer.Port), + } + } + + configGroup := config.TemplateAnchorPeers(org.Name, anchorPeers) + configUpdate := &cb.ConfigUpdate{ + ChannelId: channelID, + WriteSet: configGroup, + ReadSet: cb.NewConfigGroup(), + } + + // Add all the existing config to the readset + configUpdate.ReadSet.Groups[config.ApplicationGroupKey] = cb.NewConfigGroup() + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Version = 1 + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name] = cb.NewConfigGroup() + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Values[config.MSPKey] = &cb.ConfigValue{} + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.ReadersPolicyKey] = &cb.ConfigPolicy{} + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.WritersPolicyKey] = &cb.ConfigPolicy{} + configUpdate.ReadSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.AdminsPolicyKey] = &cb.ConfigPolicy{} + + // Add all the existing at the same versions to the writeset + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version = 1 + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Version = 1 + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Values[config.MSPKey] = &cb.ConfigValue{} + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.ReadersPolicyKey] = &cb.ConfigPolicy{} + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.WritersPolicyKey] = &cb.ConfigPolicy{} + configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups[org.Name].Policies[mspconfig.AdminsPolicyKey] = &cb.ConfigPolicy{} + + configUpdateEnvelope := &cb.ConfigUpdateEnvelope{ + ConfigUpdate: utils.MarshalOrPanic(configUpdate), + } + + update := &cb.Envelope{ + Payload: utils.MarshalOrPanic(&cb.Payload{ + Header: &cb.Header{ + ChannelHeader: utils.MarshalOrPanic(&cb.ChannelHeader{ + ChannelId: channelID, + Type: int32(cb.HeaderType_CONFIG_UPDATE), + }), + }, + Data: utils.MarshalOrPanic(configUpdateEnvelope), + }), + } + + logger.Info("Writing anchor peer update") + err := ioutil.WriteFile(outputAnchorPeersUpdate, utils.MarshalOrPanic(update), 0644) + if err != nil { + return fmt.Errorf("Error writing channel anchor peer update: %s", err) + } + return nil +} + func doInspectBlock(inspectBlock string) error { logger.Info("Inspecting block") data, err := ioutil.ReadFile(inspectBlock) @@ -168,25 +253,27 @@ func doInspectChannelCreateTx(inspectChannelCreateTx string) error { return fmt.Errorf("ConfigUpdateEnvelope was for different channel than envelope: %s vs %s", configUpdate.ChannelId, header.ChannelId) } - configResult, err := configtx.NewConfigResult(configUpdate.WriteSet, configtx.NewInitializer()) - if err != nil { - return fmt.Errorf("Error parsing configuration: %s", err) + if configUpdate.WriteSet == nil { + return fmt.Errorf("Empty WriteSet") } - buffer := &bytes.Buffer{} - err = json.Indent(buffer, []byte(configResult.JSON()), "", " ") - if err != nil { - return fmt.Errorf("Error in output JSON (usually a programming bug): %s", err) + if configUpdate.WriteSet.Groups[config.ApplicationGroupKey] == nil { + return fmt.Errorf("Empty Application group") } - fmt.Printf("Config for channel: %s\n", header.ChannelId) + var orgs []string + + for name := range configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups { + orgs = append(orgs, name) + } + + fmt.Printf("\nChannel creation for channel: %s with orgs %v\n\n", header.ChannelId, orgs) - fmt.Println(buffer.String()) return nil } func main() { - var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock, inspectChannelCreateTx string + var outputBlock, outputChannelCreateTx, profile, channelID, inspectBlock, inspectChannelCreateTx, outputAnchorPeersUpdate, asOrg string flag.StringVar(&outputBlock, "outputBlock", "", "The path to write the genesis block to (if set)") flag.StringVar(&channelID, "channelID", provisional.TestChainID, "The channel ID to use in the configtx") @@ -194,6 +281,8 @@ func main() { flag.StringVar(&profile, "profile", genesisconfig.SampleInsecureProfile, "The profile from configtx.yaml to use for generation.") flag.StringVar(&inspectBlock, "inspectBlock", "", "Prints the configuration contained in the block at the specified path") flag.StringVar(&inspectChannelCreateTx, "inspectChannelCreateTx", "", "Prints the configuration contained in the transaction at the specified path") + flag.StringVar(&outputAnchorPeersUpdate, "outputAnchorPeersUpdate", "", "Creates an config update to update an anchor peer (works only with the default channel creation, and only for the first update)") + flag.StringVar(&asOrg, "asOrg", "", "Performs the config generation as a particular organization, only including values in the write set that org (likely) has privilege to set") flag.Parse() @@ -202,16 +291,15 @@ func main() { logger.Info("Loading configuration") factory.InitFactories(nil) config := genesisconfig.Load(profile) - pgen := provisional.New(config) if outputBlock != "" { - if err := doOutputBlock(pgen, channelID, outputBlock); err != nil { + if err := doOutputBlock(config, channelID, outputBlock); err != nil { logger.Fatalf("Error on outputBlock: %s", err) } } if outputChannelCreateTx != "" { - if err := doOutputChannelCreateTx(pgen, channelID, outputChannelCreateTx); err != nil { + if err := doOutputChannelCreateTx(config, channelID, outputChannelCreateTx); err != nil { logger.Fatalf("Error on outputChannelCreateTx: %s", err) } } @@ -228,4 +316,9 @@ func main() { } } + if outputAnchorPeersUpdate != "" { + if err := doOutputAnchorPeersUpdate(config, channelID, outputAnchorPeersUpdate, asOrg); err != nil { + logger.Fatalf("Error on inspectChannelCreateTx: %s", err) + } + } } diff --git a/common/configtx/tool/configtxgen/main_test.go b/common/configtx/tool/configtxgen/main_test.go index b5aefc0487a..5c69b30e7fb 100644 --- a/common/configtx/tool/configtxgen/main_test.go +++ b/common/configtx/tool/configtxgen/main_test.go @@ -23,7 +23,6 @@ import ( "github.com/hyperledger/fabric/bccsp/factory" genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" - "github.com/hyperledger/fabric/common/configtx/tool/provisional" "github.com/stretchr/testify/assert" ) @@ -47,9 +46,8 @@ func TestInspectBlock(t *testing.T) { factory.InitFactories(nil) config := genesisconfig.Load(genesisconfig.SampleInsecureProfile) - pgen := provisional.New(config) - assert.NoError(t, doOutputBlock(pgen, "foo", blockDest), "Good block generation request") + assert.NoError(t, doOutputBlock(config, "foo", blockDest), "Good block generation request") assert.NoError(t, doInspectBlock(blockDest), "Good block inspection request") } @@ -58,8 +56,16 @@ func TestInspectConfigTx(t *testing.T) { factory.InitFactories(nil) config := genesisconfig.Load(genesisconfig.SampleInsecureProfile) - pgen := provisional.New(config) - assert.NoError(t, doOutputChannelCreateTx(pgen, "foo", configTxDest), "Good outputChannelCreateTx generation request") + assert.NoError(t, doOutputChannelCreateTx(config, "foo", configTxDest), "Good outputChannelCreateTx generation request") assert.NoError(t, doInspectChannelCreateTx(configTxDest), "Good configtx inspection request") } + +func TestGenerateAnchorPeersUpdate(t *testing.T) { + configTxDest := tmpDir + string(os.PathSeparator) + "anchorPeerUpdate" + + factory.InitFactories(nil) + config := genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile) + + assert.NoError(t, doOutputAnchorPeersUpdate(config, "foo", configTxDest, genesisconfig.SampleOrgName), "Good anchorPeerUpdate request") +} diff --git a/common/configtx/tool/localconfig/config.go b/common/configtx/tool/localconfig/config.go index eee2f65fc28..a6114963d04 100644 --- a/common/configtx/tool/localconfig/config.go +++ b/common/configtx/tool/localconfig/config.go @@ -59,8 +59,11 @@ const ( SampleInsecureProfile = "SampleInsecureSolo" // SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering. SampleSingleMSPSoloProfile = "SampleSingleMSPSolo" + // SampleConsortiumName is the sample consortium from the sample configtx.yaml SampleConsortiumName = "SampleConsortium" + // SampleOrgName is the name of the sample org in the sample profiles + SampleOrgName = "SampleOrg" // AdminRoleAdminPrincipal is set as AdminRole to cause the MSP role of type Admin to be used as the admin principal default AdminRoleAdminPrincipal = "Role.ADMIN" @@ -174,18 +177,19 @@ func Load(profile string) *Profile { err := config.ReadInConfig() if err != nil { - logger.Panicf("Error reading configuration: %s", err) + logger.Panic("Error reading configuration:", err) } + logger.Debugf("Using config file: %s", config.ConfigFileUsed()) var uconf TopLevel err = viperutil.EnhancedExactUnmarshal(config, &uconf) if err != nil { - logger.Panicf("Error unmarshaling config into struct: %s", err) + logger.Panic("Error unmarshaling config into struct:", err) } result, ok := uconf.Profiles[profile] if !ok { - logger.Panicf("Could not find profile %s", profile) + logger.Panic("Could not find profile", profile) } result.completeInitialization(filepath.Dir(config.ConfigFileUsed())) @@ -194,36 +198,12 @@ func Load(profile string) *Profile { } func (p *Profile) completeInitialization(configDir string) { - p.initDefaults() - - // Fix up any relative paths - if p.Orderer != nil { - for _, org := range p.Orderer.Organizations { - translatePaths(configDir, org) - } - } - - if p.Application != nil { - for _, org := range p.Application.Organizations { - translatePaths(configDir, org) - } - } - - if p.Consortiums != nil { - for _, consortium := range p.Consortiums { - for _, org := range consortium.Organizations { - translatePaths(configDir, org) - } - } - } -} - -func (p *Profile) initDefaults() { if p.Orderer != nil { for _, org := range p.Orderer.Organizations { if org.AdminPrincipal == "" { org.AdminPrincipal = AdminRoleAdminPrincipal } + translatePaths(configDir, org) } } @@ -232,6 +212,7 @@ func (p *Profile) initDefaults() { if org.AdminPrincipal == "" { org.AdminPrincipal = AdminRoleAdminPrincipal } + translatePaths(configDir, org) } } @@ -241,6 +222,7 @@ func (p *Profile) initDefaults() { if org.AdminPrincipal == "" { org.AdminPrincipal = AdminRoleAdminPrincipal } + translatePaths(configDir, org) } } } diff --git a/core/common/validation/config_test.go b/core/common/validation/config_test.go index 37eb1fcee8f..0264a48809d 100644 --- a/core/common/validation/config_test.go +++ b/core/common/validation/config_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/hyperledger/fabric/common/configtx" - configtxtest "github.com/hyperledger/fabric/common/configtx/test" + genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" "github.com/hyperledger/fabric/common/util" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/peer" @@ -29,7 +29,7 @@ import ( func TestValidateConfigTx(t *testing.T) { chainID := util.GetTestChainID() - chCrtEnv, err := configtx.MakeChainCreationTransaction(configtxtest.AcceptAllPolicyKey, chainID, signer, configtxtest.CompositeTemplate()) + chCrtEnv, err := configtx.MakeChainCreationTransaction(genesisconfig.SampleConsortiumName, chainID, signer) if err != nil { t.Fatalf("MakeChainCreationTransaction failed, err %s", err) return diff --git a/examples/e2e_cli/configtx.yaml b/examples/e2e_cli/configtx.yaml index de06a51aadd..3403f9de81b 100644 --- a/examples/e2e_cli/configtx.yaml +++ b/examples/e2e_cli/configtx.yaml @@ -9,11 +9,18 @@ ################################################################################ Profiles: - TwoOrgs: + TwoOrgsOrdererGenesis: Orderer: <<: *OrdererDefaults Organizations: - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org0 + - *Org1 + TwoOrgsChannel: + Consortium: SampleConsortium Application: <<: *ApplicationDefaults Organizations: @@ -43,6 +50,11 @@ Organizations: # MSPDir is the filesystem path which contains the MSP configuration MSPDir: crypto/orderer/localMspConfig + # AdminPrincipal dictates the type of principal used for an organization's Admins policy + # Today, only the values of Role.ADMIN ad Role.MEMBER are accepted, which indicates a principal + # of role type ADMIN and role type MEMBER respectively + AdminPrincipal: Role.MEMBER + # BCCSP (Blockchain crypto provider): Select which crypto implementation or # library to use BCCSP: @@ -65,6 +77,11 @@ Organizations: MSPDir: crypto/peer/peer0/localMspConfig + # AdminPrincipal dictates the type of principal used for an organization's Admins policy + # Today, only the values of Role.ADMIN ad Role.MEMBER are accepted, which indicates a principal + # of role type ADMIN and role type MEMBER respectively + AdminPrincipal: Role.MEMBER + # BCCSP (Blockchain crypto provider): Select which crypto implementation or # library to use BCCSP: @@ -94,6 +111,11 @@ Organizations: MSPDir: crypto/peer/peer2/localMspConfig + # AdminPrincipal dictates the type of principal used for an organization's Admins policy + # Today, only the values of Role.ADMIN ad Role.MEMBER are accepted, which indicates a principal + # of role type ADMIN and role type MEMBER respectively + AdminPrincipal: Role.MEMBER + # BCCSP (Blockchain crypto provider): Select which crypto implementation or # library to use BCCSP: diff --git a/examples/e2e_cli/generateCfgTrx.sh b/examples/e2e_cli/generateCfgTrx.sh index 98973d74bfa..0a6b33452d5 100755 --- a/examples/e2e_cli/generateCfgTrx.sh +++ b/examples/e2e_cli/generateCfgTrx.sh @@ -25,7 +25,13 @@ else fi echo "Generating genesis block" -$CONFIGTXGEN -profile TwoOrgs -outputBlock crypto/orderer/orderer.block +$CONFIGTXGEN -profile TwoOrgsOrdererGenesis -outputBlock crypto/orderer/orderer.block echo "Generating channel configuration transaction" -$CONFIGTXGEN -profile TwoOrgs -outputCreateChannelTx crypto/orderer/channel.tx -channelID $CHANNEL_NAME +$CONFIGTXGEN -profile TwoOrgsChannel -outputCreateChannelTx crypto/orderer/channel.tx -channelID $CHANNEL_NAME + +echo "Generating anchor peer update for Org0MSP" +$CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate crypto/orderer/Org0MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org0MSP + +echo "Generating anchor peer update for Org1MSP" +$CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate crypto/orderer/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP diff --git a/examples/e2e_cli/scripts/script.sh b/examples/e2e_cli/scripts/script.sh index 6591709e8c4..13e966952ee 100755 --- a/examples/e2e_cli/scripts/script.sh +++ b/examples/e2e_cli/scripts/script.sh @@ -34,8 +34,8 @@ setGlobals () { } createChannel() { - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/orderer/localMspConfig - CORE_PEER_LOCALMSPID="OrdererMSP" + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peer/peer0/localMspConfig + CORE_PEER_LOCALMSPID="Org0MSP" if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then peer channel create -o orderer0:7050 -c $CHANNEL_NAME -f crypto/orderer/channel.tx >&log.txt @@ -49,6 +49,22 @@ createChannel() { echo } +updateAnchorPeers() { + PEER=$1 + setGlobals $PEER + + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer channel create -o orderer0:7050 -c $CHANNEL_NAME -f crypto/orderer/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt + else + peer channel create -o orderer0:7050 -c $CHANNEL_NAME -f crypto/orderer/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt + fi + res=$? + cat log.txt + verifyResult $res "Anchor peer update failed" + echo "===================== Anchor peers for org \"$CORE_PEER_LOCALMSPID}\" on \"$CHANNEL_NAME\" is updated successfully ===================== " + echo +} + ## Sometimes Join takes time hence RETRY atleast for 5 times joinWithRetry () { peer channel join -b $CHANNEL_NAME.block >&log.txt @@ -149,6 +165,9 @@ createChannel ## Join all the peers to the channel joinChannel +## Set the anchor peers for each org in the channel +updateAnchorPeers 0 +updateAnchorPeers 2 ## Install chaincode on Peer0/Org0 and Peer2/Org1 installChaincode 0 diff --git a/msp/mspimpl.go b/msp/mspimpl.go index f245e7081f8..4d6f7ed3c28 100644 --- a/msp/mspimpl.go +++ b/msp/mspimpl.go @@ -604,8 +604,10 @@ func (msp *bccspmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal) case m.MSPRole_MEMBER: // in the case of member, we simply check // whether this identity is valid for the MSP + mspLogger.Debugf("Checking if identity satisfies MEMBER role for %s", msp.name) return msp.Validate(id) case m.MSPRole_ADMIN: + mspLogger.Debugf("Checking if identity satisfies ADMIN role for %s", msp.name) // in the case of admin, we check that the // id is exactly one of our admins for _, admincert := range msp.admins { diff --git a/orderer/configupdate/configupdate.go b/orderer/configupdate/configupdate.go index bb3e14d4340..fec43b1a84e 100644 --- a/orderer/configupdate/configupdate.go +++ b/orderer/configupdate/configupdate.go @@ -23,7 +23,7 @@ package configupdate import ( "fmt" - "github.com/hyperledger/fabric/common/configtx" + configtxapi "github.com/hyperledger/fabric/common/configtx/api" "github.com/hyperledger/fabric/common/crypto" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" @@ -43,6 +43,10 @@ const ( type SupportManager interface { // GetChain gets the chain support for a given ChannelId GetChain(chainID string) (Support, bool) + + // NewChannelConfig returns a bare bones configuration ready for channel + // creation request to be applied on top of it + NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) } // Support enumerates a subset of the full channel support function which is required for this package @@ -52,16 +56,23 @@ type Support interface { } type Processor struct { - signer crypto.LocalSigner - manager SupportManager - systemChannelID string + signer crypto.LocalSigner + manager SupportManager + systemChannelID string + systemChannelSupport Support } func New(systemChannelID string, supportManager SupportManager, signer crypto.LocalSigner) *Processor { + support, ok := supportManager.GetChain(systemChannelID) + if !ok { + logger.Panicf("Supplied a SupportManager which did not contain a system channel") + } + return &Processor{ - systemChannelID: systemChannelID, - manager: supportManager, - signer: signer, + systemChannelID: systemChannelID, + manager: supportManager, + signer: signer, + systemChannelSupport: support, } } @@ -115,44 +126,25 @@ func (p *Processor) existingChannelConfig(envConfigUpdate *cb.Envelope, channelI return utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, configEnvelope, msgVersion, epoch) } -func createInitialConfig(envConfigUpdate *cb.Envelope) (*cb.ConfigEnvelope, error) { - // TODO, for now, this assumes the update contains the entire initial config - // in the future, a subset of config needs to be allowed - - configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload) - if err != nil { - return nil, fmt.Errorf("Failing initial channel config creation because of payload unmarshaling error: %s", err) - } - - configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data) - if err != nil { - return nil, fmt.Errorf("Failing initial channel config creation because of config update envelope unmarshaling error: %s", err) - } +func (p *Processor) proposeNewChannelToSystemChannel(newChannelEnvConfig *cb.Envelope) (*cb.Envelope, error) { + return utils.CreateSignedEnvelope(cb.HeaderType_ORDERER_TRANSACTION, p.systemChannelID, p.signer, newChannelEnvConfig, msgVersion, epoch) +} - configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) +func (p *Processor) newChannelConfig(channelID string, envConfigUpdate *cb.Envelope) (*cb.Envelope, error) { + ctxm, err := p.manager.NewChannelConfig(envConfigUpdate) if err != nil { - return nil, fmt.Errorf("Failing initial channel config creation because of config update unmarshaling error: %s", err) + return nil, err } - return &cb.ConfigEnvelope{ - Config: &cb.Config{ - ChannelGroup: configUpdate.WriteSet, - }, - - LastUpdate: envConfigUpdate, - }, nil -} - -func (p *Processor) newChannelConfig(channelID string, envConfigUpdate *cb.Envelope) (*cb.Envelope, error) { - initialConfig, err := createInitialConfig(envConfigUpdate) + newChannelConfigEnv, err := ctxm.ProposeConfigUpdate(envConfigUpdate) if err != nil { return nil, err } - envConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, initialConfig, msgVersion, epoch) + newChannelEnvConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, newChannelConfigEnv, msgVersion, epoch) if err != nil { return nil, err } - return utils.CreateSignedEnvelope(cb.HeaderType_ORDERER_TRANSACTION, p.systemChannelID, p.signer, envConfig, msgVersion, epoch) + return p.proposeNewChannelToSystemChannel(newChannelEnvConfig) } diff --git a/orderer/configupdate/configupdate_test.go b/orderer/configupdate/configupdate_test.go index e0112fc1dab..7f49f307a75 100644 --- a/orderer/configupdate/configupdate_test.go +++ b/orderer/configupdate/configupdate_test.go @@ -21,6 +21,8 @@ import ( "testing" "github.com/hyperledger/fabric/common/configtx" + configtxapi "github.com/hyperledger/fabric/common/configtx/api" + mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx" mockcrypto "github.com/hyperledger/fabric/common/mocks/crypto" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" @@ -53,6 +55,14 @@ func (msm *mockSupportManager) GetChain(chainID string) (Support, bool) { return msm.GetChainVal, msm.GetChainVal != nil } +func (msm *mockSupportManager) NewChannelConfig(env *cb.Envelope) (configtxapi.Manager, error) { + return &mockconfigtx.Manager{ + ProposeConfigUpdateVal: &cb.ConfigEnvelope{ + LastUpdate: env, + }, + }, nil +} + func TestChannelID(t *testing.T) { makeEnvelope := func(payload *cb.Payload) *cb.Envelope { return &cb.Envelope{ @@ -96,6 +106,7 @@ const testUpdateChannelID = "update_channel" func newTestInstance() (*mockSupportManager, *Processor) { msm := &mockSupportManager{} + msm.GetChainVal = &mockSupport{} return msm, New(systemChannelID, msm, mockcrypto.FakeLocalSigner) } @@ -138,7 +149,8 @@ func TestExistingChannel(t *testing.T) { } func TestNewChannel(t *testing.T) { - _, p := newTestInstance() + msm, p := newTestInstance() + msm.GetChainVal = nil testUpdate := testConfigUpdate() diff --git a/orderer/localconfig/config.go b/orderer/localconfig/config.go index 9b096a29a45..3fc9ad9e4a7 100644 --- a/orderer/localconfig/config.go +++ b/orderer/localconfig/config.go @@ -35,7 +35,7 @@ import ( ) const ( - pkgLogID = "orderer/config" + pkgLogID = "orderer/localconfig" // Prefix identifies the prefix for the orderer-related ENV vars. Prefix = "ORDERER" @@ -44,8 +44,7 @@ const ( var ( logger *logging.Logger - configName string - configFileName string + configName string ) func init() { @@ -53,7 +52,20 @@ func init() { flogging.SetModuleLevel(pkgLogID, "error") configName = strings.ToLower(Prefix) - configFileName = configName + ".yaml" +} + +// TopLevel directly corresponds to the orderer config YAML. +// Note, for non 1-1 mappings, you may append +// something like `mapstructure:"weirdFoRMat"` to +// modify the default mapping, see the "Unmarshal" +// section of https://github.com/spf13/viper for more info +type TopLevel struct { + General General + FileLedger FileLedger + RAMLedger RAMLedger + Kafka Kafka + Genesis Genesis + SbftLocal SbftLocal } // General contains config which should be common among all orderer types. @@ -82,33 +94,23 @@ type TLS struct { ClientRootCAs []string } -// Genesis is a deprecated structure which was used to put -// values into the genesis block, but this is now handled elsewhere. -// SBFT did not reference these values via the genesis block however -// so it is being left here for backwards compatibility purposes. -type Genesis struct { - DeprecatedBatchTimeout time.Duration - DeprecatedBatchSize uint32 - SbftShared SbftShared -} - // Profile contains configuration for Go pprof profiling. type Profile struct { Enabled bool Address string } -// RAMLedger contains configuration for the RAM ledger. -type RAMLedger struct { - HistorySize uint -} - // FileLedger contains configuration for the file-based ledger. type FileLedger struct { Location string Prefix string } +// RAMLedger contains configuration for the RAM ledger. +type RAMLedger struct { + HistorySize uint +} + // Kafka contains configuration for the Kafka-based orderer. type Kafka struct { Retry Retry @@ -117,6 +119,22 @@ type Kafka struct { TLS TLS } +// Retry contains config for the reconnection attempts to the Kafka brokers. +type Retry struct { + Period time.Duration + Stop time.Duration +} + +// Genesis is a deprecated structure which was used to put +// values into the genesis block, but this is now handled elsewhere. +// SBFT did not reference these values via the genesis block however +// so it is being left here for backwards compatibility purposes. +type Genesis struct { + DeprecatedBatchTimeout time.Duration + DeprecatedBatchSize uint32 + SbftShared SbftShared +} + // SbftLocal contains configuration for the SBFT peer/replica. type SbftLocal struct { PeerCommAddr string @@ -133,26 +151,6 @@ type SbftShared struct { Peers map[string]string // Address to Cert mapping } -// Retry contains config for the reconnection attempts to the Kafka brokers. -type Retry struct { - Period time.Duration - Stop time.Duration -} - -// TopLevel directly corresponds to the orderer config YAML. -// Note, for non 1-1 mappings, you may append -// something like `mapstructure:"weirdFoRMat"` to -// modify the default mapping, see the "Unmarshal" -// section of https://github.com/spf13/viper for more info -type TopLevel struct { - General General - RAMLedger RAMLedger - FileLedger FileLedger - Kafka Kafka - Genesis Genesis - SbftLocal SbftLocal -} - var defaults = TopLevel{ General: General{ LedgerType: "ram", @@ -204,7 +202,44 @@ var defaults = TopLevel{ }, } -func (c *TopLevel) initDefaults() { +// Load parses the orderer.yaml file and environment, producing a struct suitable for config use +func Load() *TopLevel { + config := viper.New() + cf.InitViper(config, configName) + + // for environment variables + config.SetEnvPrefix(Prefix) + config.AutomaticEnv() + replacer := strings.NewReplacer(".", "_") + config.SetEnvKeyReplacer(replacer) + + err := config.ReadInConfig() + if err != nil { + logger.Panic("Error reading configuration:", err) + } + + var uconf TopLevel + err = viperutil.EnhancedExactUnmarshal(config, &uconf) + if err != nil { + logger.Panic("Error unmarshaling config into struct:", err) + } + + uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed())) + + return &uconf +} + +func (c *TopLevel) completeInitialization(configDir string) { + defer func() { + // Translate any paths + c.General.TLS.RootCAs = translateCAs(configDir, c.General.TLS.RootCAs) + c.General.TLS.ClientRootCAs = translateCAs(configDir, c.General.TLS.ClientRootCAs) + cf.TranslatePathInPlace(configDir, &c.General.TLS.PrivateKey) + cf.TranslatePathInPlace(configDir, &c.General.TLS.Certificate) + cf.TranslatePathInPlace(configDir, &c.General.GenesisFile) + cf.TranslatePathInPlace(configDir, &c.General.LocalMSPDir) + }() + for { switch { case c.General.LedgerType == "": @@ -259,55 +294,10 @@ func (c *TopLevel) initDefaults() { } func translateCAs(configDir string, certificateAuthorities []string) []string { - results := make([]string, 0) - for _, ca := range certificateAuthorities { result := cf.TranslatePath(configDir, ca) results = append(results, result) } - return results } - -func (c *TopLevel) completeInitialization(configDir string) { - defer logger.Infof("Validated configuration to: %+v", c) - c.initDefaults() - - // Translate any paths - c.General.TLS.RootCAs = translateCAs(configDir, c.General.TLS.RootCAs) - c.General.TLS.ClientRootCAs = translateCAs(configDir, c.General.TLS.ClientRootCAs) - cf.TranslatePathInPlace(configDir, &c.General.TLS.PrivateKey) - cf.TranslatePathInPlace(configDir, &c.General.TLS.Certificate) - - cf.TranslatePathInPlace(configDir, &c.General.GenesisFile) - cf.TranslatePathInPlace(configDir, &c.General.LocalMSPDir) -} - -// Load parses the orderer.yaml file and environment, producing a struct suitable for config use -func Load() *TopLevel { - config := viper.New() - - cf.InitViper(config, configName) - - // for environment variables - config.SetEnvPrefix(Prefix) - config.AutomaticEnv() - replacer := strings.NewReplacer(".", "_") - config.SetEnvKeyReplacer(replacer) - - err := config.ReadInConfig() - if err != nil { - logger.Panicf("Error reading configuration %s: %s", configFileName, err) - } - - var uconf TopLevel - err = viperutil.EnhancedExactUnmarshal(config, &uconf) - if err != nil { - logger.Panicf("Error unmarshaling config into struct: %s", err) - } - - uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed())) - - return &uconf -} diff --git a/orderer/multichain/manager.go b/orderer/multichain/manager.go index 609e85a1d17..7441b55b07b 100644 --- a/orderer/multichain/manager.go +++ b/orderer/multichain/manager.go @@ -17,6 +17,8 @@ limitations under the License. package multichain import ( + "fmt" + "github.com/hyperledger/fabric/common/config" "github.com/hyperledger/fabric/common/configtx" configtxapi "github.com/hyperledger/fabric/common/configtx/api" @@ -25,11 +27,17 @@ import ( "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/crypto" ) var logger = logging.MustGetLogger("orderer/multichain") +const ( + msgVersion = int32(0) + epoch = 0 +) + // Manager coordinates the creation and access of chains type Manager interface { // GetChain retrieves the chain support for a chain (and whether it exists) @@ -37,6 +45,10 @@ type Manager interface { // SystemChannelID returns the channel ID for the system channel SystemChannelID() string + + // NewChannelConfig returns a bare bones configuration ready for channel + // creation request to be applied on top of it + NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) } type configResources struct { @@ -58,6 +70,7 @@ type multiLedger struct { ledgerFactory ledger.Factory signer crypto.LocalSigner systemChannelID string + systemChannel *chainSupport } func getConfigTx(reader ledger.Reader) *cb.Envelope { @@ -107,6 +120,7 @@ func NewManagerImpl(ledgerFactory ledger.Factory, consenters map[string]Consente logger.Infof("Starting with system channel %s and orderer type %s", chainID, chain.SharedConfig().ConsensusType()) ml.chains[string(chainID)] = chain ml.systemChannelID = chainID + ml.systemChannel = chain // We delay starting this chain, as it might try to copy and replace the chains map via newChain before the map is fully built defer chain.start() } else { @@ -182,3 +196,105 @@ func (ml *multiLedger) newChain(configtx *cb.Envelope) { func (ml *multiLedger) channelsCount() int { return len(ml.chains) } + +func (ml *multiLedger) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) { + configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload) + if err != nil { + return nil, fmt.Errorf("Failing initial channel config creation because of payload unmarshaling error: %s", err) + } + + configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data) + if err != nil { + return nil, fmt.Errorf("Failing initial channel config creation because of config update envelope unmarshaling error: %s", err) + } + + configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) + if err != nil { + return nil, fmt.Errorf("Failing initial channel config creation because of config update unmarshaling error: %s", err) + } + + if configUpdate.WriteSet == nil { + return nil, fmt.Errorf("Config update has an empty writeset") + } + + if configUpdate.WriteSet.Groups == nil || configUpdate.WriteSet.Groups[config.ApplicationGroupKey] == nil { + return nil, fmt.Errorf("Config update has missing application group") + } + + if uv := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version; uv != 1 { + return nil, fmt.Errorf("Config update for channel creation does not set application group version to 1, was %d", uv) + } + + consortiumConfigValue, ok := configUpdate.WriteSet.Values[config.ConsortiumKey] + if !ok { + return nil, fmt.Errorf("Consortium config value missing") + } + + consortium := &cb.Consortium{} + err = proto.Unmarshal(consortiumConfigValue.Value, consortium) + if err != nil { + return nil, fmt.Errorf("Error reading unmarshaling consortium name: %s", err) + } + + applicationGroup := cb.NewConfigGroup() + consortiumsConfig := ml.systemChannel.ConsortiumsConfig() + if consortiumsConfig == nil { + return nil, fmt.Errorf("The ordering system channel does not appear to support creating channels") + } + + consortiumConf, ok := consortiumsConfig.Consortiums()[consortium.Name] + if !ok { + return nil, fmt.Errorf("Unknown consortium name: %s", consortium.Name) + } + + applicationGroup.Policies[config.ChannelCreationPolicyKey] = &cb.ConfigPolicy{ + Policy: consortiumConf.ChannelCreationPolicy(), + } + applicationGroup.ModPolicy = config.ChannelCreationPolicyKey + + // Get the current system channel config + systemChannelGroup := ml.systemChannel.ConfigEnvelope().Config.ChannelGroup + + // If the consortium group has no members, allow the source request to have no members. However, + // if the consortium group has any members, there must be at least one member in the source request + if len(systemChannelGroup.Groups[config.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 && + len(configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups) == 0 { + return nil, fmt.Errorf("Proposed configuration has no application group members, but consortium contains members") + } + + for orgName := range configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups { + consortiumGroup, ok := systemChannelGroup.Groups[config.ConsortiumsGroupKey].Groups[consortium.Name].Groups[orgName] + if !ok { + return nil, fmt.Errorf("Attempted to include a member which is not in the consortium") + } + applicationGroup.Groups[orgName] = consortiumGroup + } + + channelGroup := cb.NewConfigGroup() + + // Copy the system channel Channel level config to the new config + for key, value := range systemChannelGroup.Values { + channelGroup.Values[key] = value + if key == config.ConsortiumKey { + // Do not set the consortium name, we do this later + continue + } + } + + for key, policy := range systemChannelGroup.Policies { + channelGroup.Policies[key] = policy + } + + // Set the new config orderer group to the system channel orderer group and the application group to the new application group + channelGroup.Groups[config.OrdererGroupKey] = systemChannelGroup.Groups[config.OrdererGroupKey] + channelGroup.Groups[config.ApplicationGroupKey] = applicationGroup + channelGroup.Values[config.ConsortiumKey] = config.TemplateConsortium(consortium.Name).Values[config.ConsortiumKey] + + templateConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, configUpdate.ChannelId, ml.signer, &cb.ConfigEnvelope{ + Config: &cb.Config{ + ChannelGroup: channelGroup, + }, + }, msgVersion, epoch) + + return configtx.NewManagerImpl(templateConfig, configtx.NewInitializer(), nil) +} diff --git a/orderer/multichain/manager_test.go b/orderer/multichain/manager_test.go index 179518a931e..421ac7b4b02 100644 --- a/orderer/multichain/manager_test.go +++ b/orderer/multichain/manager_test.go @@ -25,6 +25,7 @@ import ( genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" "github.com/hyperledger/fabric/common/configtx/tool/provisional" mockcrypto "github.com/hyperledger/fabric/common/mocks/crypto" + "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/orderer/ledger" ramledger "github.com/hyperledger/fabric/orderer/ledger/ram" cb "github.com/hyperledger/fabric/protos/common" @@ -39,11 +40,13 @@ import ( var conf *genesisconfig.Profile var genesisBlock = cb.NewBlock(0, nil) // *cb.Block +var mockSigningIdentity msp.SigningIdentity func init() { conf = genesisconfig.Load(genesisconfig.SampleInsecureProfile) logging.SetLevel(logging.DEBUG, "") genesisBlock = provisional.New(conf).GenesisBlock() + mockSigningIdentity, _ = msp.NewNoopMsp().GetDefaultSigningIdentity() } func mockCrypto() *mockCryptoHelper { @@ -240,11 +243,18 @@ func TestNewChain(t *testing.T) { newChainID := "TestNewChain" - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, provisional.New(conf).ChannelTemplate()).Envelope(newChainID) - if err != nil { - t.Fatalf("Error constructing configtx") - } - ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv) + envConfigUpdate, err := configtx.MakeChainCreationTransaction(newChainID, genesisconfig.SampleConsortiumName, mockSigningIdentity) + assert.NoError(t, err, "Constructing chain creation tx") + + cm, err := manager.NewChannelConfig(envConfigUpdate) + assert.NoError(t, err, "Constructing initial channel config") + + configEnv, err := cm.ProposeConfigUpdate(envConfigUpdate) + assert.NoError(t, err, "Proposing initial update") + + ingressTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch) + assert.NoError(t, err, "Creating ingresstx") + wrapped := wrapConfigTx(ingressTx) chainSupport, ok := manager.GetChain(manager.SystemChannelID()) diff --git a/orderer/multichain/systemchain.go b/orderer/multichain/systemchain.go index 2bdb7710f98..f08d6cdd08e 100644 --- a/orderer/multichain/systemchain.go +++ b/orderer/multichain/systemchain.go @@ -18,14 +18,13 @@ package multichain import ( "fmt" + "reflect" "github.com/hyperledger/fabric/common/config" "github.com/hyperledger/fabric/common/configtx" configtxapi "github.com/hyperledger/fabric/common/configtx/api" - "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/orderer/common/filter" cb "github.com/hyperledger/fabric/protos/common" - ab "github.com/hyperledger/fabric/protos/orderer" "github.com/hyperledger/fabric/protos/utils" "github.com/golang/protobuf/proto" @@ -33,12 +32,12 @@ import ( // Define some internal interfaces for easier mocking type chainCreator interface { + NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) newChain(configTx *cb.Envelope) channelsCount() int } type limitedSupport interface { - PolicyManager() policies.Manager SharedConfig() config.Orderer } @@ -75,7 +74,7 @@ func (scf *systemChainFilter) Apply(env *cb.Envelope) (filter.Action, filter.Com return filter.Forward, nil } - if msgData.Header == nil /* || msgData.Header.ChannelHeader == nil */ { + if msgData.Header == nil { return filter.Forward, nil } @@ -115,80 +114,35 @@ func (scf *systemChainFilter) Apply(env *cb.Envelope) (filter.Action, filter.Com } } -func (scf *systemChainFilter) authorize(configEnvelope *cb.ConfigEnvelope) error { +func (scf *systemChainFilter) authorize(configEnvelope *cb.ConfigEnvelope) (configtxapi.Manager, error) { if configEnvelope.LastUpdate == nil { - return fmt.Errorf("Must include a config update") + return nil, fmt.Errorf("Must include a config update") } - configEnvEnvPayload, err := utils.UnmarshalPayload(configEnvelope.LastUpdate.Payload) + configManager, err := scf.cc.NewChannelConfig(configEnvelope.LastUpdate) if err != nil { - return fmt.Errorf("Failing to validate chain creation because of payload unmarshaling error: %s", err) + return nil, fmt.Errorf("Error constructing new channel config from update: %s", err) } - configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configEnvEnvPayload.Data) + newChannelConfigEnv, err := configManager.ProposeConfigUpdate(configEnvelope.LastUpdate) if err != nil { - return fmt.Errorf("Failing to validate chain creation because of config update envelope unmarshaling error: %s", err) + return nil, err } - configMsg, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) + err = configManager.Apply(newChannelConfigEnv) if err != nil { - return fmt.Errorf("Failing to validate chain creation because of unmarshaling error: %s", err) + return nil, err } - if configMsg.WriteSet == nil { - return fmt.Errorf("Failing to validate channel creation because WriteSet is nil") - } - - ordererGroup, ok := configMsg.WriteSet.Groups[config.OrdererGroupKey] - if !ok { - return fmt.Errorf("Rejecting channel creation because it is missing orderer group") - } - - creationConfigItem, ok := ordererGroup.Values[configtx.CreationPolicyKey] - if !ok { - return fmt.Errorf("Failing to validate chain creation because no creation policy included") - } - - creationPolicy := &ab.CreationPolicy{} - err = proto.Unmarshal(creationConfigItem.Value, creationPolicy) - if err != nil { - return fmt.Errorf("Failing to validate chain creation because first config item could not unmarshal to a CreationPolicy: %s", err) - } - - ok = false - for _, chainCreatorPolicy := range scf.support.SharedConfig().ChainCreationPolicyNames() { - if chainCreatorPolicy == creationPolicy.Policy { - ok = true - break - } - } - - if !ok { - return fmt.Errorf("Failed to validate chain creation because chain creation policy (%s) is not authorized for chain creation", creationPolicy.Policy) - } - - policy, ok := scf.support.PolicyManager().GetPolicy(creationPolicy.Policy) - if !ok { - return fmt.Errorf("Failed to get policy for chain creation despite it being listed as an authorized policy") - } - - signedData, err := configUpdateEnv.AsSignedData() - if err != nil { - return fmt.Errorf("Failed to validate chain creation because config envelope could not be converted to signed data: %s", err) - } - - err = policy.Evaluate(signedData) - if err != nil { - return fmt.Errorf("Failed to validate chain creation, did not satisfy policy: %s", err) - } - - return nil + return configManager, nil } -func (scf *systemChainFilter) inspect(configManager configtxapi.Resources) error { - // XXX decide what it is that we will require to be the same in the new config, and what will be allowed to be different - // Are all keys allowed? etc. - +func (scf *systemChainFilter) inspect(proposedManager, configManager configtxapi.Manager) error { + proposedEnv := proposedManager.ConfigEnvelope() + actualEnv := configManager.ConfigEnvelope() + if !reflect.DeepEqual(proposedEnv.Config, actualEnv.Config) { + return fmt.Errorf("The config proposed by the channel creation request did not match the config received with the channel creation request") + } return nil } @@ -199,7 +153,7 @@ func (scf *systemChainFilter) authorizeAndInspect(configTx *cb.Envelope) error { return fmt.Errorf("Rejecting chain proposal: Error unmarshaling envelope payload: %s", err) } - if payload.Header == nil /* || payload.Header.ChannelHeader == nil */ { + if payload.Header == nil { return fmt.Errorf("Rejecting chain proposal: Not a config transaction") } @@ -219,7 +173,7 @@ func (scf *systemChainFilter) authorizeAndInspect(configTx *cb.Envelope) error { } // Make sure that the config was signed by the appropriate authorized entities - err = scf.authorize(configEnvelope) + proposedManager, err := scf.authorize(configEnvelope) if err != nil { return err } @@ -231,5 +185,5 @@ func (scf *systemChainFilter) authorizeAndInspect(configTx *cb.Envelope) error { } // Make sure that the config does not modify any of the orderer - return scf.inspect(configManager) + return scf.inspect(proposedManager, configManager) } diff --git a/orderer/multichain/systemchain_test.go b/orderer/multichain/systemchain_test.go index 3f65095ebbd..228789a1ba7 100644 --- a/orderer/multichain/systemchain_test.go +++ b/orderer/multichain/systemchain_test.go @@ -17,49 +17,44 @@ limitations under the License. package multichain import ( + "fmt" "testing" "github.com/hyperledger/fabric/common/config" "github.com/hyperledger/fabric/common/configtx" - configtxtest "github.com/hyperledger/fabric/common/configtx/test" - "github.com/hyperledger/fabric/common/configtx/tool/provisional" + configtxapi "github.com/hyperledger/fabric/common/configtx/api" + mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx" mockconfigvaluesorderer "github.com/hyperledger/fabric/common/mocks/configvalues/channel/orderer" - mockpolicies "github.com/hyperledger/fabric/common/mocks/policies" - "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/orderer/common/filter" cb "github.com/hyperledger/fabric/protos/common" + "github.com/hyperledger/fabric/protos/utils" "github.com/stretchr/testify/assert" ) type mockSupport struct { - mpm *mockpolicies.Manager msc *mockconfigvaluesorderer.SharedConfig } -func newMockSupport(chainID string) *mockSupport { +func newMockSupport() *mockSupport { return &mockSupport{ - mpm: &mockpolicies.Manager{}, msc: &mockconfigvaluesorderer.SharedConfig{}, } } -func (ms *mockSupport) PolicyManager() policies.Manager { - return ms.mpm -} - func (ms *mockSupport) SharedConfig() config.Orderer { return ms.msc } type mockChainCreator struct { - newChains []*cb.Envelope - ms *mockSupport + ms *mockSupport + newChains []*cb.Envelope + NewChannelConfigErr error } func newMockChainCreator() *mockChainCreator { mcc := &mockChainCreator{ - ms: newMockSupport(provisional.TestChainID), + ms: newMockSupport(), } return mcc } @@ -72,14 +67,32 @@ func (mcc *mockChainCreator) channelsCount() int { return len(mcc.newChains) } +func (mcc *mockChainCreator) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) { + if mcc.NewChannelConfigErr != nil { + return nil, mcc.NewChannelConfigErr + } + confUpdate := configtx.UnmarshalConfigUpdateOrPanic(configtx.UnmarshalConfigUpdateEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(envConfigUpdate.Payload).Data).ConfigUpdate) + return &mockconfigtx.Manager{ + ConfigEnvelopeVal: &cb.ConfigEnvelope{ + Config: &cb.Config{Sequence: 1, ChannelGroup: confUpdate.WriteSet}, + LastUpdate: envConfigUpdate, + }, + }, nil +} + func TestGoodProposal(t *testing.T) { newChainID := "NewChainID" mcc := newMockChainCreator() - mcc.ms.msc.ChainCreationPolicyNamesVal = []string{provisional.AcceptAllPolicyKey} - mcc.ms.mpm.Policy = &mockpolicies.Policy{} - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtxtest.CompositeTemplate()).Envelope(newChainID) + configEnv, err := configtx.NewCompositeTemplate( + configtx.NewSimpleTemplate( + config.DefaultHashingAlgorithm(), + config.DefaultBlockDataHashingStructure(), + config.TemplateOrdererAddresses([]string{"foo"}), + ), + configtx.NewChainCreationTemplate("SampleConsortium", []string{}), + ).Envelope(newChainID) if err != nil { t.Fatalf("Error constructing configtx") } @@ -98,32 +111,20 @@ func TestGoodProposal(t *testing.T) { assert.Equal(t, ingressTx, mcc.newChains[0], "New chain should have been created with ingressTx") } -func TestProposalWithBadPolicy(t *testing.T) { +func TestProposalRejectedByConfig(t *testing.T) { newChainID := "NewChainID" mcc := newMockChainCreator() - mcc.ms.mpm.Policy = &mockpolicies.Policy{} - - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtx.NewCompositeTemplate()).Envelope(newChainID) - if err != nil { - t.Fatalf("Error constructing configtx") - } - ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv) - wrapped := wrapConfigTx(ingressTx) - - sysFilter := newSystemChainFilter(mcc.ms, mcc) - action, _ := sysFilter.Apply(wrapped) - - assert.EqualValues(t, action, filter.Reject, "Transaction creation policy was not authorized") -} - -func TestProposalWithMissingPolicy(t *testing.T) { - newChainID := "NewChainID" - - mcc := newMockChainCreator() - mcc.ms.msc.ChainCreationPolicyNamesVal = []string{provisional.AcceptAllPolicyKey} - - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtx.NewCompositeTemplate()).Envelope(newChainID) + mcc.NewChannelConfigErr = fmt.Errorf("Error creating channel") + + configEnv, err := configtx.NewCompositeTemplate( + configtx.NewSimpleTemplate( + config.DefaultHashingAlgorithm(), + config.DefaultBlockDataHashingStructure(), + config.TemplateOrdererAddresses([]string{"foo"}), + ), + configtx.NewChainCreationTemplate("SampleConsortium", []string{}), + ).Envelope(newChainID) if err != nil { t.Fatalf("Error constructing configtx") } @@ -133,19 +134,25 @@ func TestProposalWithMissingPolicy(t *testing.T) { sysFilter := newSystemChainFilter(mcc.ms, mcc) action, _ := sysFilter.Apply(wrapped) - assert.EqualValues(t, filter.Reject, action, "Transaction had missing policy") + assert.EqualValues(t, action, filter.Reject, "Did not accept valid transaction") + assert.Len(t, mcc.newChains, 0, "Proposal should not have created a new chain") } func TestNumChainsExceeded(t *testing.T) { newChainID := "NewChainID" mcc := newMockChainCreator() - mcc.ms.msc.ChainCreationPolicyNamesVal = []string{provisional.AcceptAllPolicyKey} - mcc.ms.mpm.Policy = &mockpolicies.Policy{} mcc.ms.msc.MaxChannelsCountVal = 1 mcc.newChains = make([]*cb.Envelope, 2) - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtx.NewCompositeTemplate()).Envelope(newChainID) + configEnv, err := configtx.NewCompositeTemplate( + configtx.NewSimpleTemplate( + config.DefaultHashingAlgorithm(), + config.DefaultBlockDataHashingStructure(), + config.TemplateOrdererAddresses([]string{"foo"}), + ), + configtx.NewChainCreationTemplate("SampleConsortium", []string{}), + ).Envelope(newChainID) if err != nil { t.Fatalf("Error constructing configtx") } diff --git a/orderer/multichain/util_test.go b/orderer/multichain/util_test.go index cf23e2e4102..981bf336243 100644 --- a/orderer/multichain/util_test.go +++ b/orderer/multichain/util_test.go @@ -27,11 +27,6 @@ import ( "github.com/hyperledger/fabric/protos/utils" ) -const ( - msgVersion = int32(0) - epoch = uint64(0) -) - type mockConsenter struct { } @@ -115,7 +110,7 @@ func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.Co panic(err) } configTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, chainID, mockCrypto(), &cb.ConfigEnvelope{ - Config: &cb.Config{ChannelGroup: configtx.UnmarshalConfigUpdateOrPanic(configUpdateEnv.ConfigUpdate).WriteSet}, + Config: &cb.Config{Sequence: 1, ChannelGroup: configtx.UnmarshalConfigUpdateOrPanic(configUpdateEnv.ConfigUpdate).WriteSet}, LastUpdate: configUpdateTx}, msgVersion, epoch) if err != nil { diff --git a/orderer/sample_clients/broadcast_config/client.go b/orderer/sample_clients/broadcast_config/client.go index 11d4b21054c..6aeee8dc5c3 100644 --- a/orderer/sample_clients/broadcast_config/client.go +++ b/orderer/sample_clients/broadcast_config/client.go @@ -24,6 +24,8 @@ import ( "google.golang.org/grpc" genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" + "github.com/hyperledger/fabric/msp" + mspmgmt "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/orderer/localconfig" cb "github.com/hyperledger/fabric/protos/common" ab "github.com/hyperledger/fabric/protos/orderer" @@ -31,6 +33,7 @@ import ( var conf *config.TopLevel var genConf *genesisconfig.Profile +var signer msp.SigningIdentity type broadcastClient struct { ab.AtomicBroadcast_BroadcastClient @@ -70,6 +73,18 @@ type argsImpl struct { func init() { conf = config.Load() genConf = genesisconfig.Load(genesisconfig.SampleInsecureProfile) + + // Load local MSP + err := mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir, conf.General.BCCSP, conf.General.LocalMSPID) + if err != nil { + panic(fmt.Errorf("Failed to initialize local MSP: %s", err)) + } + + msp := mspmgmt.GetLocalMSP() + signer, err = msp.GetDefaultSigningIdentity() + if err != nil { + panic(fmt.Errorf("Failed to initialize get default signer: %s", err)) + } } func main() { diff --git a/orderer/sample_clients/broadcast_config/newchain.go b/orderer/sample_clients/broadcast_config/newchain.go index 9bebcabbc3a..6df40fa571d 100644 --- a/orderer/sample_clients/broadcast_config/newchain.go +++ b/orderer/sample_clients/broadcast_config/newchain.go @@ -18,24 +18,12 @@ package main import ( "github.com/hyperledger/fabric/common/configtx" - configtxtest "github.com/hyperledger/fabric/common/configtx/test" - //"github.com/hyperledger/fabric/common/configtx/tool/provisional" - "github.com/hyperledger/fabric/msp" + genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" cb "github.com/hyperledger/fabric/protos/common" ) func newChainRequest(consensusType, creationPolicy, newChannelId string) *cb.Envelope { - //genConf.Orderer.OrdererType = consensusType - //generator := provisional.New(genConf) - //channelTemplate := generator.ChannelTemplate() - channelTemplate := configtxtest.CompositeTemplate() - - signer, err := msp.NewNoopMsp().GetDefaultSigningIdentity() - if err != nil { - panic(err) - } - - env, err := configtx.MakeChainCreationTransaction(creationPolicy, newChannelId, signer, channelTemplate) + env, err := configtx.MakeChainCreationTransaction(newChannelId, genesisconfig.SampleConsortiumName, signer) if err != nil { panic(err) } diff --git a/peer/channel/create.go b/peer/channel/create.go index 97efc1fd1ac..3f9ae23d1ed 100644 --- a/peer/channel/create.go +++ b/peer/channel/create.go @@ -25,8 +25,9 @@ import ( "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/configtx" - configtxtest "github.com/hyperledger/fabric/common/configtx/test" - "github.com/hyperledger/fabric/common/configtx/tool/provisional" + genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" + localsigner "github.com/hyperledger/fabric/common/localmsp" + "github.com/hyperledger/fabric/common/util" mspmgmt "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/peer/common" cb "github.com/hyperledger/fabric/protos/common" @@ -64,14 +65,12 @@ func createCmd(cf *ChannelCmdFactory) *cobra.Command { } func createChannelFromDefaults(cf *ChannelCmdFactory) (*cb.Envelope, error) { - chCrtTemp := configtxtest.CompositeTemplate() - signer, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() if err != nil { return nil, err } - chCrtEnv, err := configtx.MakeChainCreationTransaction(provisional.AcceptAllPolicyKey, chainID, signer, chCrtTemp) + chCrtEnv, err := configtx.MakeChainCreationTransaction(chainID, genesisconfig.SampleConsortiumName, signer) if err != nil { return nil, err @@ -86,9 +85,14 @@ func createChannelFromConfigTx(configTxFileName string) (*cb.Envelope, error) { return nil, ConfigTxFileNotFound(err.Error()) } - env := utils.UnmarshalEnvelopeOrPanic(cftx) + return utils.UnmarshalEnvelope(cftx) +} - payload := utils.ExtractPayloadOrPanic(env) +func sanityCheckAndSignChannelCreateTx(envConfigUpdate *cb.Envelope) (*cb.Envelope, error) { + payload, err := utils.ExtractPayload(envConfigUpdate) + if err != nil { + return nil, InvalidCreateTx("bad payload") + } if payload.Header == nil || payload.Header.ChannelHeader == nil { return nil, InvalidCreateTx("bad header") @@ -111,7 +115,26 @@ func createChannelFromConfigTx(configTxFileName string) (*cb.Envelope, error) { return nil, InvalidCreateTx(fmt.Sprintf("mismatched channel ID %s != %s", ch.ChannelId, chainID)) } - return env, nil + configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(payload.Data) + if err != nil { + return nil, InvalidCreateTx("Bad config update env") + } + + signer := localsigner.NewSigner() + sigHeader, err := signer.NewSignatureHeader() + if err != nil { + return nil, err + } + + configSig := &cb.ConfigSignature{ + SignatureHeader: utils.MarshalOrPanic(sigHeader), + } + + configSig.Signature, err = signer.Sign(util.ConcatenateBytes(configSig.SignatureHeader, configUpdateEnv.ConfigUpdate)) + + configUpdateEnv.Signatures = append(configUpdateEnv.Signatures, configSig) + + return utils.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, chainID, signer, configUpdateEnv, 0, 0) } func sendCreateChainTransaction(cf *ChannelCmdFactory) error { @@ -127,6 +150,11 @@ func sendCreateChainTransaction(cf *ChannelCmdFactory) error { return err } } + + if chCrtEnv, err = sanityCheckAndSignChannelCreateTx(chCrtEnv); err != nil { + return err + } + var broadcastClient common.BroadcastClient broadcastClient, err = cf.BroadcastFactory() if err != nil { diff --git a/sampleconfig/configtx.yaml b/sampleconfig/configtx.yaml index c5386c2a8c9..0485ef4ac3e 100644 --- a/sampleconfig/configtx.yaml +++ b/sampleconfig/configtx.yaml @@ -1,10 +1,13 @@ --- ################################################################################ # -# Profile +# Profiles # # - Different configuration profiles may be encoded here to be specified -# as parameters to the configtxgen tool. +# as parameters to the configtxgen tool. The profiles which specify consortiums +# are to be used for generating the orderer genesis block. With the correct +# consortium members defined in the orderer genesis block, channel creation +# requests may be generated with only the org member names and a consortium name # ################################################################################ Profiles: @@ -50,6 +53,22 @@ Profiles: Organizations: - *SampleOrg + # SampleEmptyInsecureChannel defines a channel with no members + # and therefore no access control + SampleEmptyInsecureChannel: + Consortium: SampleConsortium + Application: + Organizations: + + # SampleSingleMSPChannel defines a channel with only the sample org as a + # member. It is designed to be used in conjunction with SampleSingleMSPSolo + # and SampleSingleMSPKafka orderer profiles + SampleSingleMSPChannel: + Consortium: SampleConsortium + Application: + Organizations: + - *SampleOrg + ################################################################################ # # Section: Organizations