Skip to content

Commit

Permalink
[FAB-6141] Add more flexible channel create helper
Browse files Browse the repository at this point in the history
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 <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Oct 13, 2017
1 parent 95b95e9 commit 52dc361
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
67 changes: 67 additions & 0 deletions common/tools/configtxgen/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down
79 changes: 79 additions & 0 deletions common/tools/configtxgen/encoder/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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))
}

0 comments on commit 52dc361

Please sign in to comment.