github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/configtx/validator.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package configtx
     8  
     9  import (
    10  	"regexp"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	cb "github.com/hyperledger/fabric-protos-go/common"
    14  	"github.com/hyperledger/fabric/common/flogging"
    15  	"github.com/hyperledger/fabric/common/policies"
    16  	"github.com/hyperledger/fabric/protoutil"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  var logger = flogging.MustGetLogger("common.configtx")
    21  
    22  // Constraints for valid channel and config IDs
    23  var (
    24  	ChannelAllowedChars = "[a-z][a-z0-9.-]*"
    25  	configAllowedChars  = "[a-zA-Z0-9.-]+"
    26  	MaxLength           = 249
    27  	illegalNames        = map[string]struct{}{
    28  		".":  {},
    29  		"..": {},
    30  	}
    31  )
    32  
    33  // ValidatorImpl implements the Validator interface
    34  type ValidatorImpl struct {
    35  	channelID   string
    36  	sequence    uint64
    37  	configMap   map[string]comparable
    38  	configProto *cb.Config
    39  	namespace   string
    40  	pm          policies.Manager
    41  }
    42  
    43  // validateConfigID makes sure that the config element names (ie map key of
    44  // ConfigGroup) comply with the following restrictions
    45  //      1. Contain only ASCII alphanumerics, dots '.', dashes '-'
    46  //      2. Are shorter than 250 characters.
    47  //      3. Are not the strings "." or "..".
    48  func validateConfigID(configID string) error {
    49  	re, _ := regexp.Compile(configAllowedChars)
    50  	// Length
    51  	if len(configID) <= 0 {
    52  		return errors.New("config ID illegal, cannot be empty")
    53  	}
    54  	if len(configID) > MaxLength {
    55  		return errors.Errorf("config ID illegal, cannot be longer than %d", MaxLength)
    56  	}
    57  	// Illegal name
    58  	if _, ok := illegalNames[configID]; ok {
    59  		return errors.Errorf("name '%s' for config ID is not allowed", configID)
    60  	}
    61  	// Illegal characters
    62  	matched := re.FindString(configID)
    63  	if len(matched) != len(configID) {
    64  		return errors.Errorf("config ID '%s' contains illegal characters", configID)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  // ValidateChannelID makes sure that proposed channel IDs comply with the
    71  // following restrictions:
    72  //      1. Contain only lower case ASCII alphanumerics, dots '.', and dashes '-'
    73  //      2. Are shorter than 250 characters.
    74  //      3. Start with a letter
    75  //
    76  // This is the intersection of the Kafka restrictions and CouchDB restrictions
    77  // with the following exception: '.' is converted to '_' in the CouchDB naming
    78  // This is to accommodate existing channel names with '.', especially in the
    79  // behave tests which rely on the dot notation for their sluggification.
    80  //
    81  // note: this function is a copy of the same in core/tx/endorser/parser.go
    82  //
    83  func ValidateChannelID(channelID string) error {
    84  	re, _ := regexp.Compile(ChannelAllowedChars)
    85  	// Length
    86  	if len(channelID) <= 0 {
    87  		return errors.Errorf("channel ID illegal, cannot be empty")
    88  	}
    89  	if len(channelID) > MaxLength {
    90  		return errors.Errorf("channel ID illegal, cannot be longer than %d", MaxLength)
    91  	}
    92  
    93  	// Illegal characters
    94  	matched := re.FindString(channelID)
    95  	if len(matched) != len(channelID) {
    96  		return errors.Errorf("'%s' contains illegal characters", channelID)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  // NewValidatorImpl constructs a new implementation of the Validator interface.
   103  func NewValidatorImpl(channelID string, config *cb.Config, namespace string, pm policies.Manager) (*ValidatorImpl, error) {
   104  	if config == nil {
   105  		return nil, errors.Errorf("nil config parameter")
   106  	}
   107  
   108  	if config.ChannelGroup == nil {
   109  		return nil, errors.Errorf("nil channel group")
   110  	}
   111  
   112  	if err := ValidateChannelID(channelID); err != nil {
   113  		return nil, errors.Errorf("bad channel ID: %s", err)
   114  	}
   115  
   116  	configMap, err := mapConfig(config.ChannelGroup, namespace)
   117  	if err != nil {
   118  		return nil, errors.Errorf("error converting config to map: %s", err)
   119  	}
   120  
   121  	return &ValidatorImpl{
   122  		namespace:   namespace,
   123  		pm:          pm,
   124  		sequence:    config.Sequence,
   125  		configMap:   configMap,
   126  		channelID:   channelID,
   127  		configProto: config,
   128  	}, nil
   129  }
   130  
   131  // ProposeConfigUpdate takes in an Envelope of type CONFIG_UPDATE and produces a
   132  // ConfigEnvelope to be used as the Envelope Payload Data of a CONFIG message
   133  func (vi *ValidatorImpl) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
   134  	return vi.proposeConfigUpdate(configtx)
   135  }
   136  
   137  func (vi *ValidatorImpl) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
   138  	configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configtx)
   139  	if err != nil {
   140  		return nil, errors.Errorf("error converting envelope to config update: %s", err)
   141  	}
   142  
   143  	configMap, err := vi.authorizeUpdate(configUpdateEnv)
   144  	if err != nil {
   145  		return nil, errors.Errorf("error authorizing update: %s", err)
   146  	}
   147  
   148  	channelGroup, err := configMapToConfig(configMap, vi.namespace)
   149  	if err != nil {
   150  		return nil, errors.Errorf("could not turn configMap back to channelGroup: %s", err)
   151  	}
   152  
   153  	return &cb.ConfigEnvelope{
   154  		Config: &cb.Config{
   155  			Sequence:     vi.sequence + 1,
   156  			ChannelGroup: channelGroup,
   157  		},
   158  		LastUpdate: configtx,
   159  	}, nil
   160  }
   161  
   162  // Validate simulates applying a ConfigEnvelope to become the new config
   163  func (vi *ValidatorImpl) Validate(configEnv *cb.ConfigEnvelope) error {
   164  	if configEnv == nil {
   165  		return errors.Errorf("config envelope is nil")
   166  	}
   167  
   168  	if configEnv.Config == nil {
   169  		return errors.Errorf("config envelope has nil config")
   170  	}
   171  
   172  	if configEnv.Config.Sequence != vi.sequence+1 {
   173  		return errors.Errorf("config currently at sequence %d, cannot validate config at sequence %d", vi.sequence, configEnv.Config.Sequence)
   174  	}
   175  
   176  	configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configEnv.LastUpdate)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	configMap, err := vi.authorizeUpdate(configUpdateEnv)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	channelGroup, err := configMapToConfig(configMap, vi.namespace)
   187  	if err != nil {
   188  		return errors.Errorf("could not turn configMap back to channelGroup: %s", err)
   189  	}
   190  
   191  	// reflect.Equal will not work here, because it considers nil and empty maps as different
   192  	if !proto.Equal(channelGroup, configEnv.Config.ChannelGroup) {
   193  		return errors.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // ChannelID retrieves the channel ID associated with this manager
   200  func (vi *ValidatorImpl) ChannelID() string {
   201  	return vi.channelID
   202  }
   203  
   204  // Sequence returns the sequence number of the config
   205  func (vi *ValidatorImpl) Sequence() uint64 {
   206  	return vi.sequence
   207  }
   208  
   209  // ConfigProto returns the config proto which initialized this Validator
   210  func (vi *ValidatorImpl) ConfigProto() *cb.Config {
   211  	return vi.configProto
   212  }