diff --git a/common/channelconfig/bundle.go b/common/channelconfig/bundle.go index db1c06b44a8..189d85c83c5 100644 --- a/common/channelconfig/bundle.go +++ b/common/channelconfig/bundle.go @@ -7,8 +7,6 @@ SPDX-License-Identifier: Apache-2.0 package channelconfig import ( - "fmt" - "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/configtx" configtxapi "github.com/hyperledger/fabric/common/configtx/api" @@ -86,11 +84,11 @@ func (b *Bundle) ValidateNew(nb Resources) error { if oc, ok := b.OrdererConfig(); ok { noc, ok := nb.OrdererConfig() if !ok { - return fmt.Errorf("Current config has orderer section, but new config does not") + return errors.New("Current config has orderer section, but new config does not") } if oc.ConsensusType() != noc.ConsensusType() { - return fmt.Errorf("Attempted to change consensus type from %s to %s", oc.ConsensusType(), noc.ConsensusType()) + return errors.Errorf("Attempted to change consensus type from %s to %s", oc.ConsensusType(), noc.ConsensusType()) } for orgName, org := range oc.Organizations() { @@ -100,7 +98,7 @@ func (b *Bundle) ValidateNew(nb Resources) error { } mspID := org.MSPID() if mspID != norg.MSPID() { - return fmt.Errorf("Orderer org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID()) + return errors.Errorf("Orderer org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID()) } } } @@ -108,7 +106,7 @@ func (b *Bundle) ValidateNew(nb Resources) error { if ac, ok := b.ApplicationConfig(); ok { nac, ok := nb.ApplicationConfig() if !ok { - return fmt.Errorf("Current config has consortiums section, but new config does not") + return errors.New("Current config has application section, but new config does not") } for orgName, org := range ac.Organizations() { @@ -118,7 +116,7 @@ func (b *Bundle) ValidateNew(nb Resources) error { } mspID := org.MSPID() if mspID != norg.MSPID() { - return fmt.Errorf("Application org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID()) + return errors.Errorf("Application org %s attempted to change MSP ID from %s to %s", orgName, mspID, norg.MSPID()) } } } @@ -126,7 +124,7 @@ func (b *Bundle) ValidateNew(nb Resources) error { if cc, ok := b.ConsortiumsConfig(); ok { ncc, ok := nb.ConsortiumsConfig() if !ok { - return fmt.Errorf("Current config has consortiums section, but new config does not") + return errors.Errorf("Current config has consortiums section, but new config does not") } for consortiumName, consortium := range cc.Consortiums() { @@ -142,7 +140,7 @@ func (b *Bundle) ValidateNew(nb Resources) error { } mspID := org.MSPID() if mspID != norg.MSPID() { - return fmt.Errorf("Consortium %s org %s attempted to change MSP ID from %s to %s", consortiumName, orgName, mspID, norg.MSPID()) + return errors.Errorf("Consortium %s org %s attempted to change MSP ID from %s to %s", consortiumName, orgName, mspID, norg.MSPID()) } } } @@ -179,7 +177,7 @@ func NewBundleFromEnvelope(env *cb.Envelope) (*Bundle, error) { } if payload.Header == nil { - return nil, fmt.Errorf("envelope header cannot be nil") + return nil, errors.Errorf("envelope header cannot be nil") } chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) @@ -192,12 +190,8 @@ func NewBundleFromEnvelope(env *cb.Envelope) (*Bundle, error) { // NewBundle creates a new immutable bundle of configuration func NewBundle(channelID string, config *cb.Config) (*Bundle, error) { - if config == nil { - return nil, fmt.Errorf("config cannot be nil") - } - - if config.ChannelGroup == nil { - return nil, fmt.Errorf("config must contain a channel group") + if err := preValidate(config); err != nil { + return nil, err } channelConfig, err := NewChannelConfig(config.ChannelGroup) @@ -241,3 +235,29 @@ func NewBundle(channelID string, config *cb.Config) (*Bundle, error) { configtxManager: configtxManager, }, nil } + +func preValidate(config *cb.Config) error { + if config == nil { + return errors.New("config cannot be nil") + } + + if config.ChannelGroup == nil { + return errors.New("config must contain a channel group") + } + + if og, ok := config.ChannelGroup.Groups[OrdererGroupKey]; ok { + if _, ok := og.Values[CapabilitiesKey]; !ok { + if _, ok := config.ChannelGroup.Values[CapabilitiesKey]; ok { + return errors.New("cannot enable channel capabilities without orderer support first") + } + + if ag, ok := config.ChannelGroup.Groups[ApplicationGroupKey]; ok { + if _, ok := ag.Values[CapabilitiesKey]; ok { + return errors.New("cannot enable application capabilities without orderer support first") + } + } + } + } + + return nil +} diff --git a/common/channelconfig/bundle_test.go b/common/channelconfig/bundle_test.go index b8064e4f3cb..0f6d5bb4294 100644 --- a/common/channelconfig/bundle_test.go +++ b/common/channelconfig/bundle_test.go @@ -9,6 +9,7 @@ package channelconfig import ( "testing" + cb "github.com/hyperledger/fabric/protos/common" ab "github.com/hyperledger/fabric/protos/orderer" "github.com/stretchr/testify/assert" @@ -44,7 +45,7 @@ func TestValidateNew(t *testing.T) { err := cb.ValidateNew(nb) assert.Error(t, err) - assert.Regexp(t, "Current config has consortiums section, but new config does not", err.Error()) + assert.Regexp(t, "Current config has application section, but new config does not", err.Error()) }) t.Run("DisappearingConsortiumsConfig", func(t *testing.T) { @@ -200,3 +201,77 @@ func TestValidateNew(t *testing.T) { assert.Regexp(t, "Consortium consortium1 org org3 attempted to change MSP ID from", err.Error()) }) } + +func TestPrevalidation(t *testing.T) { + t.Run("NilConfig", func(t *testing.T) { + err := preValidate(nil) + + assert.Error(t, err) + assert.Regexp(t, "config cannot be nil", err.Error()) + }) + + t.Run("NilChannelGroup", func(t *testing.T) { + err := preValidate(&cb.Config{}) + + assert.Error(t, err) + assert.Regexp(t, "config must contain a channel group", err.Error()) + }) + + t.Run("BadChannelCapabilities", func(t *testing.T) { + err := preValidate(&cb.Config{ + ChannelGroup: &cb.ConfigGroup{ + Groups: map[string]*cb.ConfigGroup{ + OrdererGroupKey: &cb.ConfigGroup{}, + }, + Values: map[string]*cb.ConfigValue{ + CapabilitiesKey: &cb.ConfigValue{}, + }, + }, + }) + + assert.Error(t, err) + assert.Regexp(t, "cannot enable channel capabilities without orderer support first", err.Error()) + }) + + t.Run("BadApplicationCapabilities", func(t *testing.T) { + err := preValidate(&cb.Config{ + ChannelGroup: &cb.ConfigGroup{ + Groups: map[string]*cb.ConfigGroup{ + ApplicationGroupKey: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + CapabilitiesKey: &cb.ConfigValue{}, + }, + }, + OrdererGroupKey: &cb.ConfigGroup{}, + }, + }, + }) + + assert.Error(t, err) + assert.Regexp(t, "cannot enable application capabilities without orderer support first", err.Error()) + }) + + t.Run("ValidCapabilities", func(t *testing.T) { + err := preValidate(&cb.Config{ + ChannelGroup: &cb.ConfigGroup{ + Groups: map[string]*cb.ConfigGroup{ + ApplicationGroupKey: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + CapabilitiesKey: &cb.ConfigValue{}, + }, + }, + OrdererGroupKey: &cb.ConfigGroup{ + Values: map[string]*cb.ConfigValue{ + CapabilitiesKey: &cb.ConfigValue{}, + }, + }, + }, + Values: map[string]*cb.ConfigValue{ + CapabilitiesKey: &cb.ConfigValue{}, + }, + }, + }) + + assert.NoError(t, err) + }) +}