Skip to content

Commit 500d3de

Browse files
Jason Yellickyacovm
Jason Yellick
authored andcommittedDec 6, 2017
[FAB-6234] configtxgen emit resource defaults
This changeset at its core, simply enables embedding a default resource tree into the output of channel creation transactions which are created with v1.1 application capabilities. Unit tests have been added. Additionally, the new channel creation tx behavior can be confirmed by issuing: configtxgen -profile SampleSingleMSPChannelV1_1 \ -outputCreateChannelTx foo.tx -inspectChannelCreateTx foo.tx and observing the resources tree in the JSON output. Because the utility function for creating the channel creation transaction is used widely throughout the tests, and the function parameters needed to change significantly, there is a fair amount of churn in the tests, but none in production code. Change-Id: Id719a1ae3f309a0a189f566dc0fef409564b519e Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent 268e6bc commit 500d3de

File tree

15 files changed

+187
-143
lines changed

15 files changed

+187
-143
lines changed
 

‎common/tools/configtxgen/encoder/encoder.go

+95-63
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ package encoder
99
import (
1010
"github.com/hyperledger/fabric/common/cauthdsl"
1111
"github.com/hyperledger/fabric/common/channelconfig"
12+
"github.com/hyperledger/fabric/common/crypto"
1213
"github.com/hyperledger/fabric/common/flogging"
1314
"github.com/hyperledger/fabric/common/genesis"
1415
"github.com/hyperledger/fabric/common/policies"
16+
"github.com/hyperledger/fabric/common/resourcesconfig"
1517
genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
18+
"github.com/hyperledger/fabric/common/tools/configtxlator/update"
1619
"github.com/hyperledger/fabric/common/util"
1720
"github.com/hyperledger/fabric/msp"
1821
cb "github.com/hyperledger/fabric/protos/common"
@@ -281,12 +284,30 @@ func NewConsortiumGroup(conf *genesisconfig.Consortium) (*cb.ConfigGroup, error)
281284

282285
// NewChannelCreateConfigUpdate generates a ConfigUpdate which can be sent to the orderer to create a new channel. Optionally, the channel group of the
283286
// ordering system channel may be passed in, and the resulting ConfigUpdate will extract the appropriate versions from this file.
284-
func NewChannelCreateConfigUpdate(channelID, consortiumName string, orgs []string, orderingSystemChannelGroup *cb.ConfigGroup) (*cb.ConfigUpdate, error) {
285-
var applicationGroup *cb.ConfigGroup
286-
channelGroupVersion := uint64(0)
287+
func NewChannelCreateConfigUpdate(channelID string, orderingSystemChannelGroup *cb.ConfigGroup, conf *genesisconfig.Profile) (*cb.ConfigUpdate, error) {
288+
if conf.Application == nil {
289+
return nil, errors.New("cannot define a new channel with no Application section")
290+
}
291+
292+
if conf.Consortium == "" {
293+
return nil, errors.New("cannot define a new channel with no Consortium value")
294+
}
295+
296+
// Otherwise, parse only the application section, and encapsulate it inside a channel group
297+
ag, err := NewApplicationGroup(conf.Application)
298+
if err != nil {
299+
return nil, errors.Wrapf(err, "could not turn channel application profile into application group")
300+
}
301+
302+
agc, err := channelconfig.NewApplicationConfig(ag, channelconfig.NewMSPConfigHandler(msp.MSPv1_1))
303+
if err != nil {
304+
return nil, errors.Wrapf(err, "could not parse application to application group")
305+
}
306+
307+
var template, newChannelGroup *cb.ConfigGroup
287308

288309
if orderingSystemChannelGroup != nil {
289-
// In the case that a ordering system channel definition was provided, pull the appropriate versions
310+
// In the case that a ordering system channel definition was provided, use it to compute the update
290311
if orderingSystemChannelGroup.Groups == nil {
291312
return nil, errors.New("missing all channel groups")
292313
}
@@ -296,58 +317,86 @@ func NewChannelCreateConfigUpdate(channelID, consortiumName string, orgs []strin
296317
return nil, errors.New("bad consortiums group")
297318
}
298319

299-
consortium, ok := consortiums.Groups[consortiumName]
300-
if !ok || (len(orgs) > 0 && consortium.Groups == nil) {
301-
return nil, errors.Errorf("bad consortium: %s", consortiumName)
320+
consortium, ok := consortiums.Groups[conf.Consortium]
321+
if !ok {
322+
return nil, errors.Errorf("bad consortium: %s", conf.Consortium)
302323
}
303324

304-
applicationGroup = cb.NewConfigGroup()
305-
for _, org := range orgs {
306-
orgGroup, ok := consortium.Groups[org]
307-
if !ok {
308-
return nil, errors.Errorf("missing organization: %s", org)
325+
template = proto.Clone(orderingSystemChannelGroup).(*cb.ConfigGroup)
326+
template.Groups[channelconfig.ApplicationGroupKey] = proto.Clone(consortium).(*cb.ConfigGroup)
327+
// This is a bit of a hack. If the channel config specifies all consortium members, then it does not look
328+
// like a modification. The below adds a fake org with an illegal name which cannot actually exist, which
329+
// will always appear to be deleted, triggering the correct update computation.
330+
template.Groups[channelconfig.ApplicationGroupKey].Groups["*IllegalKey*!"] = &cb.ConfigGroup{}
331+
delete(template.Groups, channelconfig.ConsortiumsGroupKey)
332+
333+
newChannelGroup = proto.Clone(orderingSystemChannelGroup).(*cb.ConfigGroup)
334+
delete(newChannelGroup.Groups, channelconfig.ConsortiumsGroupKey)
335+
newChannelGroup.Groups[channelconfig.ApplicationGroupKey].Values = ag.Values
336+
newChannelGroup.Groups[channelconfig.ApplicationGroupKey].Policies = ag.Policies
337+
338+
for orgName, org := range template.Groups[channelconfig.ApplicationGroupKey].Groups {
339+
if _, ok := ag.Groups[orgName]; ok {
340+
newChannelGroup.Groups[channelconfig.ApplicationGroupKey].Groups[orgName] = org
309341
}
310-
applicationGroup.Groups[org] = &cb.ConfigGroup{Version: orgGroup.Version}
311342
}
312-
313-
channelGroupVersion = orderingSystemChannelGroup.Version
314343
} else {
315-
// Otherwise assume the orgs have not been modified
316-
applicationGroup = cb.NewConfigGroup()
317-
for _, org := range orgs {
318-
applicationGroup.Groups[org] = &cb.ConfigGroup{}
344+
newChannelGroup = &cb.ConfigGroup{
345+
Groups: map[string]*cb.ConfigGroup{
346+
channelconfig.ApplicationGroupKey: ag,
347+
},
319348
}
320-
}
321-
322-
rSet := cb.NewConfigGroup()
323-
rSet.Version = channelGroupVersion
324-
325-
// add the consortium name to the rSet
326349

327-
addValue(rSet, channelconfig.ConsortiumValue(consortiumName), "") // TODO, this emulates the old behavior, but is it desirable?
328-
329-
// create the new channel's application group
350+
// Otherwise assume the orgs have not been modified
351+
template = proto.Clone(newChannelGroup).(*cb.ConfigGroup)
352+
template.Groups[channelconfig.ApplicationGroupKey].Values = nil
353+
template.Groups[channelconfig.ApplicationGroupKey].Policies = nil
354+
}
330355

331-
rSet.Groups[channelconfig.ApplicationGroupKey] = applicationGroup
356+
updt, err := update.Compute(&cb.Config{ChannelGroup: template}, &cb.Config{ChannelGroup: newChannelGroup})
357+
if err != nil {
358+
return nil, errors.Wrapf(err, "could not compute update")
359+
}
332360

333-
wSet := proto.Clone(rSet).(*cb.ConfigGroup)
361+
// Add the consortium name to create the channel for into the write set as required.
362+
updt.ChannelId = channelID
363+
updt.ReadSet.Values[channelconfig.ConsortiumKey] = &cb.ConfigValue{Version: 0}
364+
updt.WriteSet.Values[channelconfig.ConsortiumKey] = &cb.ConfigValue{
365+
Version: 0,
366+
Value: utils.MarshalOrPanic(&cb.Consortium{
367+
Name: conf.Consortium,
368+
}),
369+
}
334370

335-
applicationGroup = wSet.Groups[channelconfig.ApplicationGroupKey]
336-
applicationGroup.Version = 1
337-
applicationGroup.Policies = make(map[string]*cb.ConfigPolicy)
338-
addImplicitMetaPolicyDefaults(applicationGroup)
339-
applicationGroup.ModPolicy = channelconfig.AdminsPolicyKey
371+
// If this channel uses the new lifecycle config, specify the seed data
372+
if agc.Capabilities().LifecycleViaConfig() {
373+
updt.IsolatedData = map[string][]byte{
374+
pb.RSCCSeedDataKey: utils.MarshalOrPanic(&cb.Config{
375+
Type: int32(cb.ConfigType_RESOURCE),
376+
ChannelGroup: &cb.ConfigGroup{
377+
Groups: map[string]*cb.ConfigGroup{
378+
resourcesconfig.ChaincodesGroupKey: &cb.ConfigGroup{
379+
ModPolicy: policies.ChannelApplicationAdmins,
380+
},
381+
resourcesconfig.PeerPoliciesGroupKey: &cb.ConfigGroup{
382+
ModPolicy: policies.ChannelApplicationAdmins,
383+
},
384+
resourcesconfig.APIsGroupKey: &cb.ConfigGroup{
385+
ModPolicy: policies.ChannelApplicationAdmins,
386+
},
387+
},
388+
ModPolicy: policies.ChannelApplicationAdmins,
389+
},
390+
}),
391+
}
392+
}
340393

341-
return &cb.ConfigUpdate{
342-
ChannelId: channelID,
343-
ReadSet: rSet,
344-
WriteSet: wSet,
345-
}, nil
394+
return updt, nil
346395
}
347396

348397
// MakeChannelCreationTransaction is a handy utility function for creating transactions for channel creation
349-
func MakeChannelCreationTransaction(channelID string, consortium string, signer msp.SigningIdentity, orderingSystemChannelConfigGroup *cb.ConfigGroup, orgs ...string) (*cb.Envelope, error) {
350-
newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, consortium, orgs, orderingSystemChannelConfigGroup)
398+
func MakeChannelCreationTransaction(channelID string, signer crypto.LocalSigner, orderingSystemChannelConfigGroup *cb.ConfigGroup, conf *genesisconfig.Profile) (*cb.Envelope, error) {
399+
newChannelConfigUpdate, err := NewChannelCreateConfigUpdate(channelID, orderingSystemChannelConfigGroup, conf)
351400
if err != nil {
352401
return nil, errors.Wrap(err, "config update generation failure")
353402
}
@@ -356,41 +405,24 @@ func MakeChannelCreationTransaction(channelID string, consortium string, signer
356405
ConfigUpdate: utils.MarshalOrPanic(newChannelConfigUpdate),
357406
}
358407

359-
payloadSignatureHeader := &cb.SignatureHeader{}
360408
if signer != nil {
361-
sSigner, err := signer.Serialize()
409+
sigHeader, err := signer.NewSignatureHeader()
362410
if err != nil {
363-
return nil, errors.Wrap(err, "serialization of identity failed")
411+
return nil, errors.Wrap(err, "creating signature header failed")
364412
}
365413

366414
newConfigUpdateEnv.Signatures = []*cb.ConfigSignature{&cb.ConfigSignature{
367-
SignatureHeader: utils.MarshalOrPanic(utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())),
415+
SignatureHeader: utils.MarshalOrPanic(sigHeader),
368416
}}
369417

370418
newConfigUpdateEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigUpdateEnv.Signatures[0].SignatureHeader, newConfigUpdateEnv.ConfigUpdate))
371419
if err != nil {
372420
return nil, errors.Wrap(err, "signature failure over config update")
373421
}
374422

375-
payloadSignatureHeader = utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())
376-
}
377-
378-
payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, channelID, epoch)
379-
utils.SetTxID(payloadChannelHeader, payloadSignatureHeader)
380-
payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader)
381-
payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(newConfigUpdateEnv)}
382-
paylBytes := utils.MarshalOrPanic(payload)
383-
384-
var sig []byte
385-
if signer != nil {
386-
// sign the payload
387-
sig, err = signer.Sign(paylBytes)
388-
if err != nil {
389-
return nil, errors.Wrap(err, "signature failure over config update envelope")
390-
}
391423
}
392424

