github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/common/configtx/manager.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package configtx 18 19 import ( 20 "fmt" 21 "reflect" 22 "regexp" 23 24 "github.com/hyperledger/fabric/common/configtx/api" 25 "github.com/hyperledger/fabric/common/flogging" 26 cb "github.com/hyperledger/fabric/protos/common" 27 "github.com/hyperledger/fabric/protos/utils" 28 ) 29 30 var logger = flogging.MustGetLogger("common/configtx") 31 32 // Constraints for valid chain IDs 33 var ( 34 allowedChars = "[a-zA-Z0-9.-]+" 35 maxLength = 249 36 illegalNames = map[string]struct{}{ 37 ".": struct{}{}, 38 "..": struct{}{}, 39 } 40 ) 41 42 type configSet struct { 43 channelID string 44 sequence uint64 45 configMap map[string]comparable 46 configEnv *cb.ConfigEnvelope 47 } 48 49 type configManager struct { 50 api.Resources 51 callOnUpdate []func(api.Manager) 52 initializer api.Initializer 53 current *configSet 54 } 55 56 // validateChainID makes sure that proposed chain IDs (i.e. channel names) 57 // comply with the following restrictions: 58 // 1. Contain only ASCII alphanumerics, dots '.', dashes '-' 59 // 2. Are shorter than 250 characters. 60 // 3. Are not the strings "." or "..". 61 // 62 // Our hand here is forced by: 63 // https://github.com/apache/kafka/blob/trunk/core/src/main/scala/kafka/common/Topic.scala#L29 64 func validateChainID(chainID string) error { 65 re, _ := regexp.Compile(allowedChars) 66 // Length 67 if len(chainID) <= 0 { 68 return fmt.Errorf("chain ID illegal, cannot be empty") 69 } 70 if len(chainID) > maxLength { 71 return fmt.Errorf("chain ID illegal, cannot be longer than %d", maxLength) 72 } 73 // Illegal name 74 if _, ok := illegalNames[chainID]; ok { 75 return fmt.Errorf("name '%s' for chain ID is not allowed", chainID) 76 } 77 // Illegal characters 78 matched := re.FindString(chainID) 79 if len(matched) != len(chainID) { 80 return fmt.Errorf("Chain ID '%s' contains illegal characters", chainID) 81 } 82 83 return nil 84 } 85 86 func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnUpdate []func(api.Manager)) (api.Manager, error) { 87 if envConfig == nil { 88 return nil, fmt.Errorf("Nil envelope") 89 } 90 91 configEnv := &cb.ConfigEnvelope{} 92 header, err := utils.UnmarshalEnvelopeOfType(envConfig, cb.HeaderType_CONFIG, configEnv) 93 if err != nil { 94 return nil, fmt.Errorf("Bad envelope: %s", err) 95 } 96 97 if configEnv.Config == nil { 98 return nil, fmt.Errorf("Nil config envelope Config") 99 } 100 101 if err := validateChainID(header.ChannelId); err != nil { 102 return nil, fmt.Errorf("Bad channel id: %s", err) 103 } 104 105 configMap, err := MapConfig(configEnv.Config.ChannelGroup) 106 if err != nil { 107 return nil, fmt.Errorf("Error converting config to map: %s", err) 108 } 109 110 cm := &configManager{ 111 Resources: initializer, 112 initializer: initializer, 113 current: &configSet{ 114 sequence: configEnv.Config.Sequence, 115 configMap: configMap, 116 channelID: header.ChannelId, 117 configEnv: configEnv, 118 }, 119 callOnUpdate: callOnUpdate, 120 } 121 122 result, err := cm.processConfig(configEnv.Config.ChannelGroup) 123 if err != nil { 124 return nil, err 125 } 126 result.commit() 127 cm.commitCallbacks() 128 129 return cm, nil 130 } 131 132 func (cm *configManager) commitCallbacks() { 133 for _, callback := range cm.callOnUpdate { 134 callback(cm) 135 } 136 } 137 138 // ProposeConfigUpdate takes in an Envelope of type CONFIG_UPDATE and produces a 139 // ConfigEnvelope to be used as the Envelope Payload Data of a CONFIG message 140 func (cm *configManager) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) { 141 return cm.proposeConfigUpdate(configtx) 142 } 143 144 func (cm *configManager) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) { 145 configUpdateEnv, err := envelopeToConfigUpdate(configtx) 146 if err != nil { 147 return nil, fmt.Errorf("Error converting envelope to config update: %s", err) 148 } 149 150 configMap, err := cm.authorizeUpdate(configUpdateEnv) 151 if err != nil { 152 return nil, fmt.Errorf("Error authorizing update: %s", err) 153 } 154 155 channelGroup, err := configMapToConfig(configMap) 156 if err != nil { 157 return nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) 158 } 159 160 result, err := cm.processConfig(channelGroup) 161 if err != nil { 162 return nil, fmt.Errorf("Error processing updated config: %s", err) 163 } 164 165 result.rollback() 166 167 return &cb.ConfigEnvelope{ 168 Config: &cb.Config{ 169 Sequence: cm.current.sequence + 1, 170 ChannelGroup: channelGroup, 171 }, 172 LastUpdate: configtx, 173 }, nil 174 } 175 176 func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) { 177 if configEnv == nil { 178 return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope") 179 } 180 181 if configEnv.Config == nil { 182 return nil, nil, fmt.Errorf("Config cannot be nil") 183 } 184 185 if configEnv.Config.Sequence != cm.current.sequence+1 { 186 return nil, nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence) 187 } 188 189 configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate) 190 if err != nil { 191 return nil, nil, err 192 } 193 194 configMap, err := cm.authorizeUpdate(configUpdateEnv) 195 if err != nil { 196 return nil, nil, err 197 } 198 199 channelGroup, err := configMapToConfig(configMap) 200 if err != nil { 201 return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err) 202 } 203 204 if !reflect.DeepEqual(channelGroup, configEnv.Config.ChannelGroup) { 205 return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result") 206 } 207 208 result, err := cm.processConfig(channelGroup) 209 if err != nil { 210 return nil, nil, err 211 } 212 213 return configMap, result, nil 214 } 215 216 // Validate simulates applying a ConfigEnvelope to become the new config 217 func (cm *configManager) Validate(configEnv *cb.ConfigEnvelope) error { 218 _, result, err := cm.prepareApply(configEnv) 219 if err != nil { 220 return err 221 } 222 223 result.rollback() 224 225 return nil 226 } 227 228 // Apply attempts to apply a ConfigEnvelope to become the new config 229 func (cm *configManager) Apply(configEnv *cb.ConfigEnvelope) error { 230 configMap, result, err := cm.prepareApply(configEnv) 231 if err != nil { 232 return err 233 } 234 235 result.commit() 236 237 cm.current = &configSet{ 238 configMap: configMap, 239 channelID: cm.current.channelID, 240 sequence: configEnv.Config.Sequence, 241 configEnv: configEnv, 242 } 243 244 cm.commitCallbacks() 245 246 return nil 247 } 248 249 // ChainID retrieves the chain ID associated with this manager 250 func (cm *configManager) ChainID() string { 251 return cm.current.channelID 252 } 253 254 // Sequence returns the current sequence number of the config 255 func (cm *configManager) Sequence() uint64 { 256 return cm.current.sequence 257 } 258 259 // ConfigEnvelope returns the current config envelope 260 func (cm *configManager) ConfigEnvelope() *cb.ConfigEnvelope { 261 return cm.current.configEnv 262 }