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