393-
return &cb.Envelope{Payload: paylBytes, Signature: sig}, nil
425+
return utils.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, signer, newConfigUpdateEnv, msgVersion, epoch)
394426
}
395427

396428
// Bootstrapper is a wrapper around NewChannelConfigGroup which can produce genesis blocks

‎common/tools/configtxgen/encoder/encoder_test.go

+39-19
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import (
1212
"github.com/hyperledger/fabric/common/channelconfig"
1313
"github.com/hyperledger/fabric/common/configtx"
1414
"github.com/hyperledger/fabric/common/flogging"
15-
mmsp "github.com/hyperledger/fabric/common/mocks/msp"
15+
"github.com/hyperledger/fabric/common/localmsp"
1616
genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
17+
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
1718
cb "github.com/hyperledger/fabric/protos/common"
19+
pb "github.com/hyperledger/fabric/protos/peer"
1820
"github.com/hyperledger/fabric/protos/utils"
1921

2022
"github.com/golang/protobuf/proto"
@@ -70,66 +72,84 @@ func TestConfigParsing(t *testing.T) {
7072

7173
func TestGoodChannelCreateConfigUpdate(t *testing.T) {
7274
config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile)
73-
group, err := NewChannelGroup(config)
75+
systemChannel, err := NewChannelGroup(config)
7476
assert.NoError(t, err)
75-
assert.NotNil(t, group)
77+
assert.NotNil(t, systemChannel)
78+
79+
createConfig := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile)
7680

77-
configUpdate, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, group)
81+
configUpdate, err := NewChannelCreateConfigUpdate("channel.id", nil, createConfig)
7882
assert.NoError(t, err)
7983
assert.NotNil(t, configUpdate)
8084

