From 6ac2f0ebf696a997d3be73306d1895f52d9b6eb0 Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Fri, 17 Mar 2017 13:57:40 -0400 Subject: [PATCH] [FAB-2821] Print readset/writeset/deltaset on updt The configtxgen tool currently only prints the organizations in the channel creation configtx. This is not enough information for an admin to decide whether to sign or not. This CR makes the configtx printing much verbose, including the read set, write set, and computes the delta set for easy identification of non-overlap between the read and write sets. Change-Id: I3f9bfc2839ec595b1f2fc9a0cb149797ed4f862b Signed-off-by: Jason Yellick Signed-off-by: Kostas Christidis --- common/configtx/config.go | 25 +++++---- common/configtx/config_test.go | 10 ++-- common/configtx/configmap.go | 8 +-- common/configtx/configmap_test.go | 4 +- common/configtx/manager.go | 2 +- common/configtx/tool/configtxgen/main.go | 65 ++++++++++++++++++------ common/configtx/update.go | 8 +-- common/configtx/update_test.go | 2 +- 8 files changed, 84 insertions(+), 40 deletions(-) diff --git a/common/configtx/config.go b/common/configtx/config.go index 85a3595cad2..a7e43f31841 100644 --- a/common/configtx/config.go +++ b/common/configtx/config.go @@ -40,6 +40,7 @@ func NewConfigResult(config *cb.ConfigGroup, proposer api.Proposer) (ConfigResul type configResult struct { tx interface{} groupName string + groupKey string group *cb.ConfigGroup valueHandler config.ValueProposer policyHandler policies.Proposer @@ -51,7 +52,7 @@ type configResult struct { func (cr *configResult) JSON() string { var buffer bytes.Buffer buffer.WriteString("{") - cr.bufferJSON(&buffer) + cr.subResults[0].bufferJSON(&buffer) buffer.WriteString("}") return buffer.String() @@ -70,7 +71,7 @@ func (cr *configResult) bufferJSON(buffer *bytes.Buffer) { // "GroupName": { buffer.WriteString("\"") - buffer.WriteString(cr.groupName) + buffer.WriteString(cr.groupKey) buffer.WriteString("\": {") // "Values": { @@ -229,6 +230,7 @@ func proposeGroup(result *configResult) error { for i, subGroup := range subGroups { result.subResults = append(result.subResults, &configResult{ tx: result.tx, + groupKey: subGroup, groupName: result.groupName + "/" + subGroup, group: result.group.Groups[subGroup], valueHandler: subValueHandlers[i], @@ -243,12 +245,6 @@ func proposeGroup(result *configResult) error { } } - err = result.preCommit() - if err != nil { - result.rollback() - return err - } - return nil } @@ -271,5 +267,16 @@ func processConfig(channelGroup *cb.ConfigGroup, proposer api.Proposer) (*config func (cm *configManager) processConfig(channelGroup *cb.ConfigGroup) (*configResult, error) { logger.Debugf("Beginning new config for channel %s", cm.current.channelID) - return processConfig(channelGroup, cm.initializer) + configResult, err := processConfig(channelGroup, cm.initializer) + if err != nil { + return nil, err + } + + err = configResult.preCommit() + if err != nil { + configResult.rollback() + return nil, err + } + + return configResult, nil } diff --git a/common/configtx/config_test.go b/common/configtx/config_test.go index 3010272db01..5e68aa83045 100644 --- a/common/configtx/config_test.go +++ b/common/configtx/config_test.go @@ -31,7 +31,7 @@ import ( func TestJSON(t *testing.T) { cr := &configResult{ - groupName: "rootGroup", + groupKey: "rootGroup", group: &cb.ConfigGroup{ Values: map[string]*cb.ConfigValue{ "outer": &cb.ConfigValue{Version: 1, ModPolicy: "mod1"}, @@ -39,7 +39,7 @@ func TestJSON(t *testing.T) { }, subResults: []*configResult{ &configResult{ - groupName: "innerGroup1", + groupKey: "innerGroup1", group: &cb.ConfigGroup{ Values: map[string]*cb.ConfigValue{ "inner1": &cb.ConfigValue{ModPolicy: "mod3"}, @@ -56,7 +56,7 @@ func TestJSON(t *testing.T) { }, }, &configResult{ - groupName: "innerGroup2", + groupKey: "innerGroup2", group: &cb.ConfigGroup{ Values: map[string]*cb.ConfigValue{ "inner2": &cb.ConfigValue{ModPolicy: "mod3"}, @@ -78,8 +78,10 @@ func TestJSON(t *testing.T) { }, } + crWrapper := &configResult{subResults: []*configResult{cr}} + buffer := &bytes.Buffer{} - assert.NoError(t, json.Indent(buffer, []byte(cr.JSON()), "", ""), "JSON should parse nicely") + assert.NoError(t, json.Indent(buffer, []byte(crWrapper.JSON()), "", ""), "JSON should parse nicely") expected := "{\"rootGroup\":{\"Values\":{\"outer\":{\"Version\":\"1\",\"ModPolicy\":\"mod1\",\"Value\":{\"type\":\"outer\"}}},\"Policies\":{},\"Groups\":{\"innerGroup1\":{\"Values\":{\"inner1\":{\"Version\":\"0\",\"ModPolicy\":\"mod3\",\"Value\":{\"type\":\"inner1\"}}},\"Policies\":{\"policy1\":{\"Version\":\"0\",\"ModPolicy\":\"mod1\",\"Policy\":{\"PolicyType\":\"0\",\"Policy\":{\"type\":\"policy1\"}}}},\"Groups\":{}},\"innerGroup2\":{\"Values\":{\"inner2\":{\"Version\":\"0\",\"ModPolicy\":\"mod3\",\"Value\":{\"type\":\"inner2\"}}},\"Policies\":{\"policy2\":{\"Version\":\"0\",\"ModPolicy\":\"mod2\",\"Policy\":{\"PolicyType\":\"1\",\"Policy\":{\"type\":\"policy2\"}}}},\"Groups\":{}}}}}" diff --git a/common/configtx/configmap.go b/common/configtx/configmap.go index bdf4f8b03f1..398b5c37e63 100644 --- a/common/configtx/configmap.go +++ b/common/configtx/configmap.go @@ -33,9 +33,9 @@ const ( PathSeparator = "/" ) -// mapConfig is intended to be called outside this file +// MapConfig is intended to be called outside this file // it takes a ConfigGroup and generates a map of fqPath to comparables (or error on invalid keys) -func mapConfig(channelGroup *cb.ConfigGroup) (map[string]comparable, error) { +func MapConfig(channelGroup *cb.ConfigGroup) (map[string]comparable, error) { result := make(map[string]comparable) if channelGroup != nil { err := recurseConfig(result, []string{RootGroupKey}, channelGroup) @@ -46,7 +46,7 @@ func mapConfig(channelGroup *cb.ConfigGroup) (map[string]comparable, error) { return result, nil } -// addToMap is used only internally by mapConfig +// addToMap is used only internally by MapConfig func addToMap(cg comparable, result map[string]comparable) error { var fqPath string @@ -77,7 +77,7 @@ func addToMap(cg comparable, result map[string]comparable) error { return nil } -// recurseConfig is used only internally by mapConfig +// recurseConfig is used only internally by MapConfig func recurseConfig(result map[string]comparable, path []string, group *cb.ConfigGroup) error { if err := addToMap(comparable{key: path[len(path)-1], path: path[:len(path)-1], ConfigGroup: group}, result); err != nil { return err diff --git a/common/configtx/configmap_test.go b/common/configtx/configmap_test.go index d884abafc42..776360290da 100644 --- a/common/configtx/configmap_test.go +++ b/common/configtx/configmap_test.go @@ -38,7 +38,7 @@ func TestConfigMap(t *testing.T) { config.Groups["0DeepGroup"].Groups["1DeepGroup"] = cb.NewConfigGroup() config.Groups["0DeepGroup"].Groups["1DeepGroup"].Values["2DeepValue"] = &cb.ConfigValue{} - confMap, err := mapConfig(config) + confMap, err := MapConfig(config) assert.NoError(t, err, "Should not have errored building map") assert.Len(t, confMap, 7, "There should be 7 entries in the config map") @@ -68,7 +68,7 @@ func TestMapConfigBack(t *testing.T) { config.Groups["0DeepGroup"].Groups["1DeepGroup"] = cb.NewConfigGroup() config.Groups["0DeepGroup"].Groups["1DeepGroup"].Values["2DeepValue"] = &cb.ConfigValue{} - confMap, err := mapConfig(config) + confMap, err := MapConfig(config) assert.NoError(t, err, "Should not have errored building map") newConfig, err := configMapToConfig(confMap) diff --git a/common/configtx/manager.go b/common/configtx/manager.go index c31e1be4897..a982f4305fd 100644 --- a/common/configtx/manager.go +++ b/common/configtx/manager.go @@ -102,7 +102,7 @@ func NewManagerImpl(envConfig *cb.Envelope, initializer api.Initializer, callOnU return nil, fmt.Errorf("Bad channel id: %s", err) } - configMap, err := mapConfig(configEnv.Config.ChannelGroup) + configMap, err := MapConfig(configEnv.Config.ChannelGroup) if err != nil { return nil, fmt.Errorf("Error converting config to map: %s", err) } diff --git a/common/configtx/tool/configtxgen/main.go b/common/configtx/tool/configtxgen/main.go index dcfa382491a..ccec611209a 100644 --- a/common/configtx/tool/configtxgen/main.go +++ b/common/configtx/tool/configtxgen/main.go @@ -195,21 +195,29 @@ func doInspectBlock(inspectBlock string) error { return fmt.Errorf("ConfigEnvelope contained no config") } - configResult, err := configtx.NewConfigResult(configEnvelope.Config.ChannelGroup, configtx.NewInitializer()) + configAsJSON, err := configGroupAsJSON(configEnvelope.Config.ChannelGroup) if err != nil { - return fmt.Errorf("Error parsing configuration: %s", err) + return err + } + + fmt.Printf("Config for channel: %s at sequence %d\n", header.ChannelId, configEnvelope.Config.Sequence) + fmt.Println(configAsJSON) + + return nil +} + +func configGroupAsJSON(group *cb.ConfigGroup) (string, error) { + configResult, err := configtx.NewConfigResult(group, configtx.NewInitializer()) + if err != nil { + return "", fmt.Errorf("Error parsing config: %s", err) } buffer := &bytes.Buffer{} err = json.Indent(buffer, []byte(configResult.JSON()), "", " ") if err != nil { - return fmt.Errorf("Error in output JSON (usually a programming bug): %s", err) + return "", fmt.Errorf("Error in output JSON (usually a programming bug): %s", err) } - - fmt.Printf("Config for channel: %s at sequence %d\n", header.ChannelId, configEnvelope.Config.Sequence) - - fmt.Println(buffer.String()) - return nil + return buffer.String(), nil } func doInspectChannelCreateTx(inspectChannelCreateTx string) error { @@ -253,21 +261,48 @@ func doInspectChannelCreateTx(inspectChannelCreateTx string) error { return fmt.Errorf("ConfigUpdateEnvelope was for different channel than envelope: %s vs %s", configUpdate.ChannelId, header.ChannelId) } + fmt.Printf("\nChannel creation for channel: %s\n", header.ChannelId) + fmt.Println() + + if configUpdate.ReadSet == nil { + fmt.Println("Read Set: empty") + } else { + fmt.Println("Read Set:") + readSetAsJSON, err := configGroupAsJSON(configUpdate.ReadSet) + if err != nil { + return err + } + fmt.Println(readSetAsJSON) + } + fmt.Println() + if configUpdate.WriteSet == nil { return fmt.Errorf("Empty WriteSet") } - if configUpdate.WriteSet.Groups[config.ApplicationGroupKey] == nil { - return fmt.Errorf("Empty Application group") + fmt.Println("Write Set:") + writeSetAsJSON, err := configGroupAsJSON(configUpdate.WriteSet) + if err != nil { + return err } + fmt.Println(writeSetAsJSON) + fmt.Println() - var orgs []string - - for name := range configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups { - orgs = append(orgs, name) + readSetMap, err := configtx.MapConfig(configUpdate.ReadSet) + if err != nil { + return fmt.Errorf("Error mapping read set: %s", err) + } + writeSetMap, err := configtx.MapConfig(configUpdate.WriteSet) + if err != nil { + return fmt.Errorf("Error mapping write set: %s", err) } - fmt.Printf("\nChannel creation for channel: %s with orgs %v\n\n", header.ChannelId, orgs) + fmt.Println("Delta Set:") + deltaSet := configtx.ComputeDeltaSet(readSetMap, writeSetMap) + for key := range deltaSet { + fmt.Println(key) + } + fmt.Println() return nil } diff --git a/common/configtx/update.go b/common/configtx/update.go index cba78fa727b..bdb3b6f4797 100644 --- a/common/configtx/update.go +++ b/common/configtx/update.go @@ -38,7 +38,7 @@ func (c *configSet) verifyReadSet(readSet map[string]comparable) error { return nil } -func computeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable { +func ComputeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable { result := make(map[string]comparable) for key, value := range writeSet { readVal, ok := readSet[key] @@ -107,7 +107,7 @@ func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelop return nil, fmt.Errorf("Update not for correct channel: %s for %s", configUpdate.ChannelId, cm.current.channelID) } - readSet, err := mapConfig(configUpdate.ReadSet) + readSet, err := MapConfig(configUpdate.ReadSet) if err != nil { return nil, fmt.Errorf("Error mapping ReadSet: %s", err) } @@ -116,12 +116,12 @@ func (cm *configManager) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelop return nil, fmt.Errorf("Error validating ReadSet: %s", err) } - writeSet, err := mapConfig(configUpdate.WriteSet) + writeSet, err := MapConfig(configUpdate.WriteSet) if err != nil { return nil, fmt.Errorf("Error mapping WriteSet: %s", err) } - deltaSet := computeDeltaSet(readSet, writeSet) + deltaSet := ComputeDeltaSet(readSet, writeSet) signedData, err := configUpdateEnv.AsSignedData() if err != nil { return nil, err diff --git a/common/configtx/update_test.go b/common/configtx/update_test.go index 4f85f77ea4e..cf3922fe051 100644 --- a/common/configtx/update_test.go +++ b/common/configtx/update_test.go @@ -66,7 +66,7 @@ func TestComputeDeltaSet(t *testing.T) { writeSet["2"] = comparable{ConfigValue: &cb.ConfigValue{Version: 1}} writeSet["3"] = comparable{} - result := computeDeltaSet(readSet, writeSet) + result := ComputeDeltaSet(readSet, writeSet) assert.Len(t, result, 2, "Should have two values in the delta set") assert.NotNil(t, result["2"], "Element had version increased") assert.NotNil(t, result["3"], "Element was new")