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 }