Skip to content

Commit

Permalink
[FAB-2097] Add config next proto
Browse files Browse the repository at this point in the history
https://jira.hyperledger.org/browse/FAB-2097

Rather than modify the config protos and the config producers, parsing,
and consumers all at once, this CR adds only the new protos, without
removing the old, and makes the minimal changes required only to use the
new proto format while on the wire.

Change-Id: I3fa2b92461a5f1e4418a59d1256c00a2fadcd49a
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Feb 8, 2017
1 parent 514db40 commit 3c10c46
Show file tree
Hide file tree
Showing 13 changed files with 453 additions and 95 deletions.
12 changes: 12 additions & 0 deletions common/configtx/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/hyperledger/fabric/common/policies"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"

logging "github.com/op/go-logging"
)
Expand Down Expand Up @@ -151,6 +152,17 @@ func validateChainID(chainID string) error {
return nil
}

func NewManagerImplNext(configtx *cb.ConfigEnvelope, initializer Initializer, callOnUpdate []func(Manager)) (Manager, error) {
configNext, err := UnmarshalConfigNext(configtx.Config)
if err != nil {
return nil, err
}

config := ConfigNextToConfig(configNext)

return NewManagerImpl(&cb.ConfigEnvelope{Config: utils.MarshalOrPanic(config), Signatures: configtx.Signatures}, initializer, callOnUpdate)
}