81-
defaultConfigUpdate, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, nil)
85+
defaultConfigUpdate, err := NewChannelCreateConfigUpdate("channel.id", systemChannel, createConfig)
8286
assert.NoError(t, err)
8387
assert.NotNil(t, configUpdate)
8488

8589
assert.True(t, proto.Equal(configUpdate, defaultConfigUpdate), "the config used has had no updates, so should equal default")
8690
}
8791

92+
func TestChannelCreateWithResources(t *testing.T) {
93+
t.Run("AtV1.0", func(t *testing.T) {
94+
createConfig := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile)
95+
96+
configUpdate, err := NewChannelCreateConfigUpdate("channel.id", nil, createConfig)
97+
assert.NoError(t, err)
98+
assert.NotNil(t, configUpdate)
99+
assert.Nil(t, configUpdate.IsolatedData)
100+
})
101+
102+
t.Run("AtV1.1", func(t *testing.T) {
103+
createConfig := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelV11Profile)
104+
105+
configUpdate, err := NewChannelCreateConfigUpdate("channel.id", nil, createConfig)
106+
assert.NoError(t, err)
107+
assert.NotNil(t, configUpdate)
108+
assert.NotNil(t, configUpdate.IsolatedData)
109+
assert.NotEmpty(t, configUpdate.IsolatedData[pb.RSCCSeedDataKey])
110+
})
111+
112+
}
113+
88114
func TestNegativeChannelCreateConfigUpdate(t *testing.T) {
89115
config := genesisconfig.Load(genesisconfig.SampleDevModeSoloProfile)
116+
channelConfig := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile)
90117
group, err := NewChannelGroup(config)
91118
assert.NoError(t, err)
92119
assert.NotNil(t, group)
93120

