Unverified Commit 28b11ed4 authored by Jason Yellick's avatar Jason Yellick Committed by Artem Barger
Browse files

FAB-14056 Non-default template channel create txes



This CR adds the ability to use a specified orderer system channel
profile in order to compute the channel creation tx.  This will form the
fundamental building block for specifying a different subset of Raft
nodes for a new channel's consenter set.

Change-Id: I3e7d620c447ff04cd8262f975d31f811532acc4b
Signed-off-by: default avatarJason Yellick <jyellick@us.ibm.com>
parent 59188f1a
......@@ -21,6 +21,8 @@ import (
"github.com/hyperledger/fabric/protos/orderer/etcdraft"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
)
......@@ -320,7 +322,13 @@ func NewApplicationOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup,
Port: int32(anchorPeer.Port),
})
}
addValue(applicationOrgGroup, channelconfig.AnchorPeersValue(anchorProtos), channelconfig.AdminsPolicyKey)
// Avoid adding an unnecessary anchor peers element when one is not required. This helps
// prevent a delta from the orderer system channel when computing more complex channel
// creation transactions
if len(anchorProtos) > 0 {
addValue(applicationOrgGroup, channelconfig.AnchorPeersValue(anchorProtos), channelconfig.AdminsPolicyKey)
}
applicationOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
return applicationOrgGroup, nil
......@@ -420,14 +428,80 @@ func DefaultConfigTemplate(conf *genesisconfig.Profile) (*cb.ConfigGroup, error)
return channelGroup, nil
}
// MakeChannelCreationTransaction is a handy utility function for creating transactions for channel creation
func ConfigTemplateFromGroup(conf *genesisconfig.Profile, cg *cb.ConfigGroup) (*cb.ConfigGroup, error) {
template := proto.Clone(cg).(*cb.ConfigGroup)
if template.Groups == nil {
return nil, errors.Errorf("supplied system channel group has no sub-groups")
}
template.Groups[channelconfig.ApplicationGroupKey] = &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{},
}
consortiums, ok := template.Groups[channelconfig.ConsortiumsGroupKey]
if !ok {
return nil, errors.Errorf("supplied system channel group does not appear to be system channel (missing consortiums group)")
}
if consortiums.Groups == nil {
return nil, errors.Errorf("system channel consortiums group appears to have no consortiums defined")
}
consortium, ok := consortiums.Groups[conf.Consortium]
if !ok {
return nil, errors.Errorf("supplied system channel group is missing '%s' consortium", conf.Consortium)
}
if conf.Application == nil {
return nil, errors.Errorf("supplied channel creation profile does not contain an application section")
}
for _, organization := range conf.Application.Organizations {
var ok bool
template.Groups[channelconfig.ApplicationGroupKey].Groups[organization.Name], ok = consortium.Groups[organization.Name]
if !ok {
return nil, errors.Errorf("consortium %s does not contain member org %s", conf.Consortium, organization.Name)
}
}
delete(template.Groups, channelconfig.ConsortiumsGroupKey)
addValue(template, channelconfig.ConsortiumValue(conf.Consortium), channelconfig.AdminsPolicyKey)
return template, nil
}
// MakeChannelCreationTransaction is a handy utility function for creating transactions for channel creation.
// It assumes the invoker has no system channel context so ignores all but the application section.
func MakeChannelCreationTransaction(channelID string, signer crypto.LocalSigner, conf *genesisconfig.Profile) (*cb.Envelope, error) {
defaultTemplate, err := DefaultConfigTemplate(conf)
template, err := DefaultConfigTemplate(conf)
if err != nil {
return nil, errors.WithMessage(err, "default template config generation failed")
return nil, errors.WithMessage(err, "could not generate default config template")
}
return MakeChannelCreationTransactionFromTemplate(channelID, signer, conf, template)
}
// MakeChannelCreationTransactionWithSystemChannelContext is a utility function for creating channel creation txes.
// It requires a configuration representing the orderer system channel to allow more sophisticated channel creation
// transactions modifying pieces of the configuration like the orderer set.
func MakeChannelCreationTransactionWithSystemChannelContext(channelID string, signer crypto.LocalSigner, conf, systemChannelConf *genesisconfig.Profile) (*cb.Envelope, error) {
cg, err := NewChannelGroup(systemChannelConf)
if err != nil {
return nil, errors.WithMessage(err, "could not parse system channel config")
}
template, err := ConfigTemplateFromGroup(conf, cg)
if err != nil {
return nil, errors.WithMessage(err, "could not create config template")
}
return MakeChannelCreationTransactionFromTemplate(channelID, signer, conf, template)
}
newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, conf, defaultTemplate)
// MakeChannelCreationTransactionFromTemplate creates a transaction for creating a channel. It uses
// the given template to produce the config update set. Usually, the caller will want to invoke
// MakeChannelCreationTransaction or MakeChannelCreationTransactionWithSystemChannelContext.
func MakeChannelCreationTransactionFromTemplate(channelID string, signer crypto.LocalSigner, conf *genesisconfig.Profile, template *cb.ConfigGroup) (*cb.Envelope, error) {
newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, conf, template)
if err != nil {
return nil, errors.Wrap(err, "config update generation failure")
}
......
......@@ -560,6 +560,19 @@ var _ = Describe("Encoder", func() {
Expect(err).To(MatchError("error adding policies to application org group SampleOrg: invalid signature policy rule 'garbage': unrecognized token 'garbage' in policy string"))
})
})
Context("when there are no anchor peers defined", func() {
BeforeEach(func() {
conf.AnchorPeers = nil
})
It("does not encode the anchor peers", func() {
cg, err := encoder.NewApplicationOrgGroup(conf)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Values)).To(Equal(1))
Expect(cg.Values["AnchorPeers"]).To(BeNil())
})
})
})
Describe("ChannelCreationOperations", func() {
......@@ -578,6 +591,12 @@ var _ = Describe("Encoder", func() {
ID: "SampleMSP",
MSPType: "bccsp",
Name: "SampleOrg",
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "some-host",
Port: 1111,
},
},
},
},
Policies: map[string]*genesisconfig.Policy{
......@@ -715,6 +734,13 @@ var _ = Describe("Encoder", func() {
Expect(err).To(MatchError("cannot define a new channel with no Consortium value"))
})
})
Context("when an update cannot be computed", func() {
It("returns an error", func() {
_, err := encoder.NewChannelCreateConfigUpdate("channel-id", conf, nil)
Expect(err).To(MatchError("could not compute update: no channel group included for original config"))
})
})
})
Describe("MakeChannelCreationTransaction", func() {
......@@ -751,7 +777,7 @@ var _ = Describe("Encoder", func() {
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransaction("channel-id", fakeSigner, conf)
Expect(err).To(MatchError("default template config generation failed: channel template configs must contain an application section"))
Expect(err).To(MatchError("could not generate default config template: channel template configs must contain an application section"))
})
})
......@@ -796,6 +822,109 @@ var _ = Describe("Encoder", func() {
})
})
Describe("MakeChannelCreationTransactionWithSystemChannelContext", func() {
var (
applicationConf *genesisconfig.Profile
sysChannelConf *genesisconfig.Profile
)
BeforeEach(func() {
applicationConf = &genesisconfig.Profile{
Consortium: "SampleConsortium",
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
},
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org1MSP",
MSPType: "bccsp",
Name: "Org1",
AnchorPeers: []*genesisconfig.AnchorPeer{
{
Host: "my-peer",
Port: 5555,
},
},
},
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org2MSP",
MSPType: "bccsp",
Name: "Org2",
},
},
},
}
sysChannelConf = &genesisconfig.Profile{
Orderer: &genesisconfig.Orderer{
OrdererType: "kafka",
},
Consortiums: map[string]*genesisconfig.Consortium{
"SampleConsortium": {
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org1MSP",
MSPType: "bccsp",
Name: "Org1",
},
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org2MSP",
MSPType: "bccsp",
Name: "Org2",
},
},
},
},
}
})
It("returns an encoded and signed tx including differences from the system channel", func() {
env, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).NotTo(HaveOccurred())
payload := &cb.Payload{}
err = proto.Unmarshal(env.Payload, payload)
Expect(err).NotTo(HaveOccurred())
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
err = proto.Unmarshal(payload.Data, configUpdateEnv)
Expect(err).NotTo(HaveOccurred())
configUpdate := &cb.ConfigUpdate{}
err = proto.Unmarshal(configUpdateEnv.ConfigUpdate, configUpdate)
Expect(err).NotTo(HaveOccurred())
Expect(configUpdate.WriteSet.Version).To(Equal(uint64(0)))
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Version).To(Equal(uint64(1)))
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org1"].Values["AnchorPeers"]).NotTo(BeNil())
Expect(configUpdate.WriteSet.Groups["Application"].Groups["Org2"].Version).To(Equal(uint64(0)))
Expect(configUpdate.WriteSet.Groups["Orderer"].Values["ConsensusType"].Version).To(Equal(uint64(1)))
})
Context("when the system channel config is bad", func() {
BeforeEach(func() {
sysChannelConf.Orderer.OrdererType = "garbage"
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).To(MatchError("could not parse system channel config: could not create orderer group: unknown orderer type: garbage"))
})
})
Context("when the template cannot be computed", func() {
BeforeEach(func() {
applicationConf.Application = nil
})
It("wraps and returns the error", func() {
_, err := encoder.MakeChannelCreationTransactionWithSystemChannelContext("channel-id", nil, applicationConf, sysChannelConf)
Expect(err).To(MatchError("could not create config template: supplied channel creation profile does not contain an application section"))
})
})
})
Describe("DefaultConfigTemplate", func() {
var (
conf *genesisconfig.Profile
......@@ -864,6 +993,133 @@ var _ = Describe("Encoder", func() {
})
})
})
Describe("ConfigTemplateFromGroup", func() {
var (
applicationConf *genesisconfig.Profile
sysChannelGroup *cb.ConfigGroup
)
BeforeEach(func() {
applicationConf = &genesisconfig.Profile{
Consortium: "SampleConsortium",
Orderer: &genesisconfig.Orderer{
OrdererType: "solo",
},
Application: &genesisconfig.Application{
Organizations: []*genesisconfig.Organization{
{Name: "Org1"},
{Name: "Org2"},
},
},
}
var err error
sysChannelGroup, err = encoder.NewChannelGroup(&genesisconfig.Profile{
Orderer: &genesisconfig.Orderer{
OrdererType: "kafka",
},
Consortiums: map[string]*genesisconfig.Consortium{
"SampleConsortium": {
Organizations: []*genesisconfig.Organization{
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org1MSP",
MSPType: "bccsp",
Name: "Org1",
},
{
MSPDir: "../../../../sampleconfig/msp",
ID: "Org2MSP",
MSPType: "bccsp",
Name: "Org2",
},
},
},
},
})
Expect(err).NotTo(HaveOccurred())
})
It("returns a config tempalte", func() {
cg, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).NotTo(HaveOccurred())
Expect(len(cg.Groups)).To(Equal(2))
Expect(cg.Groups["Orderer"]).NotTo(BeNil())
Expect(proto.Equal(cg.Groups["Orderer"], sysChannelGroup.Groups["Orderer"])).To(BeTrue())
Expect(cg.Groups["Application"]).NotTo(BeNil())
Expect(cg.Groups["Application"].Policies).To(BeEmpty())
Expect(cg.Groups["Application"].Values).To(BeEmpty())
Expect(len(cg.Groups["Application"].Groups)).To(Equal(2))
})
Context("when the orderer system channel group has no sub-groups", func() {
BeforeEach(func() {
sysChannelGroup.Groups = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group has no sub-groups"))
})
})
Context("when the orderer system channel group has no consortiums group", func() {
BeforeEach(func() {
delete(sysChannelGroup.Groups, "Consortiums")
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group does not appear to be system channel (missing consortiums group)"))
})
})
Context("when the orderer system channel group has no consortiums in the consortiums group", func() {
BeforeEach(func() {
sysChannelGroup.Groups["Consortiums"].Groups = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("system channel consortiums group appears to have no consortiums defined"))
})
})
Context("when the orderer system channel group does not have the requested consortium", func() {
BeforeEach(func() {
applicationConf.Consortium = "bad-consortium"
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied system channel group is missing 'bad-consortium' consortium"))
})
})
Context("when the channel creation profile has no application section", func() {
BeforeEach(func() {
applicationConf.Application = nil
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("supplied channel creation profile does not contain an application section"))
})
})
Context("when the orderer system channel group does not have all the channel creation orgs", func() {
BeforeEach(func() {
delete(sysChannelGroup.Groups["Consortiums"].Groups["SampleConsortium"].Groups, "Org1")
})
It("returns an error", func() {
_, err := encoder.ConfigTemplateFromGroup(applicationConf, sysChannelGroup)
Expect(err).To(MatchError("consortium SampleConsortium does not contain member org Org1"))
})
})
})
})
Describe("Bootstrapper", func() {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment