Skip to content

Commit 810f7ba

Browse files
author
Jason Yellick
committed
[FAB-6139] Add new channel config encoder package
configtxgen currently depends on the 'provisional' package for encoding the information contained in configtx.yaml into the fabric native proto format. The provisional package was originally developed as part of the orderer testing framework, and was never designed to work as it does today. This CR introduces a new encoder which more directly translates between the viper config struct and the native proto format. It relies on much less indirection and should be easier for users to understand, easier to hack on, and already has significantly better test coverage. In addition, the new encoder allows for encoding of a subset of the configuration, which is very useful for tests and tools. There are many places in fabric codebase which use the provisional package to generate a genesis block, then unpack the the block, unpack the envelope, unpack the config envelope, and finally access the config inside, and the new package allows producing the config directly without these intermediate steps. This CR simply introduces the new encoder and tests. It utilizes the new simple util functions from the channelconfig and policies packages. In subsequent CRs, the old provisional package will be removed, as will the assorted old 'Template<...>' methods it depended upon. Change-Id: I827504af301631171fc5a5998637b31e99c8dc4b Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent 79c2b99 commit 810f7ba

File tree

3 files changed

+361
-1
lines changed

3 files changed

+361
-1
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package encoder
8+
9+
import (
10+
"github.com/hyperledger/fabric/common/cauthdsl"
11+
"github.com/hyperledger/fabric/common/channelconfig"
12+
"github.com/hyperledger/fabric/common/flogging"
13+
"github.com/hyperledger/fabric/common/policies"
14+
genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
15+
"github.com/hyperledger/fabric/msp"
16+
cb "github.com/hyperledger/fabric/protos/common"
17+
pb "github.com/hyperledger/fabric/protos/peer"
18+
"github.com/hyperledger/fabric/protos/utils"
19+
20+
"github.com/pkg/errors"
21+
)
22+
23+
const (
24+
pkgLogID = "common/tools/configtxgen/encoder"
25+
ordererAdminsPolicyName = "/Channel/Orderer/Admins"
26+
)
27+
28+
var logger = flogging.MustGetLogger(pkgLogID)
29+
30+
func init() {
31+
flogging.SetModuleLevel(pkgLogID, "info")
32+
}
33+
34+
const (
35+
// ConsensusTypeSolo identifies the solo consensus implementation.
36+
ConsensusTypeSolo = "solo"
37+
// ConsensusTypeKafka identifies the Kafka-based consensus implementation.
38+
ConsensusTypeKafka = "kafka"
39+
40+
// BlockValidationPolicyKey TODO
41+
BlockValidationPolicyKey = "BlockValidation"
42+
43+
// OrdererAdminsPolicy is the absolute path to the orderer admins policy
44+
OrdererAdminsPolicy = "/Channel/Orderer/Admins"
45+
)
46+
47+
func addValue(cg *cb.ConfigGroup, value channelconfig.ConfigValue, modPolicy string) {
48+
cg.Values[value.Key()] = &cb.ConfigValue{
49+
Value: utils.MarshalOrPanic(value.Value()),
50+
ModPolicy: modPolicy,
51+
}
52+
}
53+
54+
func addPolicy(cg *cb.ConfigGroup, policy policies.ConfigPolicy, modPolicy string) {
55+
cg.Policies[policy.Key()] = &cb.ConfigPolicy{
56+
Policy: policy.Value(),
57+
ModPolicy: modPolicy,
58+
}
59+
}
60+
61+
// addImplicitMetaPolicyDefaults adds the Readers/Writers/Admins policies, with Any/Any/Majority rules respectively.
62+
func addImplicitMetaPolicyDefaults(cg *cb.ConfigGroup) {
63+
addPolicy(cg, policies.ImplicitMetaMajorityPolicy(channelconfig.AdminsPolicyKey), channelconfig.AdminsPolicyKey)
64+
addPolicy(cg, policies.ImplicitMetaAnyPolicy(channelconfig.ReadersPolicyKey), channelconfig.AdminsPolicyKey)
65+
addPolicy(cg, policies.ImplicitMetaAnyPolicy(channelconfig.WritersPolicyKey), channelconfig.AdminsPolicyKey)
66+
}
67+
68+
// addSignaturePolicyDefaults adds the Readers/Writers/Admins policies as signature policies requiring one signature from the given mspID.
69+
// If devMode is set to true, the Admins policy will accept arbitrary user certs for admin functions, otherwise it requires the cert satisfies
70+
// the admin role principal.
71+
func addSignaturePolicyDefaults(cg *cb.ConfigGroup, mspID string, devMode bool) {
72+
if devMode {
73+
addPolicy(cg, policies.SignaturePolicy(channelconfig.AdminsPolicyKey, cauthdsl.SignedByMspMember(mspID)), channelconfig.AdminsPolicyKey)
74+
} else {
75+
addPolicy(cg, policies.SignaturePolicy(channelconfig.AdminsPolicyKey, cauthdsl.SignedByMspAdmin(mspID)), channelconfig.AdminsPolicyKey)
76+
}
77+
addPolicy(cg, policies.SignaturePolicy(channelconfig.ReadersPolicyKey, cauthdsl.SignedByMspMember(mspID)), channelconfig.AdminsPolicyKey)
78+
addPolicy(cg, policies.SignaturePolicy(channelconfig.WritersPolicyKey, cauthdsl.SignedByMspMember(mspID)), channelconfig.AdminsPolicyKey)
79+
}
80+
81+
// NewChannelGroup defines the root of the channel configuration. It defines basic operating principles like the hashing
82+
// algorithm used for the blocks, as well as the location of the ordering service. It will recursively call into the
83+
// NewOrdererGroup, NewConsortiumsGroup, and NewApplicationGroup depending on whether these sub-elements are set in the
84+
// configuration. All mod_policy values are set to "Admins" for this group, with the exception of the OrdererAddresses
85+
// value which is set to "/Channel/Orderer/Admins".
86+
func NewChannelGroup(conf *genesisconfig.Profile) (*cb.ConfigGroup, error) {
87+
if conf.Orderer == nil {
88+
return nil, errors.New("missing orderer config section")
89+
}
90+
91+
channelGroup := cb.NewConfigGroup()
92+
addImplicitMetaPolicyDefaults(channelGroup)
93+
addValue(channelGroup, channelconfig.HashingAlgorithmValue(), channelconfig.AdminsPolicyKey)
94+
addValue(channelGroup, channelconfig.BlockDataHashingStructureValue(), channelconfig.AdminsPolicyKey)
95+
addValue(channelGroup, channelconfig.OrdererAddressesValue(conf.Orderer.Addresses), ordererAdminsPolicyName)
96+
97+
if conf.Consortium != "" {
98+
addValue(channelGroup, channelconfig.ConsortiumValue(conf.Consortium), channelconfig.AdminsPolicyKey)
99+
}
100+
101+
if len(conf.Capabilities) > 0 {
102+
addValue(channelGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
103+
}
104+
105+
var err error
106+
channelGroup.Groups[channelconfig.OrdererGroupKey], err = NewOrdererGroup(conf.Orderer)
107+
if err != nil {
108+
return nil, errors.Wrap(err, "could not create orderer group")
109+
}
110+
111+
if conf.Application != nil {
112+
channelGroup.Groups[channelconfig.ApplicationGroupKey], err = NewApplicationGroup(conf.Application)
113+
if err != nil {
114+
return nil, errors.Wrap(err, "could not create application group")
115+
}
116+
}
117+
118+
if conf.Consortiums != nil {
119+
channelGroup.Groups[channelconfig.ConsortiumsGroupKey], err = NewConsortiumsGroup(conf.Consortiums)
120+
if err != nil {
121+
return nil, errors.Wrap(err, "could not create consortiums group")
122+
}
123+
}
124+
125+
channelGroup.ModPolicy = channelconfig.AdminsPolicyKey
126+
return channelGroup, nil
127+
}
128+
129+
// NewOrdererGroup returns the orderer component of the channel configuration. It defines parameters of the ordering service
130+
// about how large blocks should be, how frequently they should be emitted, etc. as well as the organizations of the ordering network.
131+
// It sets the mod_policy of all elements to "Admins". This group is always present in any channel configuration.
132+
func NewOrdererGroup(conf *genesisconfig.Orderer) (*cb.ConfigGroup, error) {
133+
ordererGroup := cb.NewConfigGroup()
134+
addImplicitMetaPolicyDefaults(ordererGroup)
135+
ordererGroup.Policies[BlockValidationPolicyKey] = &cb.ConfigPolicy{
136+
Policy: policies.ImplicitMetaAnyPolicy(channelconfig.WritersPolicyKey).Value(),
137+
ModPolicy: channelconfig.AdminsPolicyKey,
138+
}
139+
addValue(ordererGroup, channelconfig.ConsensusTypeValue(conf.OrdererType), channelconfig.AdminsPolicyKey)
140+
addValue(ordererGroup, channelconfig.BatchSizeValue(
141+
conf.BatchSize.MaxMessageCount,
142+
conf.BatchSize.AbsoluteMaxBytes,
143+
conf.BatchSize.PreferredMaxBytes,
144+
), channelconfig.AdminsPolicyKey)
145+
addValue(ordererGroup, channelconfig.BatchTimeoutValue(conf.BatchTimeout.String()), channelconfig.AdminsPolicyKey)
146+
addValue(ordererGroup, channelconfig.ChannelRestrictionsValue(conf.MaxChannels), channelconfig.AdminsPolicyKey)
147+
148+
if len(conf.Capabilities) > 0 {
149+
addValue(ordererGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
150+
}
151+
152+
switch conf.OrdererType {
153+
case ConsensusTypeSolo:
154+
case ConsensusTypeKafka:
155+
addValue(ordererGroup, channelconfig.KafkaBrokersValue(conf.Kafka.Brokers), channelconfig.AdminsPolicyKey)
156+
default:
157+
return nil, errors.Errorf("unknown orderer type: %s", conf.OrdererType)
158+
}
159+
160+
for _, org := range conf.Organizations {
161+
var err error
162+
ordererGroup.Groups[org.Name], err = NewOrdererOrgGroup(org)
163+
if err != nil {
164+
return nil, errors.Wrap(err, "failed to create orderer org")
165+
}
166+
}
167+
168+
ordererGroup.ModPolicy = channelconfig.AdminsPolicyKey
169+
return ordererGroup, nil
170+
}
171+
172+
// NewOrdererOrgGroup returns an orderer org component of the channel configuration. It defines the crypto material for the
173+
// organization (its MSP). It sets the mod_policy of all elements to "Admins".
174+
func NewOrdererOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
175+
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID)
176+
if err != nil {
177+
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org %s: %s", conf.Name)
178+
}
179+
180+
ordererOrgGroup := cb.NewConfigGroup()
181+
addSignaturePolicyDefaults(ordererOrgGroup, conf.ID, conf.AdminPrincipal != genesisconfig.AdminRoleAdminPrincipal)
182+
addValue(ordererOrgGroup, channelconfig.MSPValue(mspConfig), channelconfig.AdminsPolicyKey)
183+
184+
ordererOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
185+
return ordererOrgGroup, nil
186+
}
187+
188+
// NewApplicationGroup returns the application component of the channel configuration. It defines the organizations which are involved
189+
// in application logic like chaincodes, and how these members may interact with the orderer. It sets the mod_policy of all elements to "Admins".
190+
func NewApplicationGroup(conf *genesisconfig.Application) (*cb.ConfigGroup, error) {
191+
applicationGroup := cb.NewConfigGroup()
192+
addImplicitMetaPolicyDefaults(applicationGroup)
193+
194+
if len(conf.Capabilities) > 0 {
195+
addValue(applicationGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
196+
}
197+
198+
for _, org := range conf.Organizations {
199+
var err error
200+
applicationGroup.Groups[org.Name], err = NewApplicationOrgGroup(org)
201+
if err != nil {
202+
return nil, errors.Wrap(err, "failed to create application org")
203+
}
204+
}
205+
206+
applicationGroup.ModPolicy = channelconfig.AdminsPolicyKey
207+
return applicationGroup, nil
208+
}
209+
210+
// NewApplicationOrgGroup returns an application org component of the channel configuration. It defines the crypto material for the organization
211+
// (its MSP) as well as its anchor peers for use by the gossip network. It sets the mod_policy of all elements to "Admins".
212+
func NewApplicationOrgGroup(conf *genesisconfig.Organization) (*cb.ConfigGroup, error) {
213+
mspConfig, err := msp.GetVerifyingMspConfig(conf.MSPDir, conf.ID)
214+
if err != nil {
215+
return nil, errors.Wrapf(err, "1 - Error loading MSP configuration for org %s: %s", conf.Name)
216+
}
217+
218+
applicationOrgGroup := cb.NewConfigGroup()
219+
addSignaturePolicyDefaults(applicationOrgGroup, conf.ID, conf.AdminPrincipal != genesisconfig.AdminRoleAdminPrincipal)
220+
addValue(applicationOrgGroup, channelconfig.MSPValue(mspConfig), channelconfig.AdminsPolicyKey)
221+
222+
var anchorProtos []*pb.AnchorPeer
223+
for _, anchorPeer := range conf.AnchorPeers {
224+
anchorProtos = append(anchorProtos, &pb.AnchorPeer{
225+
Host: anchorPeer.Host,
226+
Port: int32(anchorPeer.Port),
227+
})
228+
}
229+
addValue(applicationOrgGroup, channelconfig.AnchorPeersValue(anchorProtos), channelconfig.AdminsPolicyKey)
230+
231+
applicationOrgGroup.ModPolicy = channelconfig.AdminsPolicyKey
232+
return applicationOrgGroup, nil
233+
}
234+
235+
// NewConsortiumsGroup returns the consortiums component of the channel configuration. This element is only defined for the ordering system channel.
236+
// It sets the mod_policy for all elements to "/Channel/Orderer/Admins".
237+
func NewConsortiumsGroup(conf map[string]*genesisconfig.Consortium) (*cb.ConfigGroup, error) {
238+
consortiumsGroup := cb.NewConfigGroup()
239+
// This policy is not referenced anywhere, it is only used as part of the implicit meta policy rule at the channel level, so this setting
240+
// effectively degrades control of the ordering system channel to the ordering admins
241+
addPolicy(consortiumsGroup, policies.SignaturePolicy(channelconfig.AdminsPolicyKey, cauthdsl.AcceptAllPolicy), ordererAdminsPolicyName)
242+
243+
for consortiumName, consortium := range conf {
244+
var err error
245+
consortiumsGroup.Groups[consortiumName], err = NewConsortiumGroup(consortium)
246+
if err != nil {
247+
return nil, errors.Wrapf(err, "failed to create consortium %s", consortiumName)
248+
}
249+
}
250+
251+
consortiumsGroup.ModPolicy = ordererAdminsPolicyName
252+
return consortiumsGroup, nil
253+
}
254+
255+
// NewConsortiums returns a consortiums component of the channel configuration. Each consortium defines the organizations which may be involved in channel
256+
// creation, as well as the channel creation policy the orderer checks at channel creation time to authorize the action. It sets the mod_policy of all
257+
// elements to "/Channel/Orderer/Admins".
258+
func NewConsortiumGroup(conf *genesisconfig.Consortium) (*cb.ConfigGroup, error) {
259+
consortiumGroup := cb.NewConfigGroup()
260+
261+
for _, org := range conf.Organizations {
262+
var err error
263+
// Note, NewOrdererOrgGroup is correct here, as the structure is identical
264+
consortiumGroup.Groups[org.Name], err = NewOrdererOrgGroup(org)
265+
if err != nil {
266+
return nil, errors.Wrap(err, "failed to create consortium org")
267+
}
268+
}
269+
270+
addValue(consortiumGroup, channelconfig.ChannelCreationPolicyValue(policies.ImplicitMetaAnyPolicy(channelconfig.AdminsPolicyKey).Value()), ordererAdminsPolicyName)
271+
272+
consortiumGroup.ModPolicy = ordererAdminsPolicyName
273+
return consortiumGroup, nil
274+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package encoder
8+
9+
import (
10+
"testing"
11+
12+
"github.com/hyperledger/fabric/common/channelconfig"
13+
"github.com/hyperledger/fabric/common/flogging"
14+
genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
15+
"github.com/hyperledger/fabric/common/tools/configtxgen/provisional"
16+
cb "github.com/hyperledger/fabric/protos/common"
17+
"github.com/hyperledger/fabric/protos/utils"
18+
19+
"github.com/golang/protobuf/proto"
20+
"github.com/stretchr/testify/assert"
21+
)
22+
23+
func init() {
24+
flogging.SetModuleLevel(pkgLogID, "DEBUG")
25+
}
26+
27+
func hasModPolicySet(t *testing.T, groupName string, cg *cb.ConfigGroup) {
28+
assert.NotEmpty(t, cg.ModPolicy, "group %s has empty mod_policy", groupName)
29+
30+
for valueName, value := range cg.Values {
31+
assert.NotEmpty(t, value.ModPolicy, "group %s has value %s with empty mod_policy", groupName, valueName)
32+
}
33+
34+
for policyName, policy := range cg.Policies {
35+
assert.NotEmpty(t, policy.ModPolicy, "group %s has policy %s with empty mod_policy", groupName, policyName)
36+
}
37+
38+
for groupName, group := range cg.Groups {
39+
hasModPolicySet(t, groupName, group)
40+
}
41+
}
42+
43+
func TestConfigParsing(t *testing.T) {
44+
config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile)
45+
group, err := NewChannelGroup(config)
46+
assert.NoError(t, err)
47+
assert.NotNil(t, group)
48+
49+
_, err = channelconfig.NewBundle("test", &cb.Config{
50+
ChannelGroup: group,
51+
})
52+
assert.NoError(t, err)
53+
54+
hasModPolicySet(t, "Channel", group)
55+
}
56+
57+
// This test will be removed with the legacy provisional package, but demonstrates
58+
// that the old and new encoders produce identical output
59+
func TestEquivalentParsing(t *testing.T) {
60+
for _, profile := range []string{
61+
genesisconfig.SampleInsecureSoloProfile,
62+
genesisconfig.SampleSingleMSPSoloProfile,
63+
genesisconfig.SampleDevModeSoloProfile,
64+
genesisconfig.SampleInsecureKafkaProfile,
65+
genesisconfig.SampleSingleMSPKafkaProfile,
66+
genesisconfig.SampleDevModeKafkaProfile,
67+
} {
68+
config := genesisconfig.Load(profile)
69+
group, err := NewChannelGroup(config)
70+
assert.NoError(t, err)
71+
assert.NotNil(t, group)
72+
73+
gb := provisional.New(config).GenesisBlockForChannel("foo")
74+
env := utils.ExtractEnvelopeOrPanic(gb, 0)
75+
configEnv := &cb.ConfigEnvelope{}
76+
utils.UnmarshalEnvelopeOfType(env, cb.HeaderType_CONFIG, configEnv)
77+
assert.True(t, proto.Equal(configEnv.Config.ChannelGroup, group))
78+
}
79+
}

sampleconfig/configtx.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,20 @@ Profiles:
3939

4040
# SampleDevModeSolo defines a configuration which uses the Solo orderer,
4141
# contains the sample MSP as both orderer and consortium member, and
42-
# requires only basic membership for admin privileges.
42+
# requires only basic membership for admin privileges. It also defines
43+
# an Application on the ordering system channel, which should usually
44+
# be avoided.
4345
SampleDevModeSolo:
4446
Orderer:
4547
<<: *OrdererDefaults
4648
Organizations:
4749
- <<: *SampleOrg
4850
AdminPrincipal: Role.MEMBER
51+
Application:
52+
<<: *ApplicationDefaults
53+
Organizations:
54+
- <<: *SampleOrg
55+
AdminPrincipal: Role.MEMBER
4956
Consortiums:
5057
SampleConsortium:
5158
Organizations:

0 commit comments

Comments
 (0)