From 52dc3615c735edff730fb1055e59046f5769432c Mon Sep 17 00:00:00 2001 From: Jason Yellick Date: Tue, 12 Sep 2017 23:30:39 -0400 Subject: [PATCH] [FAB-6141] Add more flexible channel create helper The original channel creation tx generation code assumes that no modifications have been made to the organization definitions in the ordering system channel. For longer running systems this assumption may not hold. Consequently, a new function which accepts the ordering system channel's config is required so that it can set the correct versions for the organization's in the channel creation transaction. This CR adds this new function. Change-Id: Iebeb247bbea5c688a14c6133f793373da7357c9b Signed-off-by: Jason Yellick --- common/tools/configtxgen/encoder/encoder.go | 67 ++++++++++++++++ .../tools/configtxgen/encoder/encoder_test.go | 79 +++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/common/tools/configtxgen/encoder/encoder.go b/common/tools/configtxgen/encoder/encoder.go index bd6712e9a83..3ff9111fcde 100644 --- a/common/tools/configtxgen/encoder/encoder.go +++ b/common/tools/configtxgen/encoder/encoder.go @@ -18,6 +18,7 @@ import ( pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" + "github.com/golang/protobuf/proto" "github.com/pkg/errors" ) @@ -274,6 +275,72 @@ func NewConsortiumGroup(conf *genesisconfig.Consortium) (*cb.ConfigGroup, error) return consortiumGroup, nil } +// NewChannelCreateConfigUpdate generates a ConfigUpdate which can be sent to the orderer to create a new channel. Optionally, the channel group of the +// ordering system channel may be passed in, and the resulting ConfigUpdate will extract the appropriate versions from this file. +func NewChannelCreateConfigUpdate(channelID, consortiumName string, orgs []string, orderingSystemChannelGroup *cb.ConfigGroup) (*cb.ConfigUpdate, error) { + var applicationGroup *cb.ConfigGroup + channelGroupVersion := uint64(0) + + if orderingSystemChannelGroup != nil { + // In the case that a ordering system channel definition was provided, pull the appropriate versions + if orderingSystemChannelGroup.Groups == nil { + return nil, errors.New("missing all channel groups") + } + + consortiums, ok := orderingSystemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey] + if !ok { + return nil, errors.New("bad consortiums group") + } + + consortium, ok := consortiums.Groups[consortiumName] + if !ok || (len(orgs) > 0 && consortium.Groups == nil) { + return nil, errors.Errorf("bad consortium: %s", consortiumName) + } + + applicationGroup = cb.NewConfigGroup() + for _, org := range orgs { + orgGroup, ok := consortium.Groups[org] + if !ok { + return nil, errors.Errorf("missing organization: %s", org) + } + applicationGroup.Groups[org] = &cb.ConfigGroup{Version: orgGroup.Version} + } + + channelGroupVersion = orderingSystemChannelGroup.Version + } else { + // Otherwise assume the orgs have not been modified + applicationGroup = cb.NewConfigGroup() + for _, org := range orgs { + applicationGroup.Groups[org] = &cb.ConfigGroup{} + } + } + + rSet := cb.NewConfigGroup() + rSet.Version = channelGroupVersion + + // add the consortium name to the rSet + + addValue(rSet, channelconfig.ConsortiumValue(consortiumName), "") // TODO, this emulates the old behavior, but is it desirable? + + // create the new channel's application group + + rSet.Groups[channelconfig.ApplicationGroupKey] = applicationGroup + + wSet := proto.Clone(rSet).(*cb.ConfigGroup) + + applicationGroup = wSet.Groups[channelconfig.ApplicationGroupKey] + applicationGroup.Version = 1 + applicationGroup.Policies = make(map[string]*cb.ConfigPolicy) + addImplicitMetaPolicyDefaults(applicationGroup) + applicationGroup.ModPolicy = channelconfig.AdminsPolicyKey + + return &cb.ConfigUpdate{ + ChannelId: channelID, + ReadSet: rSet, + WriteSet: wSet, + }, nil +} + // Bootstrapper is a wrapper around NewChannelConfigGroup which can produce genesis blocks type Bootstrapper struct { channelGroup *cb.ConfigGroup diff --git a/common/tools/configtxgen/encoder/encoder_test.go b/common/tools/configtxgen/encoder/encoder_test.go index f28c455765b..9f4be94c106 100644 --- a/common/tools/configtxgen/encoder/encoder_test.go +++ b/common/tools/configtxgen/encoder/encoder_test.go @@ -10,10 +10,12 @@ import ( "testing" "github.com/hyperledger/fabric/common/channelconfig" + "github.com/hyperledger/fabric/common/configtx" "github.com/hyperledger/fabric/common/flogging" genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig" cb "github.com/hyperledger/fabric/protos/common" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" ) @@ -61,3 +63,80 @@ func TestConfigParsing(t *testing.T) { }) } } + +func TestGoodChannelCreateConfigUpdate(t *testing.T) { + config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile) + group, err := NewChannelGroup(config) + assert.NoError(t, err) + assert.NotNil(t, group) + + configUpdate, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, group) + assert.NoError(t, err) + assert.NotNil(t, configUpdate) + + defaultConfigUpdate, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, nil) + assert.NoError(t, err) + assert.NotNil(t, configUpdate) + + assert.True(t, proto.Equal(configUpdate, defaultConfigUpdate), "the config used has had no updates, so should equal default") +} + +func TestNegativeChannelCreateConfigUpdate(t *testing.T) { + config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile) + group, err := NewChannelGroup(config) + assert.NoError(t, err) + assert.NotNil(t, group) + + t.Run("NoGroups", func(t *testing.T) { + channelGroup := proto.Clone(group).(*cb.ConfigGroup) + channelGroup.Groups = nil + _, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup) + assert.Error(t, err) + assert.Regexp(t, "missing all channel groups", err.Error()) + }) + + t.Run("NoConsortiumsGroup", func(t *testing.T) { + channelGroup := proto.Clone(group).(*cb.ConfigGroup) + delete(channelGroup.Groups, channelconfig.ConsortiumsGroupKey) + _, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup) + assert.Error(t, err) + assert.Regexp(t, "bad consortiums group", err.Error()) + }) + + t.Run("NoConsortiums", func(t *testing.T) { + channelGroup := proto.Clone(group).(*cb.ConfigGroup) + delete(channelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups, genesisconfig.SampleConsortiumName) + _, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup) + assert.Error(t, err) + assert.Regexp(t, "bad consortium:", err.Error()) + }) + + t.Run("MissingOrg", func(t *testing.T) { + channelGroup := proto.Clone(group).(*cb.ConfigGroup) + _, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName + ".wrong"}, channelGroup) + assert.Error(t, err) + assert.Regexp(t, "missing organization:", err.Error()) + }) +} + +// This is a temporary test to make sure that the newly implement channel creation method properly replicates +// the old behavior +func TestCompatability(t *testing.T) { + config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile) + group, err := NewChannelGroup(config) + assert.NoError(t, err) + assert.NotNil(t, group) + + channelID := "channel.id" + orgs := []string{genesisconfig.SampleOrgName} + configUpdate, err := NewChannelCreateConfigUpdate(channelID, genesisconfig.SampleConsortiumName, orgs, group) + assert.NoError(t, err) + assert.NotNil(t, configUpdate) + + template := channelconfig.NewChainCreationTemplate(genesisconfig.SampleConsortiumName, orgs) + configEnv, err := template.Envelope(channelID) + assert.NoError(t, err) + oldUpdate := configtx.UnmarshalConfigUpdateOrPanic(configEnv.ConfigUpdate) + oldUpdate.IsolatedData = nil + assert.True(t, proto.Equal(oldUpdate, configUpdate)) +}