// NewManagerImpl creates a new Manager unless an error is encountered, each element of the callOnUpdate slice
// is invoked when a new config is committed
func NewManagerImpl(configtx *cb.ConfigEnvelope, initializer Initializer, callOnUpdate []func(Manager)) (Manager, error) {
Expand Down
105 changes: 99 additions & 6 deletions common/configtx/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const (
CreationPolicyKey = "CreationPolicy"
msgVersion = int32(0)
epoch = 0

ApplicationGroup = "Application"
OrdererGroup = "Orderer"
MSPKey = "MSP"
)

// Template can be used to faciliate creation of config transactions
Expand All @@ -45,18 +49,59 @@ type simpleTemplate struct {
}

// NewSimpleTemplate creates a Template using the supplied items
// XXX This signature will change soon, leaving as is for backwards compatibility
func NewSimpleTemplate(items ...*cb.ConfigItem) Template {
return &simpleTemplate{items: items}
}

// Items returns a set of ConfigEnvelopes for the given chainID, and errors only on marshaling errors
func (st *simpleTemplate) Envelope(chainID string) (*cb.ConfigEnvelope, error) {
marshaledConfig, err := proto.Marshal(&cb.Config{
channel := cb.NewConfigGroup()
channel.Groups[ApplicationGroup] = cb.NewConfigGroup()
channel.Groups[OrdererGroup] = cb.NewConfigGroup()

for _, item := range st.items {
var values map[string]*cb.ConfigValue
switch item.Type {
case cb.ConfigItem_Peer:
values = channel.Groups[ApplicationGroup].Values
case cb.ConfigItem_Orderer:
values = channel.Groups[OrdererGroup].Values
case cb.ConfigItem_Chain:
values = channel.Values
case cb.ConfigItem_Policy:
logger.Debugf("Templating about policy %s", item.Key)
policy := &cb.Policy{}
err := proto.Unmarshal(item.Value, policy)
if err != nil {
return nil, err
}
channel.Policies[item.Key] = &cb.ConfigPolicy{
Policy: policy,
}
continue
case cb.ConfigItem_MSP:
group := cb.NewConfigGroup()
channel.Groups[ApplicationGroup].Groups[item.Key] = group
channel.Groups[OrdererGroup].Groups[item.Key] = group
group.Values[MSPKey] = &cb.ConfigValue{
Value: item.Value,
}
continue
}

// For Peer, Orderer, Chain, types
values[item.Key] = &cb.ConfigValue{
Value: item.Value,
}
}

marshaledConfig, err := proto.Marshal(&cb.ConfigNext{
Header: &cb.ChainHeader{
ChainID: chainID,
Type: int32(cb.HeaderType_CONFIGURATION_ITEM),
},
Items: st.items,
Channel: channel,
})
if err != nil {
return nil, err
Expand All @@ -74,22 +119,70 @@ func NewCompositeTemplate(templates ...Template) Template {
return &compositeTemplate{templates: templates}
}

func copyGroup(source *cb.ConfigGroup, target *cb.ConfigGroup) error {
for key, value := range source.Values {
_, ok := target.Values[key]
if ok {
return fmt.Errorf("Duplicate key: %s", key)
}
target.Values[key] = value
}

for key, policy := range source.Policies {
_, ok := target.Policies[key]
if ok {
return fmt.Errorf("Duplicate policy: %s", key)
}
target.Policies[key] = policy
}

for key, group := range source.Groups {
_, ok := target.Groups[key]
if !ok {
target.Groups[key] = cb.NewConfigGroup()
}

err := copyGroup(group, target.Groups[key])
if err != nil {
return fmt.Errorf("Error copying group %s: %s", key, err)
}
}
return nil
}

// Items returns a set of ConfigEnvelopes for the given chainID, and errors only on marshaling errors
func (ct *compositeTemplate) Envelope(chainID string) (*cb.ConfigEnvelope, error) {
items := make([][]*cb.ConfigItem, len(ct.templates))
channel := cb.NewConfigGroup()
channel.Groups[ApplicationGroup] = cb.NewConfigGroup()
channel.Groups[OrdererGroup] = cb.NewConfigGroup()

for i := range ct.templates {
configEnv, err := ct.templates[i].Envelope(chainID)
if err != nil {
return nil, err
}
config, err := UnmarshalConfig(configEnv.Config)
config, err := UnmarshalConfigNext(configEnv.Config)
if err != nil {
return nil, err
}
err = copyGroup(config.Channel, channel)
if err != nil {
return nil, err
}
items[i] = config.Items
}

return NewSimpleTemplate(join(items...)...).Envelope(chainID)
marshaledConfig, err := proto.Marshal(&cb.ConfigNext{
Header: &cb.ChainHeader{
ChainID: chainID,
Type: int32(cb.HeaderType_CONFIGURATION_ITEM),
},
Channel: channel,
})
if err != nil {
return nil, err
}

return &cb.ConfigEnvelope{Config: marshaledConfig}, nil
}

// NewChainCreationTemplate takes a CreationPolicy and a Template to produce a Template which outputs an appropriately
Expand Down
64 changes: 44 additions & 20 deletions common/configtx/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,45 @@ func verifyItemsResult(t *testing.T, template Template, count int) {
t.Fatalf("Should not have errored: %s", err)
}

config, err := UnmarshalConfig(configEnv.Config)
configNext, err := UnmarshalConfigNext(configEnv.Config)
if err != nil {
t.Fatalf("Should not have errored: %s", err)
}
config := ConfigNextToConfig(configNext)

if len(config.Items) != count {
t.Errorf("Expected %d items, but got %d", count, len(config.Items))
}

for i, item := range config.Items {
expected := fmt.Sprintf("%d", i)
assert.Equal(t, expected, string(item.Value), "Expected %s but got %s", expected, item.Value)
for i, _ := range config.Items {
count := 0
for _, item := range config.Items {
key := fmt.Sprintf("%d", i)
if key == item.Key {
count++
}
}
expected := 1
assert.Equal(t, expected, count, "Expected %d but got %d for %d", expected, count, i)
}
}

func TestSimpleTemplate(t *testing.T) {
simple := NewSimpleTemplate(
&cb.ConfigItem{Value: []byte("0")},
&cb.ConfigItem{Value: []byte("1")},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "0"},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "1"},
)
verifyItemsResult(t, simple, 2)
}

func TestCompositeTemplate(t *testing.T) {
composite := NewCompositeTemplate(
NewSimpleTemplate(
&cb.ConfigItem{Value: []byte("0")},
&cb.ConfigItem{Value: []byte("1")},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "0"},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "1"},
),
NewSimpleTemplate(
&cb.ConfigItem{Value: []byte("2")},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "2"},
),
)

Expand All @@ -71,8 +79,8 @@ func TestCompositeTemplate(t *testing.T) {

func TestNewChainTemplate(t *testing.T) {
simple := NewSimpleTemplate(
&cb.ConfigItem{Value: []byte("1")},
&cb.ConfigItem{Value: []byte("2")},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "0"},
&cb.ConfigItem{Type: cb.ConfigItem_Orderer, Key: "1"},
)

creationPolicy := "Test"
Expand All @@ -84,24 +92,40 @@ func TestNewChainTemplate(t *testing.T) {
t.Fatalf("Error creation a chain creation config")
}

config, err := UnmarshalConfig(configEnv.Config)
configNext, err := UnmarshalConfigNext(configEnv.Config)
if err != nil {
t.Fatalf("Should not have errored: %s", err)
}
config := ConfigNextToConfig(configNext)

if expected := 3; len(config.Items) != expected {
t.Fatalf("Expected %d items, but got %d", expected, len(config.Items))
}

for i, item := range config.Items {
if i == 0 {
if item.Key != CreationPolicyKey {
t.Errorf("First item should have been the creation policy")
}
} else {
if expected := fmt.Sprintf("%d", i); string(item.Value) != expected {
t.Errorf("Expected %s but got %s", expected, item.Value)
for i, _ := range config.Items {
if i == len(config.Items)-1 {
break
}
count := 0
for _, item := range config.Items {
key := fmt.Sprintf("%d", i)
if key == item.Key {
count++
}
}
expected := 1
assert.Equal(t, expected, count, "Expected %d but got %d for %d", expected, count, i)
}

foundCreationPolicy := false
for _, item := range config.Items {
if item.Key == CreationPolicyKey {
foundCreationPolicy = true
continue
}
}

if !foundCreationPolicy {
t.Errorf("Should have found the creation policy")
}
}
70 changes: 70 additions & 0 deletions common/configtx/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,75 @@ func UnmarshalConfig(data []byte) (*cb.Config, error) {
return config, nil
}

// UnmarshalConfigNext attempts to unmarshal bytes to a *cb.ConfigNext
func UnmarshalConfigNext(data []byte) (*cb.ConfigNext, error) {
config := &cb.ConfigNext{}
err := proto.Unmarshal(data, config)
if err != nil {
return nil, err
}
return config, nil
}

// ConfigNextToConfig is a XXX temporary method for use in the change series converting the configtx protos
// so error handling and testing is omitted as it will be removed shortly
func ConfigNextToConfig(config *cb.ConfigNext) *cb.Config {
result := &cb.Config{
Header: config.Header,
}

channel := config.Channel

for key, value := range channel.Values {
result.Items = append(result.Items, &cb.ConfigItem{
Key: key,
Type: cb.ConfigItem_Chain,
Value: value.Value,
})
}

for key, value := range channel.Groups[OrdererGroup].Values {
result.Items = append(result.Items, &cb.ConfigItem{
Key: key,
Type: cb.ConfigItem_Orderer,
Value: value.Value,
})
}

for key, value := range channel.Groups[ApplicationGroup].Values {
result.Items = append(result.Items, &cb.ConfigItem{
Key: key,
Type: cb.ConfigItem_Peer,
Value: value.Value,
})
}

logger.Debugf("Processing polices %v", channel.Policies)
for key, value := range channel.Policies {
logger.Debugf("Reversing policy %s", key)
result.Items = append(result.Items, &cb.ConfigItem{
Key: key,
Type: cb.ConfigItem_Policy,
Value: utils.MarshalOrPanic(value.Policy),
})
}

// Note, for now, all MSPs are encoded in both ApplicationGroup and OrdererGroup, so we only need to pick one
for key, group := range channel.Groups[ApplicationGroup].Groups {
msp, ok := group.Values[MSPKey]
if !ok {
panic("Expected MSP defined")
}
result.Items = append(result.Items, &cb.ConfigItem{
Key: key,
Type: cb.ConfigItem_MSP,
Value: msp.Value,
})
}

return result
}

// UnmarshalConfigEnvelope attempts to unmarshal bytes to a *cb.ConfigEnvelope
func UnmarshalConfigEnvelope(data []byte) (*cb.ConfigEnvelope, error) {
configEnv := &cb.ConfigEnvelope{}
Expand All @@ -45,6 +114,7 @@ func UnmarshalConfigEnvelope(data []byte) (*cb.ConfigEnvelope, error) {
return configEnv, nil
}

// ConfigEnvelopeFromBlock extract the config envelope from a config block
func ConfigEnvelopeFromBlock(block *cb.Block) (*cb.ConfigEnvelope, error) {
if block.Data == nil || len(block.Data.Data) != 1 {
return nil, fmt.Errorf("Not a config block, must contain exactly one tx")
Expand Down
2 changes: 1 addition & 1 deletion core/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error {

configtxInitializer := configtx.NewInitializer()
configtxInitializer.Handlers()[common.ConfigItem_Peer] = sharedConfigHandler
configtxManager, err := configtx.NewManagerImpl(
configtxManager, err := configtx.NewManagerImplNext(
configEnvelope,
configtxInitializer,
[]func(cm configtx.Manager){gossipCallbackWrapper},
Expand Down
2 changes: 1 addition & 1 deletion orderer/multichain/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func newConfigResources(configEnvelope *cb.ConfigEnvelope) (*configResources, er
initializer := configtx.NewInitializer()
initializer.Handlers()[cb.ConfigItem_Orderer] = sharedConfigManager

configManager, err := configtx.NewManagerImpl(configEnvelope, initializer, nil)
configManager, err := configtx.NewManagerImplNext(configEnvelope, initializer, nil)
if err != nil {
return nil, fmt.Errorf("Error unpacking config transaction: %s", err)
}
Expand Down
Loading

0 comments on commit 3c10c46

Please sign in to comment.