github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/common/configtx/update.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  	"strings"
    11  
    12  	cb "github.com/hyperledger/fabric-protos-go/common"
    13  	"github.com/hyperledger/fabric/common/policies"
    14  	"github.com/hyperledger/fabric/protoutil"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  func (vi *ValidatorImpl) verifyReadSet(readSet map[string]comparable) error {
    19  	for key, value := range readSet {
    20  		existing, ok := vi.configMap[key]
    21  		if !ok {
    22  			return errors.Errorf("existing config does not contain element for %s but was in the read set", key)
    23  		}
    24  
    25  		if existing.version() != value.version() {
    26  			return errors.Errorf("proposed update requires that key %s be at version %d, but it is currently at version %d", key, value.version(), existing.version())
    27  		}
    28  	}
    29  	return nil
    30  }
    31  
    32  func computeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable {
    33  	result := make(map[string]comparable)
    34  	for key, value := range writeSet {
    35  		readVal, ok := readSet[key]
    36  
    37  		if ok && readVal.version() == value.version() {
    38  			continue
    39  		}
    40  
    41  		// If the key in the readset is a different version, we include it
    42  		// Error checking on the sanity of the update is done against the config
    43  		result[key] = value
    44  	}
    45  	return result
    46  }
    47  
    48  func validateModPolicy(modPolicy string) error {
    49  	if modPolicy == "" {
    50  		return errors.Errorf("mod_policy not set")
    51  	}
    52  
    53  	trimmed := modPolicy
    54  	if modPolicy[0] == '/' {
    55  		trimmed = modPolicy[1:]
    56  	}
    57  
    58  	for i, pathElement := range strings.Split(trimmed, pathSeparator) {
    59  		err := validateConfigID(pathElement)
    60  		if err != nil {
    61  			return errors.Wrapf(err, "path element at %d is invalid", i)
    62  		}
    63  	}
    64  	return nil
    65  
    66  }
    67  
    68  func (vi *ValidatorImpl) verifyDeltaSet(deltaSet map[string]comparable, signedData []*protoutil.SignedData) error {
    69  	if len(deltaSet) == 0 {
    70  		return errors.Errorf("delta set was empty -- update would have no effect")
    71  	}
    72  
    73  	for key, value := range deltaSet {
    74  		logger.Debugf("Processing change to key: %s", key)
    75  		if err := validateModPolicy(value.modPolicy()); err != nil {
    76  			return errors.Wrapf(err, "invalid mod_policy for element %s", key)
    77  		}
    78  
    79  		existing, ok := vi.configMap[key]
    80  		if !ok {
    81  			if value.version() != 0 {
    82  				return errors.Errorf("attempted to set key %s to version %d, but key does not exist", key, value.version())
    83  			}
    84  
    85  			continue
    86  		}
    87  		if value.version() != existing.version()+1 {
    88  			return errors.Errorf("attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
    89  		}
    90  
    91  		policy, ok := vi.policyForItem(existing)
    92  		if !ok {
    93  			return errors.Errorf("unexpected missing policy %s for item %s", existing.modPolicy(), key)
    94  		}
    95  
    96  		// Ensure the policy is satisfied
    97  		if err := policy.EvaluateSignedData(signedData); err != nil {
    98  			return errors.Wrapf(err, "policy for %s not satisfied", key)
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
   105  	for key := range writeSet {
   106  		if _, ok := fullProposedConfig[key]; !ok {
   107  			return errors.Errorf("writeset contained key %s which did not appear in proposed config", key)
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  // authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
   114  // it returns a map of the modified config
   115  func (vi *ValidatorImpl) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
   116  	if configUpdateEnv == nil {
   117  		return nil, errors.Errorf("cannot process nil ConfigUpdateEnvelope")
   118  	}
   119  
   120  	configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	if configUpdate.ChannelId != vi.channelID {
   126  		return nil, errors.Errorf("ConfigUpdate for channel '%s' but envelope for channel '%s'", configUpdate.ChannelId, vi.channelID)
   127  	}
   128  
   129  	readSet, err := mapConfig(configUpdate.ReadSet, vi.namespace)
   130  	if err != nil {
   131  		return nil, errors.Wrapf(err, "error mapping ReadSet")
   132  	}
   133  	err = vi.verifyReadSet(readSet)
   134  	if err != nil {
   135  		return nil, errors.Wrapf(err, "error validating ReadSet")
   136  	}
   137  
   138  	writeSet, err := mapConfig(configUpdate.WriteSet, vi.namespace)
   139  	if err != nil {
   140  		return nil, errors.Wrapf(err, "error mapping WriteSet")
   141  	}
   142  
   143  	deltaSet := computeDeltaSet(readSet, writeSet)
   144  	signedData, err := protoutil.ConfigUpdateEnvelopeAsSignedData(configUpdateEnv)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	if err = vi.verifyDeltaSet(deltaSet, signedData); err != nil {
   150  		return nil, errors.Wrapf(err, "error validating DeltaSet")
   151  	}
   152  
   153  	fullProposedConfig := vi.computeUpdateResult(deltaSet)
   154  	if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
   155  		return nil, errors.Wrapf(err, "full config did not verify")
   156  	}
   157  
   158  	return fullProposedConfig, nil
   159  }
   160  
   161  func (vi *ValidatorImpl) policyForItem(item comparable) (policies.Policy, bool) {
   162  	manager := vi.pm
   163  
   164  	modPolicy := item.modPolicy()
   165  	logger.Debugf("Getting policy for item %s with mod_policy %s", item.key, modPolicy)
   166  
   167  	// If the mod_policy path is relative, get the right manager for the context
   168  	// If the item has a zero length path, it is the root group, use the base policy manager
   169  	// if the mod_policy path is absolute (starts with /) also use the base policy manager
   170  	if len(modPolicy) > 0 && modPolicy[0] != policies.PathSeparator[0] && len(item.path) != 0 {
   171  		var ok bool
   172  
   173  		manager, ok = manager.Manager(item.path[1:])
   174  		if !ok {
   175  			logger.Debugf("Could not find manager at path: %v", item.path[1:])
   176  			return nil, ok
   177  		}
   178  
   179  		// In the case of the group type, its key is part of its path for the purposes of finding the policy manager
   180  		if item.ConfigGroup != nil {
   181  			manager, ok = manager.Manager([]string{item.key})
   182  		}
   183  		if !ok {
   184  			logger.Debugf("Could not find group at subpath: %v", item.key)
   185  			return nil, ok
   186  		}
   187  	}
   188  
   189  	return manager.GetPolicy(item.modPolicy())
   190  }
   191  
   192  // computeUpdateResult takes a configMap generated by an update and produces a new configMap overlaying it onto the old config
   193  func (vi *ValidatorImpl) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
   194  	newConfigMap := make(map[string]comparable)
   195  	for key, value := range vi.configMap {
   196  		newConfigMap[key] = value
   197  	}
   198  
   199  	for key, value := range updatedConfig {
   200  		newConfigMap[key] = value
   201  	}
   202  	return newConfigMap
   203  }