github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/common/configtx/update.go (about)

     1  /*
     2  Copyright IBM Corp. 2016-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  
    22  	"github.com/hyperledger/fabric/common/policies"
    23  	cb "github.com/hyperledger/fabric/protos/common"
    24  	"github.com/hyperledger/fabric/protos/utils"
    25  )
    26  
    27  func (c *configSet) verifyReadSet(readSet map[string]comparable) error {
    28  	for key, value := range readSet {
    29  		existing, ok := c.configMap[key]
    30  		if !ok {
    31  			return fmt.Errorf("Existing config does not contain element for %s but was in the read set", key)
    32  		}
    33  
    34  		if existing.version() != value.version() {
    35  			return fmt.Errorf("Readset expected key %s at version %d, but got version %d", key, value.version(), existing.version())
    36  		}
    37  	}
    38  	return nil
    39  }
    40  
    41  func ComputeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable {
    42  	result := make(map[string]comparable)
    43  	for key, value := range writeSet {
    44  		readVal, ok := readSet[key]
    45  
    46  		if ok && readVal.version() == value.version() {
    47  			continue
    48  		}
    49  
    50  		// If the key in the readset is a different version, we include it
    51  		// Error checking on the sanity of the update is done against the current config
    52  		result[key] = value
    53  	}
    54  	return result
    55  }
    56  
    57  func (cm *configManager) verifyDeltaSet(deltaSet map[string]comparable, signedData []*cb.SignedData) error {
    58  	if len(deltaSet) == 0 {
    59  		return fmt.Errorf("Delta set was empty.  Update would have no effect.")
    60  	}
    61  
    62  	for key, value := range deltaSet {
    63  		existing, ok := cm.current.configMap[key]
    64  		if !ok {
    65  			if value.version() != 0 {
    66  				return fmt.Errorf("Attempted to set key %s to version %d, but key does not exist", key, value.version())
    67  			} else {
    68  				continue
    69  			}
    70  
    71  		}
    72  		if value.version() != existing.version()+1 {
    73  			return fmt.Errorf("Attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
    74  		}
    75  
    76  		policy, ok := cm.policyForItem(existing)
    77  		if !ok {
    78  			return fmt.Errorf("Unexpected missing policy %s for item %s", existing.modPolicy(), key)
    79  		}
    80  
    81  		// Ensure the policy is satisfied
    82  		if err := policy.Evaluate(signedData); err != nil {
    83  			return fmt.Errorf("Policy for %s not satisfied: %s", key, err)
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
    90  	for key, _ := range writeSet {
    91  		if _, ok := fullProposedConfig[key]; !ok {
    92  			return fmt.Errorf("Writeset contained key %s which did not appear in proposed config", key)
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  // authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
    99  // it returns a map of the modified config
   100  func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
   101  	if configUpdateEnv == nil {
   102  		return nil, fmt.Errorf("Cannot process nil ConfigUpdateEnvelope")
   103  	}
   104  
   105  	configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	if configUpdate.ChannelId != cm.current.channelID {
   111  		return nil, fmt.Errorf("Update not for correct channel: %s for %s", configUpdate.ChannelId, cm.current.channelID)
   112  	}
   113  
   114  	readSet, err := MapConfig(configUpdate.ReadSet)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("Error mapping ReadSet: %s", err)
   117  	}
   118  	err = cm.current.verifyReadSet(readSet)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("Error validating ReadSet: %s", err)
   121  	}
   122  
   123  	writeSet, err := MapConfig(configUpdate.WriteSet)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("Error mapping WriteSet: %s", err)
   126  	}
   127  
   128  	deltaSet := ComputeDeltaSet(readSet, writeSet)
   129  	signedData, err := configUpdateEnv.AsSignedData()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	if err = cm.verifyDeltaSet(deltaSet, signedData); err != nil {
   135  		return nil, fmt.Errorf("Error validating DeltaSet: %s", err)
   136  	}
   137  
   138  	fullProposedConfig := cm.computeUpdateResult(deltaSet)
   139  	if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
   140  		return nil, fmt.Errorf("Full config did not verify: %s", err)
   141  	}
   142  
   143  	return fullProposedConfig, nil
   144  }
   145  
   146  func (cm *configManager) policyForItem(item comparable) (policies.Policy, bool) {
   147  	// path is always at least of length 1
   148  	manager, ok := cm.PolicyManager().Manager(item.path[1:])
   149  	if !ok {
   150  		return nil, ok
   151  	}
   152  
   153  	// In the case of the group type, its key is part of its path for the purposes of finding the policy manager
   154  	if item.ConfigGroup != nil {
   155  		manager, ok = manager.Manager([]string{item.key})
   156  	}
   157  	if !ok {
   158  		return nil, ok
   159  	}
   160  	return manager.GetPolicy(item.modPolicy())
   161  }
   162  
   163  // computeUpdateResult takes a configMap generated by an update and produces a new configMap overlaying it onto the old config
   164  func (cm *configManager) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
   165  	newConfigMap := make(map[string]comparable)
   166  	for key, value := range cm.current.configMap {
   167  		newConfigMap[key] = value
   168  	}
   169  
   170  	for key, value := range updatedConfig {
   171  		newConfigMap[key] = value
   172  	}
   173  	return newConfigMap
   174  }
   175  
   176  func envelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
   177  	configUpdateEnv := &cb.ConfigUpdateEnvelope{}
   178  	_, err := utils.UnmarshalEnvelopeOfType(configtx, cb.HeaderType_CONFIG_UPDATE, configUpdateEnv)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return configUpdateEnv, nil
   183  }