94121
t.Run("NoGroups", func(t *testing.T) {
95122
channelGroup := proto.Clone(group).(*cb.ConfigGroup)
96123
channelGroup.Groups = nil
97-
_, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup)
124+
_, err := NewChannelCreateConfigUpdate("channel.id", &cb.ConfigGroup{}, channelConfig)
98125
assert.Error(t, err)
99126
assert.Regexp(t, "missing all channel groups", err.Error())
100127
})
101128

102129
t.Run("NoConsortiumsGroup", func(t *testing.T) {
103130
channelGroup := proto.Clone(group).(*cb.ConfigGroup)
104131
delete(channelGroup.Groups, channelconfig.ConsortiumsGroupKey)
105-
_, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup)
132+
_, err := NewChannelCreateConfigUpdate("channel.id", channelGroup, channelConfig)
106133
assert.Error(t, err)
107134
assert.Regexp(t, "bad consortiums group", err.Error())
108135
})
109136

110137
t.Run("NoConsortiums", func(t *testing.T) {
111138
channelGroup := proto.Clone(group).(*cb.ConfigGroup)
112139
delete(channelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups, genesisconfig.SampleConsortiumName)
113-
_, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName}, channelGroup)
140+
_, err := NewChannelCreateConfigUpdate("channel.id", channelGroup, channelConfig)
114141
assert.Error(t, err)
115142
assert.Regexp(t, "bad consortium:", err.Error())
116143
})
117-
118-
t.Run("MissingOrg", func(t *testing.T) {
119-
channelGroup := proto.Clone(group).(*cb.ConfigGroup)
120-
_, err := NewChannelCreateConfigUpdate("channel.id", genesisconfig.SampleConsortiumName, []string{genesisconfig.SampleOrgName + ".wrong"}, channelGroup)
121-
assert.Error(t, err)
122-
assert.Regexp(t, "missing organization:", err.Error())
123-
})
124144
}
125145

