diff --git a/common/configtx/api/api.go b/common/configtx/api/api.go index a569f9f9e52..dc53843157d 100644 --- a/common/configtx/api/api.go +++ b/common/configtx/api/api.go @@ -18,7 +18,7 @@ package api import ( configvalues "github.com/hyperledger/fabric/common/configvalues" - configvalueschannel "github.com/hyperledger/fabric/common/configvalues/channel" + config "github.com/hyperledger/fabric/common/configvalues/root" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/msp" cb "github.com/hyperledger/fabric/protos/common" @@ -52,7 +52,7 @@ type Resources interface { PolicyManager() policies.Manager // ChannelConfig returns the ChannelConfig for the chain - ChannelConfig() configvalueschannel.ConfigReader + ChannelConfig() config.ChannelValues // OrdererConfig returns the configtxorderer.SharedConfig for the channel OrdererConfig() configvalues.Orderer diff --git a/common/configtx/initializer.go b/common/configtx/initializer.go index 583b6dd5ca8..670453fb310 100644 --- a/common/configtx/initializer.go +++ b/common/configtx/initializer.go @@ -22,9 +22,8 @@ import ( "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/configtx/api" configvaluesapi "github.com/hyperledger/fabric/common/configvalues" - configvalueschannel "github.com/hyperledger/fabric/common/configvalues/channel" configtxmsp "github.com/hyperledger/fabric/common/configvalues/msp" - configvaluesroot "github.com/hyperledger/fabric/common/configvalues/root" + config "github.com/hyperledger/fabric/common/configvalues/root" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/msp" cb "github.com/hyperledger/fabric/protos/common" @@ -32,7 +31,7 @@ import ( type resources struct { policyManager *policies.ManagerImpl - configRoot *configvaluesroot.Root + configRoot *config.Root mspConfigHandler *configtxmsp.MSPConfigHandler } @@ -42,7 +41,7 @@ func (r *resources) PolicyManager() policies.Manager { } // ChannelConfig returns the api.ChannelConfig for the chain -func (r *resources) ChannelConfig() configvalueschannel.ConfigReader { +func (r *resources) ChannelConfig() config.ChannelValues { return r.configRoot.Channel() } @@ -79,7 +78,7 @@ func newResources() *resources { return &resources{ policyManager: policies.NewManagerImpl(RootGroupKey, policyProviderMap), - configRoot: configvaluesroot.NewRoot(mspConfigHandler), + configRoot: config.NewRoot(mspConfigHandler), mspConfigHandler: mspConfigHandler, } } diff --git a/common/configtx/manager.go b/common/configtx/manager.go index 599b11de16e..7861ade9bc6 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -40,7 +40,7 @@ var ( } ) -type config struct { +type configSet struct { channelID string sequence uint64 configMap map[string]comparable @@ -50,7 +50,7 @@ type configManager struct { api.Resources callOnUpdate []func(api.Manager) initializer api.Initializer - current *config + current *configSet } // validateChainID makes sure that proposed chain IDs (i.e. channel names) @@ -110,7 +110,7 @@ func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnU cm := &configManager{ Resources: initializer, initializer: initializer, - current: &config{ + current: &configSet{ sequence: configEnv.Config.Sequence, configMap: configMap, channelID: header.ChannelId, @@ -234,7 +234,7 @@ func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error { result.commit() cm.commitCallbacks() - cm.current = &config{ + cm.current = &configSet{ configMap: configMap, channelID: cm.current.channelID, sequence: configEnv.Config.Sequence, diff --git a/common/configtx/tool/provisional/provisional.go b/common/configtx/tool/provisional/provisional.go index 2f401c81f4c..f92c1f0f846 100644 --- a/common/configtx/tool/provisional/provisional.go +++ b/common/configtx/tool/provisional/provisional.go @@ -22,10 +22,10 @@ import ( "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/configtx" genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" - configtxchannel "github.com/hyperledger/fabric/common/configvalues/channel" configtxapplication "github.com/hyperledger/fabric/common/configvalues/channel/application" configtxorderer "github.com/hyperledger/fabric/common/configvalues/channel/orderer" configvaluesmsp "github.com/hyperledger/fabric/common/configvalues/msp" + config "github.com/hyperledger/fabric/common/configvalues/root" "github.com/hyperledger/fabric/common/genesis" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/msp" @@ -83,9 +83,9 @@ func New(conf *genesisconfig.Profile) Generator { bs := &bootstrapper{ channelGroups: []*cb.ConfigGroup{ // Chain Config Types - configtxchannel.DefaultHashingAlgorithm(), - configtxchannel.DefaultBlockDataHashingStructure(), - configtxchannel.TemplateOrdererAddresses(conf.Orderer.Addresses), // TODO, move to conf.Channel when it exists + config.DefaultHashingAlgorithm(), + config.DefaultBlockDataHashingStructure(), + config.TemplateOrdererAddresses(conf.Orderer.Addresses), // TODO, move to conf.Channel when it exists // Default policies policies.TemplateImplicitMetaAnyPolicy([]string{}, configvaluesmsp.ReadersPolicyKey), diff --git a/common/configtx/update.go b/common/configtx/update.go index dbcffe23c87..965d8f4cbb2 100644 --- a/common/configtx/update.go +++ b/common/configtx/update.go @@ -24,7 +24,7 @@ import ( "github.com/hyperledger/fabric/protos/utils" ) -func (c *config) verifyReadSet(readSet map[string]comparable) error { +func (c *configSet) verifyReadSet(readSet map[string]comparable) error { for key, value := range readSet { existing, ok := c.configMap[key] if !ok { diff --git a/common/configtx/update_test.go b/common/configtx/update_test.go index 206ac0918d4..430105e9915 100644 --- a/common/configtx/update_test.go +++ b/common/configtx/update_test.go @@ -28,7 +28,7 @@ import ( ) func TestReadSetNotPresent(t *testing.T) { - cm := &config{ + cm := &configSet{ configMap: make(map[string]comparable), } @@ -43,7 +43,7 @@ func TestReadSetNotPresent(t *testing.T) { } func TestReadSetBackVersioned(t *testing.T) { - cm := &config{ + cm := &configSet{ configMap: make(map[string]comparable), } @@ -79,7 +79,7 @@ func TestVerifyDeltaSet(t *testing.T) { Policy: &mockpolicies.Policy{}, }, }, - current: &config{ + current: &configSet{ configMap: make(map[string]comparable), }, } diff --git a/common/configvalues/channel/config.go b/common/configvalues/channel/config.go deleted file mode 100644 index f5f65e23337..00000000000 --- a/common/configvalues/channel/config.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright IBM Corp. 2017 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package channel - -import ( - "fmt" - "math" - - api "github.com/hyperledger/fabric/common/configvalues" - "github.com/hyperledger/fabric/common/configvalues/channel/application" - "github.com/hyperledger/fabric/common/configvalues/channel/orderer" - "github.com/hyperledger/fabric/common/util" - cb "github.com/hyperledger/fabric/protos/common" - - "github.com/golang/protobuf/proto" - "github.com/op/go-logging" -) - -var Schema = &cb.ConfigGroupSchema{ - Groups: map[string]*cb.ConfigGroupSchema{ - application.GroupKey: application.Schema, - orderer.GroupKey: orderer.Schema, - }, - Values: map[string]*cb.ConfigValueSchema{ - HashingAlgorithmKey: nil, - BlockDataHashingStructureKey: nil, - OrdererAddressesKey: nil, - }, - Policies: map[string]*cb.ConfigPolicySchema{ - // TODO, set appropriately once hierarchical policies are implemented - }, -} - -// Channel config keys -const ( - // HashingAlgorithmKey is the cb.ConfigItem type key name for the HashingAlgorithm message - HashingAlgorithmKey = "HashingAlgorithm" - - // BlockDataHashingStructureKey is the cb.ConfigItem type key name for the BlockDataHashingStructure message - BlockDataHashingStructureKey = "BlockDataHashingStructure" - - // OrdererAddressesKey is the cb.ConfigItem type key name for the OrdererAddresses message - OrdererAddressesKey = "OrdererAddresses" - - // GroupKey is the name of the channel group - GroupKey = "Channel" -) - -// Hashing algorithm types -const ( - // SHA3256 is SHA3 with fixed size 256-bit hash - SHA3256 = "SHA3256" - - // SHA256 is SHA2 with fixed size 256-bit hash - SHA256 = "SHA256" -) - -var logger = logging.MustGetLogger("configvalues/channel") - -type ConfigReader interface { - // HashingAlgorithm returns the default algorithm to be used when hashing - // such as computing block hashes, and CreationPolicy digests - HashingAlgorithm() func(input []byte) []byte - - // BlockDataHashingStructureWidth returns the width to use when constructing the - // Merkle tree to compute the BlockData hash - BlockDataHashingStructureWidth() uint32 - - // OrdererAddresses returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver - OrdererAddresses() []string -} - -type values struct { - hashingAlgorithm func(input []byte) []byte - blockDataHashingStructureWidth uint32 - ordererAddresses []string -} - -// SharedConfigImpl is an implementation of Manager and configtx.ConfigHandler -// In general, it should only be referenced as an Impl for the configtx.Manager -type Config struct { - pending *values - current *values - - ordererConfig *orderer.ManagerImpl - applicationConfig *application.SharedConfigImpl -} - -// NewSharedConfigImpl creates a new SharedConfigImpl with the given CryptoHelper -func NewConfig(ordererConfig *orderer.ManagerImpl, applicationConfig *application.SharedConfigImpl) *Config { - return &Config{ - current: &values{}, - ordererConfig: ordererConfig, - applicationConfig: applicationConfig, - } -} - -// HashingAlgorithm returns a function pointer to the chain hashing algorihtm -func (c *Config) HashingAlgorithm() func(input []byte) []byte { - return c.current.hashingAlgorithm -} - -// BlockDataHashingStructure returns the width to use when forming the block data hashing structure -func (c *Config) BlockDataHashingStructureWidth() uint32 { - return c.current.blockDataHashingStructureWidth -} - -// OrdererAddresses returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver -func (c *Config) OrdererAddresses() []string { - return c.current.ordererAddresses -} - -// BeginValueProposals is used to start a new config proposal -func (c *Config) BeginValueProposals(groups []string) ([]api.ValueProposer, error) { - handlers := make([]api.ValueProposer, len(groups)) - - for i, group := range groups { - switch group { - case application.GroupKey: - handlers[i] = c.applicationConfig - case orderer.GroupKey: - handlers[i] = c.ordererConfig - default: - return nil, fmt.Errorf("Disallowed channel group: %s", group) - } - } - - if c.pending != nil { - logger.Panicf("Programming error, cannot call begin in the middle of a proposal") - } - - c.pending = &values{} - return handlers, nil -} - -// RollbackProposals is used to abandon a new config proposal -func (c *Config) RollbackProposals() { - c.pending = nil -} - -// CommitProposals is used to commit a new config proposal -func (c *Config) CommitProposals() { - if c.pending == nil { - logger.Panicf("Programming error, cannot call commit without an existing proposal") - } - c.current = c.pending - c.pending = nil -} - -// PreCommit returns nil -func (c *Config) PreCommit() error { return nil } - -// ProposeValue is used to add new config to the config proposal -func (c *Config) ProposeValue(key string, configValue *cb.ConfigValue) error { - switch key { - case HashingAlgorithmKey: - hashingAlgorithm := &cb.HashingAlgorithm{} - if err := proto.Unmarshal(configValue.Value, hashingAlgorithm); err != nil { - return fmt.Errorf("Unmarshaling error for HashingAlgorithm: %s", err) - } - switch hashingAlgorithm.Name { - case SHA256: - c.pending.hashingAlgorithm = util.ComputeSHA256 - case SHA3256: - c.pending.hashingAlgorithm = util.ComputeSHA3256 - default: - return fmt.Errorf("Unknown hashing algorithm type: %s", hashingAlgorithm.Name) - } - case BlockDataHashingStructureKey: - blockDataHashingStructure := &cb.BlockDataHashingStructure{} - if err := proto.Unmarshal(configValue.Value, blockDataHashingStructure); err != nil { - return fmt.Errorf("Unmarshaling error for BlockDataHashingStructure: %s", err) - } - - if blockDataHashingStructure.Width != math.MaxUint32 { - return fmt.Errorf("BlockDataHashStructure width only supported at MaxUint32 in this version") - } - - c.pending.blockDataHashingStructureWidth = blockDataHashingStructure.Width - case OrdererAddressesKey: - ordererAddresses := &cb.OrdererAddresses{} - if err := proto.Unmarshal(configValue.Value, ordererAddresses); err != nil { - return fmt.Errorf("Unmarshaling error for HashingAlgorithm: %s", err) - } - c.pending.ordererAddresses = ordererAddresses.Addresses - default: - logger.Warningf("Uknown Chain config item with key %s", key) - } - return nil -} diff --git a/common/configvalues/channel/config_test.go b/common/configvalues/channel/config_test.go deleted file mode 100644 index df45dd981cd..00000000000 --- a/common/configvalues/channel/config_test.go +++ /dev/null @@ -1,162 +0,0 @@ -/* -Copyright IBM Corp. 2017 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package channel - -import ( - "reflect" - "testing" - - cb "github.com/hyperledger/fabric/protos/common" - - logging "github.com/op/go-logging" -) - -func init() { - logging.SetLevel(logging.DEBUG, "") -} - -func groupToKeyValue(configGroup *cb.ConfigGroup) (string, *cb.ConfigValue) { - for key, value := range configGroup.Values { - return key, value - } - panic("No value encoded") -} - -func makeInvalidConfigValue() *cb.ConfigValue { - return &cb.ConfigValue{ - Value: []byte("Garbage Data"), - } -} - -func TestInterface(t *testing.T) { - _ = ConfigReader(NewConfig(nil, nil)) -} - -func TestDoubleBegin(t *testing.T) { - defer func() { - if err := recover(); err == nil { - t.Fatalf("Should have panicked on multiple begin configs") - } - }() - - m := NewConfig(nil, nil) - m.BeginValueProposals(nil) - m.BeginValueProposals(nil) -} - -func TestCommitWithoutBegin(t *testing.T) { - defer func() { - if err := recover(); err == nil { - t.Fatalf("Should have panicked on multiple begin configs") - } - }() - - m := NewConfig(nil, nil) - m.CommitProposals() -} - -func TestRollback(t *testing.T) { - m := NewConfig(nil, nil) - m.pending = &values{} - m.RollbackProposals() - if m.pending != nil { - t.Fatalf("Should have cleared pending config on rollback") - } -} - -func TestHashingAlgorithm(t *testing.T) { - invalidMessage := makeInvalidConfigValue() - invalidAlgorithm := TemplateHashingAlgorithm("MD5") - validAlgorithm := DefaultHashingAlgorithm() - - m := NewConfig(nil, nil) - m.BeginValueProposals(nil) - - err := m.ProposeValue(HashingAlgorithmKey, invalidMessage) - if err == nil { - t.Fatalf("Should have failed on invalid message") - } - - err = m.ProposeValue(groupToKeyValue(invalidAlgorithm)) - if err == nil { - t.Fatalf("Should have failed on invalid algorithm") - } - - err = m.ProposeValue(groupToKeyValue(validAlgorithm)) - if err != nil { - t.Fatalf("Error applying valid config: %s", err) - } - - m.CommitProposals() - - if m.HashingAlgorithm() == nil { - t.Fatalf("Should have set default hashing algorithm") - } -} - -func TestBlockDataHashingStructure(t *testing.T) { - invalidMessage := makeInvalidConfigValue() - invalidWidth := TemplateBlockDataHashingStructure(0) - validWidth := DefaultBlockDataHashingStructure() - - m := NewConfig(nil, nil) - m.BeginValueProposals(nil) - - err := m.ProposeValue(BlockDataHashingStructureKey, invalidMessage) - if err == nil { - t.Fatalf("Should have failed on invalid message") - } - - err = m.ProposeValue(groupToKeyValue(invalidWidth)) - if err == nil { - t.Fatalf("Should have failed on invalid width") - } - - err = m.ProposeValue(groupToKeyValue(validWidth)) - if err != nil { - t.Fatalf("Error applying valid config: %s", err) - } - - m.CommitProposals() - - if newWidth := m.BlockDataHashingStructureWidth(); newWidth != defaultBlockDataHashingStructureWidth { - t.Fatalf("Unexpected width, got %d expected %d", newWidth, defaultBlockDataHashingStructureWidth) - } -} - -func TestOrdererAddresses(t *testing.T) { - invalidMessage := makeInvalidConfigValue() - validMessage := DefaultOrdererAddresses() - m := NewConfig(nil, nil) - m.BeginValueProposals(nil) - - err := m.ProposeValue(OrdererAddressesKey, invalidMessage) - if err == nil { - t.Fatalf("Should have failed on invalid message") - } - - err = m.ProposeValue(groupToKeyValue(validMessage)) - if err != nil { - t.Fatalf("Error applying valid config: %s", err) - } - - m.CommitProposals() - - if newAddrs := m.OrdererAddresses(); !reflect.DeepEqual(newAddrs, defaultOrdererAddresses) { - t.Fatalf("Unexpected width, got %s expected %s", newAddrs, defaultOrdererAddresses) - } -} diff --git a/common/configvalues/root/channel.go b/common/configvalues/root/channel.go new file mode 100644 index 00000000000..2c4843ea09f --- /dev/null +++ b/common/configvalues/root/channel.go @@ -0,0 +1,222 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "fmt" + "math" + + "github.com/hyperledger/fabric/bccsp" + api "github.com/hyperledger/fabric/common/configvalues" + "github.com/hyperledger/fabric/common/configvalues/channel/application" + "github.com/hyperledger/fabric/common/configvalues/channel/orderer" + "github.com/hyperledger/fabric/common/configvalues/msp" + "github.com/hyperledger/fabric/common/util" + cb "github.com/hyperledger/fabric/protos/common" +) + +// Channel config keys +const ( + // HashingAlgorithmKey is the cb.ConfigItem type key name for the HashingAlgorithm message + HashingAlgorithmKey = "HashingAlgorithm" + + // BlockDataHashingStructureKey is the cb.ConfigItem type key name for the BlockDataHashingStructure message + BlockDataHashingStructureKey = "BlockDataHashingStructure" + + // OrdererAddressesKey is the cb.ConfigItem type key name for the OrdererAddresses message + OrdererAddressesKey = "OrdererAddresses" + + // GroupKey is the name of the channel group + ChannelGroupKey = "Channel" +) + +// ChannelValues gives read only access to the channel configuration +type ChannelValues interface { + // HashingAlgorithm returns the default algorithm to be used when hashing + // such as computing block hashes, and CreationPolicy digests + HashingAlgorithm() func(input []byte) []byte + + // BlockDataHashingStructureWidth returns the width to use when constructing the + // Merkle tree to compute the BlockData hash + BlockDataHashingStructureWidth() uint32 + + // OrdererAddresses returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver + OrdererAddresses() []string +} + +// ChannelProtos is where the proposed configuration is unmarshaled into +type ChannelProtos struct { + HashingAlgorithm *cb.HashingAlgorithm + BlockDataHashingStructure *cb.BlockDataHashingStructure + OrdererAddresses *cb.OrdererAddresses +} + +type channelConfigSetter struct { + target **ChannelConfig + *ChannelConfig +} + +func (ccs *channelConfigSetter) Commit() { + *(ccs.target) = ccs.ChannelConfig +} + +// ChannelGroup +type ChannelGroup struct { + *ChannelConfig + *Proposer + mspConfigHandler *msp.MSPConfigHandler +} + +func NewChannelGroup(mspConfigHandler *msp.MSPConfigHandler) *ChannelGroup { + cg := &ChannelGroup{ + ChannelConfig: NewChannelConfig(), + mspConfigHandler: mspConfigHandler, + } + cg.Proposer = NewProposer(cg) + return cg +} + +// Allocate creates new config resources for a pending config update +func (cg *ChannelGroup) Allocate() Values { + return &channelConfigSetter{ + ChannelConfig: NewChannelConfig(), + target: &cg.ChannelConfig, + } +} + +// OrdererConfig returns the orderer config associated with this channel +func (cg *ChannelGroup) OrdererConfig() *orderer.ManagerImpl { + return cg.ChannelConfig.ordererConfig +} + +// ApplicationConfig returns the application config associated with this channel +func (cg *ChannelGroup) ApplicationConfig() *application.SharedConfigImpl { + return cg.ChannelConfig.appConfig +} + +// NewGroup instantiates either a new application or orderer config +func (cg *ChannelGroup) NewGroup(group string) (api.ValueProposer, error) { + switch group { + case application.GroupKey: + return application.NewSharedConfigImpl(cg.mspConfigHandler), nil + case orderer.GroupKey: + return orderer.NewManagerImpl(cg.mspConfigHandler), nil + default: + return nil, fmt.Errorf("Disallowed channel group: %s", group) + } +} + +// ChannelConfig stores the channel configuration +type ChannelConfig struct { + *standardValues + protos *ChannelProtos + + hashingAlgorithm func(input []byte) []byte + + appConfig *application.SharedConfigImpl + ordererConfig *orderer.ManagerImpl +} + +// NewChannelConfig creates a new ChannelConfig +func NewChannelConfig() *ChannelConfig { + cc := &ChannelConfig{ + protos: &ChannelProtos{}, + } + + var err error + cc.standardValues, err = NewStandardValues(cc.protos) + if err != nil { + logger.Panicf("Programming error: %s", err) + } + return cc +} + +// HashingAlgorithm returns a function pointer to the chain hashing algorihtm +func (cc *ChannelConfig) HashingAlgorithm() func(input []byte) []byte { + return cc.hashingAlgorithm +} + +// BlockDataHashingStructure returns the width to use when forming the block data hashing structure +func (cc *ChannelConfig) BlockDataHashingStructureWidth() uint32 { + return cc.protos.BlockDataHashingStructure.Width +} + +// OrdererAddresses returns the list of valid orderer addresses to connect to to invoke Broadcast/Deliver +func (cc *ChannelConfig) OrdererAddresses() []string { + return cc.protos.OrdererAddresses.Addresses +} + +// Validate inspects the generated configuration protos, ensures that the values are correct, and +// sets the ChannelConfig fields that may be referenced after Commit +func (cc *ChannelConfig) Validate(groups map[string]api.ValueProposer) error { + for _, validator := range []func() error{ + cc.validateHashingAlgorithm, + cc.validateBlockDataHashingStructure, + cc.validateOrdererAddresses, + } { + if err := validator(); err != nil { + return err + } + } + + var ok bool + for key, value := range groups { + switch key { + case application.GroupKey: + cc.appConfig, ok = value.(*application.SharedConfigImpl) + if !ok { + return fmt.Errorf("Application group was not Application config") + } + case orderer.GroupKey: + cc.ordererConfig, ok = value.(*orderer.ManagerImpl) + if !ok { + return fmt.Errorf("Orderer group was not Orderer config") + } + default: + return fmt.Errorf("Disallowed channel group: %s", key) + } + } + + return nil +} + +func (cc *ChannelConfig) validateHashingAlgorithm() error { + switch cc.protos.HashingAlgorithm.Name { + case bccsp.SHA256: + cc.hashingAlgorithm = util.ComputeSHA256 + case bccsp.SHA3_256: + cc.hashingAlgorithm = util.ComputeSHA3256 + default: + return fmt.Errorf("Unknown hashing algorithm type: %s", cc.protos.HashingAlgorithm.Name) + } + + return nil +} + +func (cc *ChannelConfig) validateBlockDataHashingStructure() error { + if cc.protos.BlockDataHashingStructure.Width != math.MaxUint32 { + return fmt.Errorf("BlockDataHashStructure width only supported at MaxUint32 in this version") + } + return nil +} + +func (cc *ChannelConfig) validateOrdererAddresses() error { + if len(cc.protos.OrdererAddresses.Addresses) == 0 { + return fmt.Errorf("Must set some OrdererAddresses") + } + return nil +} diff --git a/common/configvalues/root/channel_test.go b/common/configvalues/root/channel_test.go new file mode 100644 index 00000000000..e9c3ca89ecf --- /dev/null +++ b/common/configvalues/root/channel_test.go @@ -0,0 +1,66 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "math" + "testing" + + "github.com/hyperledger/fabric/bccsp" + cb "github.com/hyperledger/fabric/protos/common" + + logging "github.com/op/go-logging" + "github.com/stretchr/testify/assert" +) + +func init() { + logging.SetLevel(logging.DEBUG, "") +} + +func TestInterface(t *testing.T) { + _ = ChannelValues(NewChannelGroup(nil)) +} + +func TestHashingAlgorithm(t *testing.T) { + cc := &ChannelConfig{protos: &ChannelProtos{HashingAlgorithm: &cb.HashingAlgorithm{}}} + assert.Error(t, cc.validateHashingAlgorithm(), "Must supply hashing algorithm") + + cc = &ChannelConfig{protos: &ChannelProtos{HashingAlgorithm: &cb.HashingAlgorithm{Name: "MD5"}}} + assert.Error(t, cc.validateHashingAlgorithm(), "Bad hashing algorithm supplied") + + cc = &ChannelConfig{protos: &ChannelProtos{HashingAlgorithm: &cb.HashingAlgorithm{Name: bccsp.SHA256}}} + assert.NoError(t, cc.validateHashingAlgorithm(), "Allowed hashing algorith supplied") +} + +func TestBlockDataHashingStructure(t *testing.T) { + cc := &ChannelConfig{protos: &ChannelProtos{BlockDataHashingStructure: &cb.BlockDataHashingStructure{}}} + assert.Error(t, cc.validateBlockDataHashingStructure(), "Must supply block data hashing structure") + + cc = &ChannelConfig{protos: &ChannelProtos{BlockDataHashingStructure: &cb.BlockDataHashingStructure{Width: 7}}} + assert.Error(t, cc.validateBlockDataHashingStructure(), "Invalid Merkle tree width supplied") + + cc = &ChannelConfig{protos: &ChannelProtos{BlockDataHashingStructure: &cb.BlockDataHashingStructure{Width: math.MaxUint32}}} + assert.NoError(t, cc.validateBlockDataHashingStructure(), "Valid Merkle tree width supplied") +} + +func TestOrdererAddresses(t *testing.T) { + cc := &ChannelConfig{protos: &ChannelProtos{OrdererAddresses: &cb.OrdererAddresses{}}} + assert.Error(t, cc.validateOrdererAddresses(), "Must supply orderer addresses") + + cc = &ChannelConfig{protos: &ChannelProtos{OrdererAddresses: &cb.OrdererAddresses{Addresses: []string{"127.0.0.1:7050"}}}} + assert.NoError(t, cc.validateOrdererAddresses(), "Invalid Merkle tree width supplied") +} diff --git a/common/configvalues/channel/config_util.go b/common/configvalues/root/channel_util.go similarity index 96% rename from common/configvalues/channel/config_util.go rename to common/configvalues/root/channel_util.go index bae57aa5c47..44a028f27ec 100644 --- a/common/configvalues/channel/config_util.go +++ b/common/configvalues/root/channel_util.go @@ -14,16 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package channel +package config import ( "math" + "github.com/hyperledger/fabric/bccsp" cb "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" ) -const defaultHashingAlgorithm = SHA256 +const defaultHashingAlgorithm = bccsp.SHA256 func configGroup(key string, value []byte) *cb.ConfigGroup { result := cb.NewConfigGroup() diff --git a/common/configvalues/root/proposer.go b/common/configvalues/root/proposer.go index 684575343aa..9844ba9a101 100644 --- a/common/configvalues/root/proposer.go +++ b/common/configvalues/root/proposer.go @@ -35,7 +35,8 @@ type Values interface { ProtoMsg(key string) (proto.Message, bool) // Validate should ensure that the values set into the proto messages are correct - Validate() error + // and that the new group values are allowed + Validate(map[string]api.ValueProposer) error // Commit should call back into the Value handler to update the config Commit() @@ -119,7 +120,7 @@ func (p *Proposer) ProposeValue(key string, configValue *cb.ConfigValue) error { // Validate ensures that the new config values is a valid change func (p *Proposer) PreCommit() error { - return p.pending.allocated.Validate() + return p.pending.allocated.Validate(p.pending.groups) } // RollbackProposals called when a config proposal is abandoned diff --git a/common/configvalues/root/proposer_test.go b/common/configvalues/root/proposer_test.go index dba004f8b80..a80e767943a 100644 --- a/common/configvalues/root/proposer_test.go +++ b/common/configvalues/root/proposer_test.go @@ -43,7 +43,7 @@ func (v *mockValues) ProtoMsg(key string) (proto.Message, bool) { return msg, ok } -func (v *mockValues) Validate() error { +func (v *mockValues) Validate(map[string]api.ValueProposer) error { return v.ValidateReturn } diff --git a/common/configvalues/root/root.go b/common/configvalues/root/root.go index 893feb13482..c3151178059 100644 --- a/common/configvalues/root/root.go +++ b/common/configvalues/root/root.go @@ -20,7 +20,6 @@ import ( "fmt" configvaluesapi "github.com/hyperledger/fabric/common/configvalues" - "github.com/hyperledger/fabric/common/configvalues/channel" "github.com/hyperledger/fabric/common/configvalues/channel/application" "github.com/hyperledger/fabric/common/configvalues/channel/orderer" "github.com/hyperledger/fabric/common/configvalues/msp" @@ -31,21 +30,14 @@ import ( // Note, yes, this is a stuttering name, but, the intent is to move // this up one level at the end of refactoring type Root struct { - channel *channel.Config - orderer *orderer.ManagerImpl - application *application.SharedConfigImpl + channel *ChannelGroup mspConfigHandler *msp.MSPConfigHandler } // NewRoot creates a new instance of the configvalues Root func NewRoot(mspConfigHandler *msp.MSPConfigHandler) *Root { - ordererConfig := orderer.NewManagerImpl(mspConfigHandler) - applicationConfig := application.NewSharedConfigImpl(mspConfigHandler) - return &Root{ - channel: channel.NewConfig(ordererConfig, applicationConfig), - orderer: ordererConfig, - application: applicationConfig, + channel: NewChannelGroup(mspConfigHandler), mspConfigHandler: mspConfigHandler, } } @@ -55,7 +47,7 @@ func (r *Root) BeginValueProposals(groups []string) ([]configvaluesapi.ValueProp if len(groups) != 1 { return nil, fmt.Errorf("Root config only supports having one base group") } - if groups[0] != channel.GroupKey { + if groups[0] != ChannelGroupKey { return nil, fmt.Errorf("Root group must have channel") } r.mspConfigHandler.BeginConfig() @@ -83,16 +75,16 @@ func (r *Root) ProposeValue(key string, value *cb.ConfigValue) error { } // Channel returns the associated Channel level config -func (r *Root) Channel() *channel.Config { +func (r *Root) Channel() *ChannelGroup { return r.channel } // Orderer returns the associated Orderer level config func (r *Root) Orderer() *orderer.ManagerImpl { - return r.orderer + return r.channel.OrdererConfig() } // Application returns the associated Application level config func (r *Root) Application() *application.SharedConfigImpl { - return r.application + return r.channel.ApplicationConfig() } diff --git a/common/configvalues/root/root_test.go b/common/configvalues/root/root_test.go index 3024fdf707c..250d5672033 100644 --- a/common/configvalues/root/root_test.go +++ b/common/configvalues/root/root_test.go @@ -19,7 +19,6 @@ package config import ( "testing" - "github.com/hyperledger/fabric/common/configvalues/channel" "github.com/hyperledger/fabric/common/configvalues/msp" logging "github.com/op/go-logging" @@ -33,11 +32,11 @@ func init() { func TestBeginBadRoot(t *testing.T) { r := NewRoot(&msp.MSPConfigHandler{}) - _, err := r.BeginValueProposals([]string{channel.GroupKey, channel.GroupKey}) + _, err := r.BeginValueProposals([]string{ChannelGroupKey, ChannelGroupKey}) assert.Error(t, err, "Only one root element allowed") _, err = r.BeginValueProposals([]string{"foo"}) - assert.Error(t, err, "Non %s group not allowed", channel.GroupKey) + assert.Error(t, err, "Non %s group not allowed", ChannelGroupKey) } func TestProposeValue(t *testing.T) { diff --git a/common/mocks/configtx/configtx.go b/common/mocks/configtx/configtx.go index ee6f226fb76..f3a570ac2de 100644 --- a/common/mocks/configtx/configtx.go +++ b/common/mocks/configtx/configtx.go @@ -18,7 +18,7 @@ package configtx import ( configvaluesapi "github.com/hyperledger/fabric/common/configvalues" - configvalueschannel "github.com/hyperledger/fabric/common/configvalues/channel" + config "github.com/hyperledger/fabric/common/configvalues/root" mockpolicies "github.com/hyperledger/fabric/common/mocks/policies" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/msp" @@ -30,7 +30,7 @@ type Resources struct { PolicyManagerVal *mockpolicies.Manager // ChannelConfigVal is returned as the result of ChannelConfig() - ChannelConfigVal configvalueschannel.ConfigReader + ChannelConfigVal config.ChannelValues // OrdererConfigVal is returned as the result of OrdererConfig() OrdererConfigVal configvaluesapi.Orderer @@ -48,7 +48,7 @@ func (r *Resources) PolicyManager() policies.Manager { } // Returns the ChannelConfigVal -func (r *Resources) ChannelConfig() configvalueschannel.ConfigReader { +func (r *Resources) ChannelConfig() config.ChannelValues { return r.ChannelConfigVal } diff --git a/common/mocks/configvalues/channel/sharedconfig_test.go b/common/mocks/configvalues/channel/sharedconfig_test.go index 842edc73e81..e78debd6602 100644 --- a/common/mocks/configvalues/channel/sharedconfig_test.go +++ b/common/mocks/configvalues/channel/sharedconfig_test.go @@ -19,9 +19,9 @@ package channel import ( "testing" - configvalueschannel "github.com/hyperledger/fabric/common/configvalues/channel" + config "github.com/hyperledger/fabric/common/configvalues/root" ) func TestChainConfigInterface(t *testing.T) { - _ = configvalueschannel.ConfigReader(&SharedConfig{}) + _ = config.ChannelValues(&SharedConfig{}) } diff --git a/orderer/multichain/systemchain_test.go b/orderer/multichain/systemchain_test.go index dd07509bda6..75643415d36 100644 --- a/orderer/multichain/systemchain_test.go +++ b/orderer/multichain/systemchain_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/hyperledger/fabric/common/configtx" + configtxtest "github.com/hyperledger/fabric/common/configtx/test" "github.com/hyperledger/fabric/common/configtx/tool/provisional" configvaluesapi "github.com/hyperledger/fabric/common/configvalues" mockconfigvaluesorderer "github.com/hyperledger/fabric/common/mocks/configvalues/channel/orderer" @@ -74,7 +75,7 @@ func TestGoodProposal(t *testing.T) { mcc.ms.msc.ChainCreationPolicyNamesVal = []string{provisional.AcceptAllPolicyKey} mcc.ms.mpm.Policy = &mockpolicies.Policy{} - configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtx.NewCompositeTemplate()).Envelope(newChainID) + configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtxtest.CompositeTemplate()).Envelope(newChainID) if err != nil { t.Fatalf("Error constructing configtx") } diff --git a/peer/node/start.go b/peer/node/start.go index 7a8a50e65a1..24b4c35a8a0 100644 --- a/peer/node/start.go +++ b/peer/node/start.go @@ -30,9 +30,9 @@ import ( "github.com/hyperledger/fabric/common/configtx" "github.com/hyperledger/fabric/common/configtx/test" - configtxchannel "github.com/hyperledger/fabric/common/configvalues/channel" "github.com/hyperledger/fabric/common/configvalues/channel/application" "github.com/hyperledger/fabric/common/configvalues/msp" + config "github.com/hyperledger/fabric/common/configvalues/root" "github.com/hyperledger/fabric/common/genesis" "github.com/hyperledger/fabric/common/localmsp" "github.com/hyperledger/fabric/common/policies" @@ -193,7 +193,7 @@ func serve(args []string) error { block, err := genesis.NewFactoryImpl( configtx.NewCompositeTemplate( test.ApplicationOrgTemplate(), - configtx.NewSimpleTemplate(configtxchannel.TemplateOrdererAddresses([]string{orderingEndpoint})), + configtx.NewSimpleTemplate(config.TemplateOrdererAddresses([]string{orderingEndpoint})), policyTemplate)).Block(chainID) if nil != err {