github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/common/configtx/config.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 "bytes" 21 "fmt" 22 23 "github.com/hyperledger/fabric/common/config" 24 "github.com/hyperledger/fabric/common/configtx/api" 25 "github.com/hyperledger/fabric/common/policies" 26 cb "github.com/hyperledger/fabric/protos/common" 27 28 "github.com/golang/protobuf/jsonpb" 29 "github.com/golang/protobuf/proto" 30 ) 31 32 type ConfigResult interface { 33 JSON() string 34 } 35 36 func NewConfigResult(config *cb.ConfigGroup, proposer api.Proposer) (ConfigResult, error) { 37 return processConfig(config, proposer) 38 } 39 40 type configResult struct { 41 tx interface{} 42 groupName string 43 groupKey string 44 group *cb.ConfigGroup 45 valueHandler config.ValueProposer 46 policyHandler policies.Proposer 47 subResults []*configResult 48 deserializedValues map[string]proto.Message 49 deserializedPolicies map[string]proto.Message 50 } 51 52 func (cr *configResult) JSON() string { 53 var buffer bytes.Buffer 54 buffer.WriteString("{") 55 cr.subResults[0].bufferJSON(&buffer) 56 buffer.WriteString("}") 57 return buffer.String() 58 59 } 60 61 // bufferJSON takes a buffer and writes a JSON representation of the configResult into the buffer 62 // Note that we use some mildly ad-hoc JSON encoding because the proto documentation explicitly 63 // mentions that the encoding/json package does not correctly marshal proto objects, and we 64 // do not have a proto object (nor can one be defined) which presents the mixed-map style of 65 // keys mapping to different types of the config 66 func (cr *configResult) bufferJSON(buffer *bytes.Buffer) { 67 jpb := &jsonpb.Marshaler{ 68 EmitDefaults: true, 69 Indent: " ", 70 } 71 72 // "GroupName": { 73 buffer.WriteString("\"") 74 buffer.WriteString(cr.groupKey) 75 buffer.WriteString("\": {") 76 77 // "Values": { 78 buffer.WriteString("\"Values\": {") 79 count := 0 80 for key, value := range cr.group.Values { 81 // "Key": { 82 buffer.WriteString("\"") 83 buffer.WriteString(key) 84 buffer.WriteString("\": {") 85 // "Version": "X", 86 buffer.WriteString("\"Version\":\"") 87 buffer.WriteString(fmt.Sprintf("%d", value.Version)) 88 buffer.WriteString("\",") 89 // "ModPolicy": "foo", 90 buffer.WriteString("\"ModPolicy\":\"") 91 buffer.WriteString(value.ModPolicy) 92 buffer.WriteString("\",") 93 // "Value": protoAsJSON 94 buffer.WriteString("\"Value\":") 95 jpb.Marshal(buffer, cr.deserializedValues[key]) 96 // }, 97 buffer.WriteString("}") 98 count++ 99 if count < len(cr.group.Values) { 100 buffer.WriteString(",") 101 } 102 } 103 // }, 104 buffer.WriteString("},") 105 106 count = 0 107 // "Policies": { 108 buffer.WriteString("\"Policies\": {") 109 for key, policy := range cr.group.Policies { 110 // "Key": { 111 buffer.WriteString("\"") 112 buffer.WriteString(key) 113 buffer.WriteString("\": {") 114 // "Version": "X", 115 buffer.WriteString("\"Version\":\"") 116 buffer.WriteString(fmt.Sprintf("%d", policy.Version)) 117 buffer.WriteString("\",") 118 // "ModPolicy": "foo", 119 buffer.WriteString("\"ModPolicy\":\"") 120 buffer.WriteString(policy.ModPolicy) 121 buffer.WriteString("\",") 122 // "Policy": { 123 buffer.WriteString("\"Policy\":{") 124 // "PolicyType" : 125 buffer.WriteString(fmt.Sprintf("\"PolicyType\":\"%d\",", policy.Policy.Type)) 126 // "Policy" : policyAsJSON 127 buffer.WriteString("\"Policy\":") 128 jpb.Marshal(buffer, cr.deserializedPolicies[key]) 129 // } 130 // }, 131 buffer.WriteString("}}") 132 count++ 133 if count < len(cr.group.Policies) { 134 buffer.WriteString(",") 135 } 136 } 137 // }, 138 buffer.WriteString("},") 139 140 // "Groups": { 141 count = 0 142 buffer.WriteString("\"Groups\": {") 143 for _, subResult := range cr.subResults { 144 subResult.bufferJSON(buffer) 145 count++ 146 if count < len(cr.subResults) { 147 buffer.WriteString(",") 148 } 149 } 150 // } 151 buffer.WriteString("}") 152 153 // } 154 buffer.WriteString("}") 155 } 156 157 func (cr *configResult) preCommit() error { 158 for _, subResult := range cr.subResults { 159 err := subResult.preCommit() 160 if err != nil { 161 return err 162 } 163 } 164 return cr.valueHandler.PreCommit(cr.tx) 165 } 166 167 func (cr *configResult) commit() { 168 for _, subResult := range cr.subResults { 169 subResult.commit() 170 } 171 cr.valueHandler.CommitProposals(cr.tx) 172 cr.policyHandler.CommitProposals(cr.tx) 173 } 174 175 func (cr *configResult) rollback() { 176 for _, subResult := range cr.subResults { 177 subResult.rollback() 178 } 179 cr.valueHandler.RollbackProposals(cr.tx) 180 cr.policyHandler.RollbackProposals(cr.tx) 181 } 182 183 // proposeGroup proposes a group configuration with a given handler 184 // it will in turn recursively call itself until all groups have been exhausted 185 // at each call, it updates the configResult to contain references to the handlers 186 // which have been invoked so that calling result.commit() or result.rollback() will 187 // appropriately cleanup 188 func proposeGroup(result *configResult) error { 189 subGroups := make([]string, len(result.group.Groups)) 190 i := 0 191 for subGroup := range result.group.Groups { 192 subGroups[i] = subGroup 193 i++ 194 } 195 196 valueDeserializer, subValueHandlers, err := result.valueHandler.BeginValueProposals(result.tx, subGroups) 197 if err != nil { 198 return err 199 } 200 201 subPolicyHandlers, err := result.policyHandler.BeginPolicyProposals(result.tx, subGroups) 202 if err != nil { 203 return err 204 } 205 206 if len(subValueHandlers) != len(subGroups) || len(subPolicyHandlers) != len(subGroups) { 207 return fmt.Errorf("Programming error, did not return as many handlers as groups %d vs %d vs %d", len(subValueHandlers), len(subGroups), len(subPolicyHandlers)) 208 } 209 210 for key, value := range result.group.Values { 211 msg, err := valueDeserializer.Deserialize(key, value.Value) 212 if err != nil { 213 result.rollback() 214 return fmt.Errorf("Error deserializing key %s for group %s: %s", key, result.groupName, err) 215 } 216 result.deserializedValues[key] = msg 217 } 218 219 for key, policy := range result.group.Policies { 220 policy, err := result.policyHandler.ProposePolicy(result.tx, key, policy) 221 if err != nil { 222 result.rollback() 223 return err 224 } 225 result.deserializedPolicies[key] = policy 226 } 227 228 result.subResults = make([]*configResult, 0, len(subGroups)) 229 230 for i, subGroup := range subGroups { 231 result.subResults = append(result.subResults, &configResult{ 232 tx: result.tx, 233 groupKey: subGroup, 234 groupName: result.groupName + "/" + subGroup, 235 group: result.group.Groups[subGroup], 236 valueHandler: subValueHandlers[i], 237 policyHandler: subPolicyHandlers[i], 238 deserializedValues: make(map[string]proto.Message), 239 deserializedPolicies: make(map[string]proto.Message), 240 }) 241 242 if err := proposeGroup(result.subResults[i]); err != nil { 243 result.rollback() 244 return err 245 } 246 } 247 248 return nil 249 } 250 251 func processConfig(channelGroup *cb.ConfigGroup, proposer api.Proposer) (*configResult, error) { 252 helperGroup := cb.NewConfigGroup() 253 helperGroup.Groups[RootGroupKey] = channelGroup 254 255 configResult := &configResult{ 256 group: helperGroup, 257 valueHandler: proposer.ValueProposer(), 258 policyHandler: proposer.PolicyProposer(), 259 } 260 err := proposeGroup(configResult) 261 if err != nil { 262 return nil, err 263 } 264 265 return configResult, nil 266 } 267 268 func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) { 269 logger.Debugf("Beginning new config for channel %s", cm.current.channelID) 270 configResult, err := processConfig(channelGroup, cm.initializer) 271 if err != nil { 272 return nil, err 273 } 274 275 err = configResult.preCommit() 276 if err != nil { 277 configResult.rollback() 278 return nil, err 279 } 280 281 return configResult, nil 282 }