Skip to content

Commit

Permalink
[FAB-5649] Invert configtx resource encapsulation
Browse files Browse the repository at this point in the history
The configtx manager current retains a reference to the channel config,
but to support other config types, this actually needs to be inverted so
that the channel config retains a reference to the configtx manager.

This CR performs this inversion and fixes the embedding packages of the
configtx.Manager to instead embed the channelconfig.Resources.

[FAB-5650] Move channel creation from configtx

One of the last big offenders for putting channel config specific code
into the configtx package is the templating code, which provides
mechanisms to create new channel configurations.

The generic templating is fine to remain there, but the channel
specifics must be moved ot the channel config folder.

Note: These two sub-tasks were squashed into one because of otherwise
difficult to break import cycles.

Change-Id: Ib8bf829216e19fbbbd8841c9a3551356ed1676b6
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Aug 12, 2017
1 parent 6e6b77d commit d0c97c0
Show file tree
Hide file tree
Showing 33 changed files with 523 additions and 450 deletions.
48 changes: 34 additions & 14 deletions common/config/channel/api.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Copyright IBM Corp. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
*/

// Note, the directory is still configvalues, but this is stuttery and config
// is a more accurate and better name, TODO, update directory
package config

import (
"time"

configtxapi "github.com/hyperledger/fabric/common/configtx/api"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
ab "github.com/hyperledger/fabric/protos/orderer"
pb "github.com/hyperledger/fabric/protos/peer"
Expand Down Expand Up @@ -97,3 +88,32 @@ type Orderer interface {
// Organizations returns the organizations for the ordering service
Organizations() map[string]Org
}

// Resources is the common set of config resources for all channels
// Depending on whether chain is used at the orderer or at the peer, other
// config resources may be available
type Resources interface {
// ConfigtxManager returns the configtx.Manager for the channel
ConfigtxManager() configtxapi.Manager

// PolicyManager returns the policies.Manager for the channel
PolicyManager() policies.Manager

// ChannelConfig returns the config.Channel for the chain
ChannelConfig() Channel

// OrdererConfig returns the config.Orderer for the channel
// and whether the Orderer config exists
OrdererConfig() (Orderer, bool)

// ConsortiumsConfig() returns the config.Consortiums for the channel
// and whether the consortiums config exists
ConsortiumsConfig() (Consortiums, bool)

// ApplicationConfig returns the configtxapplication.SharedConfig for the channel
// and whether the Application config exists
ApplicationConfig() (Application, bool)

// MSPManager returns the msp.MSPManager for the chain
MSPManager() msp.MSPManager
}
41 changes: 38 additions & 3 deletions common/config/channel/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/config"
configtxmsp "github.com/hyperledger/fabric/common/config/channel/msp"
"github.com/hyperledger/fabric/common/configtx"
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
Expand All @@ -35,13 +37,19 @@ type resources struct {
policyManager *policies.ManagerImpl
configRoot *Root
mspConfigHandler *configtxmsp.MSPConfigHandler
configtxManager configtxapi.Manager
}

// PolicyManager returns the policies.Manager for the chain
func (r *resources) PolicyManager() policies.Manager {
return r.policyManager
}

// ConfigtxManager returns the configtxapi.Manager for the chain
func (r *resources) ConfigtxManager() configtxapi.Manager {
return r.configtxManager
}

// ChannelConfig returns the api.ChannelConfig for the chain
func (r *resources) ChannelConfig() Channel {
return r.configRoot.Channel()
Expand Down Expand Up @@ -133,18 +141,45 @@ func (i *policyProposerRoot) CommitProposals(tx interface{}) {}

type Initializer struct {
*resources
configtxapi.Manager
ppr *policyProposerRoot
}

// NewInitializer creates a chain Initializer for the basic set of common chain resources
func NewInitializer() *Initializer {
// New creates a new channel config, complete with backing configtx manager
// TODO move configtx.Manager to resources, make func on Resources type
func New(envConfig *cb.Envelope, callOnUpdate []func(*Initializer)) (*Initializer, error) {
resources := newResources()
return &Initializer{
i := &Initializer{
resources: resources,
ppr: &policyProposerRoot{
policyManager: resources.policyManager,
},
}
var err error
initialized := false
i.Manager, err = configtx.NewManagerImpl(envConfig, i, []func(cm configtxapi.Manager){
func(cm configtxapi.Manager) {
// This gets invoked once at instantiation before we are ready for it
// we manually do this right after
if !initialized {
return
}
logger.Criticalf("Making callback normally")
for _, callback := range callOnUpdate {
callback(i)
}
},
})
if err != nil {
return nil, err
}
initialized = true
i.resources.configtxManager = i.Manager
logger.Criticalf("Making callback manually")
for _, callback := range callOnUpdate {
callback(i)
}
return i, err
}

func (i *Initializer) RootGroupKey() string {
Expand Down
113 changes: 113 additions & 0 deletions common/config/channel/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package config

import (
"fmt"

configmsp "github.com/hyperledger/fabric/common/config/channel/msp"
"github.com/hyperledger/fabric/common/configtx"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/msp"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
)

const (
msgVersion = int32(0)
epoch = 0
)

type channelCreationTemplate struct {
consortiumName string
orgs []string
}

// NewChainCreationTemplate takes a consortium name and a Template to produce a
// Template which outputs an appropriately constructed list of ConfigUpdateEnvelopes.
func NewChainCreationTemplate(consortiumName string, orgs []string) configtx.Template {
return &channelCreationTemplate{
consortiumName: consortiumName,
orgs: orgs,
}
}

func (cct *channelCreationTemplate) Envelope(channelID string) (*cb.ConfigUpdateEnvelope, error) {
rSet := TemplateConsortium(cct.consortiumName)
wSet := TemplateConsortium(cct.consortiumName)

rSet.Groups[ApplicationGroupKey] = cb.NewConfigGroup()
wSet.Groups[ApplicationGroupKey] = cb.NewConfigGroup()

for _, org := range cct.orgs {
rSet.Groups[ApplicationGroupKey].Groups[org] = cb.NewConfigGroup()
wSet.Groups[ApplicationGroupKey].Groups[org] = cb.NewConfigGroup()
}

wSet.Groups[ApplicationGroupKey].ModPolicy = configmsp.AdminsPolicyKey
wSet.Groups[ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.AdminsPolicyKey, cb.ImplicitMetaPolicy_MAJORITY)
wSet.Groups[ApplicationGroupKey].Policies[configmsp.AdminsPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
wSet.Groups[ApplicationGroupKey].Policies[configmsp.WritersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.WritersPolicyKey, cb.ImplicitMetaPolicy_ANY)
wSet.Groups[ApplicationGroupKey].Policies[configmsp.WritersPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
wSet.Groups[ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey] = policies.ImplicitMetaPolicyWithSubPolicy(configmsp.ReadersPolicyKey, cb.ImplicitMetaPolicy_ANY)
wSet.Groups[ApplicationGroupKey].Policies[configmsp.ReadersPolicyKey].ModPolicy = configmsp.AdminsPolicyKey
wSet.Groups[ApplicationGroupKey].Version = 1

return &cb.ConfigUpdateEnvelope{
ConfigUpdate: utils.MarshalOrPanic(&cb.ConfigUpdate{
ChannelId: channelID,
ReadSet: rSet,
WriteSet: wSet,
}),
}, nil
}

// MakeChainCreationTransaction is a handy utility function for creating new chain transactions using the underlying Template framework
func MakeChainCreationTransaction(channelID string, consortium string, signer msp.SigningIdentity, orgs ...string) (*cb.Envelope, error) {
newChainTemplate := NewChainCreationTemplate(consortium, orgs)
newConfigUpdateEnv, err := newChainTemplate.Envelope(channelID)
if err != nil {
return nil, err
}

payloadSignatureHeader := &cb.SignatureHeader{}
if signer != nil {
sSigner, err := signer.Serialize()
if err != nil {
return nil, fmt.Errorf("Serialization of identity failed, err %s", err)
}

newConfigUpdateEnv.Signatures = []*cb.ConfigSignature{&cb.ConfigSignature{
SignatureHeader: utils.MarshalOrPanic(utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())),
}}

newConfigUpdateEnv.Signatures[0].Signature, err = signer.Sign(util.ConcatenateBytes(newConfigUpdateEnv.Signatures[0].SignatureHeader, newConfigUpdateEnv.ConfigUpdate))
if err != nil {
return nil, err
}

payloadSignatureHeader = utils.MakeSignatureHeader(sSigner, utils.CreateNonceOrPanic())
}

payloadChannelHeader := utils.MakeChannelHeader(cb.HeaderType_CONFIG_UPDATE, msgVersion, channelID, epoch)
utils.SetTxID(payloadChannelHeader, payloadSignatureHeader)
payloadHeader := utils.MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader)
payload := &cb.Payload{Header: payloadHeader, Data: utils.MarshalOrPanic(newConfigUpdateEnv)}
paylBytes := utils.MarshalOrPanic(payload)

var sig []byte
if signer != nil {
// sign the payload
sig, err = signer.Sign(paylBytes)
if err != nil {
return nil, err
}
}

return &cb.Envelope{Payload: paylBytes, Signature: sig}, nil
}
98 changes: 98 additions & 0 deletions common/config/channel/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package config

import (
"testing"

configmsp "github.com/hyperledger/fabric/common/config/channel/msp"
"github.com/hyperledger/fabric/common/configtx"
mmsp "github.com/hyperledger/fabric/common/mocks/msp"
cb "github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"

"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
)

func TestNewChainTemplate(t *testing.T) {
consortiumName := "Test"
orgs := []string{"org1", "org2", "org3"}
nct := NewChainCreationTemplate(consortiumName, orgs)

newChainID := "foo"
configEnv, err := nct.Envelope(newChainID)
if err != nil {
t.Fatalf("Error creation a chain creation config")
}

configUpdate, err := configtx.UnmarshalConfigUpdate(configEnv.ConfigUpdate)
if err != nil {
t.Fatalf("Should not have errored: %s", err)
}

consortiumProto := &cb.Consortium{}
err = proto.Unmarshal(configUpdate.WriteSet.Values[ConsortiumKey].Value, consortiumProto)
assert.NoError(t, err)
assert.Equal(t, consortiumName, consortiumProto.Name, "Should have set correct consortium name")

assert.Equal(t, configUpdate.WriteSet.Groups[ApplicationGroupKey].Version, uint64(1))

assert.Len(t, configUpdate.WriteSet.Groups[ApplicationGroupKey].Groups, len(orgs))

for _, org := range orgs {
group, ok := configUpdate.WriteSet.Groups[ApplicationGroupKey].Groups[org]
assert.True(t, ok, "Expected to find %s but did not", org)
for _, policy := range group.Policies {
assert.Equal(t, configmsp.AdminsPolicyKey, policy.ModPolicy)
}
}
}

func TestMakeChainCreationTransactionWithSigner(t *testing.T) {
channelID := "foo"

signer, err := mmsp.NewNoopMsp().GetDefaultSigningIdentity()
assert.NoError(t, err, "Creating noop MSP")

cct, err := MakeChainCreationTransaction(channelID, "test", signer)
assert.NoError(t, err, "Making chain creation tx")

assert.NotEmpty(t, cct.Signature, "Should have signature")

payload, err := utils.UnmarshalPayload(cct.Payload)
assert.NoError(t, err, "Unmarshaling payload")

configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(payload.Data)
assert.NoError(t, err, "Unmarshaling ConfigUpdateEnvelope")

assert.NotEmpty(t, configUpdateEnv.Signatures, "Should have config env sigs")

sigHeader, err := utils.GetSignatureHeader(payload.Header.SignatureHeader)
assert.NoError(t, err, "Unmarshaling SignatureHeader")
assert.NotEmpty(t, sigHeader.Creator, "Creator specified")
}

func TestMakeChainCreationTransactionNoSigner(t *testing.T) {
channelID := "foo"
cct, err := MakeChainCreationTransaction(channelID, "test", nil)
assert.NoError(t, err, "Making chain creation tx")

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

payload, err := utils.UnmarshalPayload(cct.Payload)
assert.NoError(t, err, "Unmarshaling payload")

configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(payload.Data)
assert.NoError(t, err, "Unmarshaling ConfigUpdateEnvelope")

assert.Empty(t, configUpdateEnv.Signatures, "Should have no config env sigs")

sigHeader, err := utils.GetSignatureHeader(payload.Header.SignatureHeader)
assert.NoError(t, err, "Unmarshaling SignatureHeader")
assert.Empty(t, sigHeader.Creator, "No creator specified")
}
Loading

0 comments on commit d0c97c0

Please sign in to comment.