126146
func TestMakeChannelCreationTransactionWithSigner(t *testing.T) {
127147
channelID := "foo"
128148

129-
signer, err := mmsp.NewNoopMsp().GetDefaultSigningIdentity()
130-
assert.NoError(t, err, "Creating noop MSP")
149+
mspmgmt.LoadDevMsp()
150+
signer := localmsp.NewSigner()
131151

132-
cct, err := MakeChannelCreationTransaction(channelID, "test", signer, nil)
152+
cct, err := MakeChannelCreationTransaction(channelID, signer, nil, genesisconfig.Load(genesisconfig.SampleSingleMSPChannelV11Profile))
133153
assert.NoError(t, err, "Making chain creation tx")
134154

135155
assert.NotEmpty(t, cct.Signature, "Should have signature")
@@ -149,7 +169,7 @@ func TestMakeChannelCreationTransactionWithSigner(t *testing.T) {
149169

150170
func TestMakeChannelCreationTransactionNoSigner(t *testing.T) {
151171
channelID := "foo"
152-
cct, err := MakeChannelCreationTransaction(channelID, "test", nil, nil)
172+
cct, err := MakeChannelCreationTransaction(channelID, nil, nil, genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile))
153173
assert.NoError(t, err, "Making chain creation tx")
154174

155175
assert.Empty(t, cct.Signature, "Should have empty signature")

‎common/tools/configtxgen/localconfig/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ const (
5252
SampleInsecureSoloProfile = "SampleInsecureSolo"
5353
// SampleDevModeSoloProfile references the sample profile which requires only basic membership for admin privileges and uses solo for ordering.
5454
SampleDevModeSoloProfile = "SampleDevModeSolo"
55+
// SampleDevModeSoloProfileV11 references the sample profile which requires only basic membership for admin privileges and uses solo for ordering, but has v1.1 capabilities enabled
56+
SampleDevModeSoloV11Profile = "SampleDevModeSoloV1_1"
5557
// SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering.
5658
SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"
5759
// SampleSingleMSPSoloV11Profile references the sample profile which includes only the sample MSP with v1.1 capabilities defined and uses solo for ordering.
@@ -61,6 +63,8 @@ const (
6163
SampleInsecureKafkaProfile = "SampleInsecureKafka"
6264
// SampleDevModeKafkaProfile references the sample profile which requires only basic membership for admin privileges and uses Kafka for ordering.
6365
SampleDevModeKafkaProfile = "SampleDevModeKafka"
66+
// SampleDevModeKafkaProfileV11 references the sample profile which requires only basic membership for admin privileges and uses Kafka for ordering, but has v1.1 capabilities enabled.
67+
SampleDevModeKafkaV11Profile = "SampleDevModeKafkaV1_1"
6468
// SampleSingleMSPKafkaProfile references the sample profile which includes only the sample MSP and uses Kafka for ordering.
6569
SampleSingleMSPKafkaProfile = "SampleSingleMSPKafka"
6670
// SampleSingleMSPKafkaV11Profile references the sample profile which includes only the sample MSP with v1.1 capabilities defined and uses Kafka for ordering.

0 commit comments

Comments
 (0)
Please sign in to comment.