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  }