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  }