github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  	for key, value := range deltaSet {
    59  		existing, ok := cm.current.configMap[key]
    60  		if !ok {
    61  			if value.version() != 0 {
    62  				return fmt.Errorf("Attempted to set key %s to version %d, but key does not exist", key, value.version())
    63  			} else {
    64  				continue
    65  			}
    66  
    67  		}
    68  		if value.version() != existing.version()+1 {
    69  			return fmt.Errorf("Attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
    70  		}
    71  
    72  		policy, ok := cm.policyForItem(existing)
    73  		if !ok {
    74  			return fmt.Errorf("Unexpected missing policy %s for item %s", existing.modPolicy(), key)
    75  		}
    76  
    77  		// Ensure the policy is satisfied
    78  		if err := policy.Evaluate(signedData); err != nil {
    79  			return fmt.Errorf("Policy for %s not satisfied: %s", key, err)
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
    86  	for key, _ := range writeSet {
    87  		if _, ok := fullProposedConfig[key]; !ok {
    88  			return fmt.Errorf("Writeset contained key %s which did not appear in proposed config", key)
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  // authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
    95  // it returns a map of the modified config
    96  func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
    97  	if configUpdateEnv == nil {
    98  		return nil, fmt.Errorf("Cannot process nil ConfigUpdateEnvelope")
    99  	}
   100  
   101  	configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if configUpdate.ChannelId != cm.current.channelID {
   107  		return nil, fmt.Errorf("Update not for correct channel: %s for %s", configUpdate.ChannelId, cm.current.channelID)
   108  	}
   109  
   110  	readSet, err := MapConfig(configUpdate.ReadSet)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("Error mapping ReadSet: %s", err)
   113  	}
   114  	err = cm.current.verifyReadSet(readSet)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("Error validating ReadSet: %s", err)
   117  	}
   118  
   119  	writeSet, err := MapConfig(configUpdate.WriteSet)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("Error mapping WriteSet: %s", err)
   122  	}
   123  
   124  	deltaSet := ComputeDeltaSet(readSet, writeSet)
   125  	signedData, err := configUpdateEnv.AsSignedData()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	if err = cm.verifyDeltaSet(deltaSet, signedData); err != nil {
   131  		return nil, fmt.Errorf("Error validating DeltaSet: %s", err)
   132  	}
   133  
   134  	fullProposedConfig := cm.computeUpdateResult(deltaSet)
   135  	if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
   136  		return nil, fmt.Errorf("Full config did not verify: %s", err)
   137  	}
   138  
   139  	return fullProposedConfig, nil
   140  }
   141  
   142  func (cm *configManager) policyForItem(item comparable) (policies.Policy, bool) {
   143  	// path is always at least of length 1
   144  	manager, ok := cm.PolicyManager().Manager(item.path[1:])
   145  	if !ok {
   146  		return nil, ok
   147  	}
   148  
   149  	// In the case of the group type, its key is part of its path for the purposes of finding the policy manager
   150  	if item.ConfigGroup != nil {
   151  		manager, ok = manager.Manager([]string{item.key})
   152  	}
   153  	if !ok {
   154  		return nil, ok
   155  	}
   156  	return manager.GetPolicy(item.modPolicy())
   157  }
   158  
   159  // computeUpdateResult takes a configMap generated by an update and produces a new configMap overlaying it onto the old config
   160  func (cm *configManager) computeUpdateResult(updatedConfig map[string]comparable) map[string]comparable {
   161  	newConfigMap := make(map[string]comparable)
   162  	for key, value := range cm.current.configMap {
   163  		newConfigMap[key] = value
   164  	}
   165  
   166  	for key, value := range updatedConfig {
   167  		newConfigMap[key] = value
   168  	}
   169  	return newConfigMap
   170  }
   171  
   172  func envelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
   173  	configUpdateEnv := &cb.ConfigUpdateEnvelope{}
   174  	_, err := utils.UnmarshalEnvelopeOfType(configtx, cb.HeaderType_CONFIG_UPDATE, configUpdateEnv)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	return configUpdateEnv, nil
   179  }