Skip to content

Commit

Permalink
[FAB-2821] Print readset/writeset/deltaset on updt
Browse files Browse the repository at this point in the history
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 <jyellick@us.ibm.com>
Signed-off-by: Kostas Christidis <kostas@christidis.io>
  • Loading branch information
Jason Yellick authored and kchristidis committed Apr 26, 2017
1 parent 3fb58c8 commit 6ac2f0e
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 40 deletions.
25 changes: 16 additions & 9 deletions common/configtx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()

Expand All @@ -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": {
Expand Down Expand Up @@ -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],
Expand All @@ -243,12 +245,6 @@ func proposeGroup(result *configResult) error {
}
}

err = result.preCommit()
if err != nil {
result.rollback()
return err
}

return nil
}

Expand All @@ -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
}
10 changes: 6 additions & 4 deletions common/configtx/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ 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"},
},
},
subResults: []*configResult{
&configResult{
groupName: "innerGroup1",
groupKey: "innerGroup1",
group: &cb.ConfigGroup{
Values: map[string]*cb.ConfigValue{
"inner1": &cb.ConfigValue{ModPolicy: "mod3"},
Expand All @@ -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"},
Expand All @@ -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\":{}}}}}"

Expand Down
8 changes: 4 additions & 4 deletions common/configtx/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions common/configtx/configmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion common/configtx/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
65 changes: 50 additions & 15 deletions common/configtx/tool/configtxgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down
8 changes: 4 additions & 4 deletions common/configtx/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion common/configtx/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 6ac2f0e

Please sign in to comment.