Skip to content

Commit 7ff778f

Browse files
author
Jason Yellick
committed
[FAB-5818] Make MSPConfigHandler immutable
Although the MSPConfigHandler has not followed the configtx value interface for some time, it still follows the transactional model of Being/Propose/Precommit/Commit. This is wrong for a number of reasons now: 1. The underlying MSP Manager is immutable 2. The MSPConfigHandler is only used by immutable config This CR removes all of the mutable components of the code, drastically simplifying the implementation and reducing the bug surface. Change-Id: Ia940facbc152b2b17c944df8a2d24c080eff3013 Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent d510612 commit 7ff778f

File tree

7 files changed

+59
-156
lines changed

7 files changed

+59
-156
lines changed

common/config/channel/channel.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/hyperledger/fabric/bccsp"
1414
"github.com/hyperledger/fabric/common/config/channel/msp"
1515
"github.com/hyperledger/fabric/common/util"
16+
outermsp "github.com/hyperledger/fabric/msp"
1617
cb "github.com/hyperledger/fabric/protos/common"
1718

1819
"github.com/pkg/errors"
@@ -64,7 +65,7 @@ type ChannelConfig struct {
6465

6566
hashingAlgorithm func(input []byte) []byte
6667

67-
mspConfigHandler *msp.MSPConfigHandler
68+
mspManager outermsp.MSPManager
6869

6970
appConfig *ApplicationConfig
7071
ordererConfig *OrdererConfig
@@ -74,10 +75,11 @@ type ChannelConfig struct {
7475
// NewChannelConfig creates a new ChannelConfig
7576
func NewChannelConfig(channelGroup *cb.ConfigGroup) (*ChannelConfig, error) {
7677
cc := &ChannelConfig{
77-
protos: &ChannelProtos{},
78-
mspConfigHandler: msp.NewMSPConfigHandler(),
78+
protos: &ChannelProtos{},
7979
}
8080

81+
mspConfigHandler := msp.NewMSPConfigHandler()
82+
8183
if err := DeserializeProtoValuesFromGroup(channelGroup, cc.protos); err != nil {
8284
return nil, errors.Wrap(err, "failed to deserialize values")
8385
}
@@ -86,17 +88,15 @@ func NewChannelConfig(channelGroup *cb.ConfigGroup) (*ChannelConfig, error) {
8688
return nil, err
8789
}
8890

89-
cc.mspConfigHandler.BeginConfig("")
90-
91+
var err error
9192
for groupName, group := range channelGroup.Groups {
92-
var err error
9393
switch groupName {
9494
case ApplicationGroupKey:
95-
cc.appConfig, err = NewApplicationConfig(group, cc.mspConfigHandler)
95+
cc.appConfig, err = NewApplicationConfig(group, mspConfigHandler)
9696
case OrdererGroupKey:
97-
cc.ordererConfig, err = NewOrdererConfig(group, cc.mspConfigHandler)
97+
cc.ordererConfig, err = NewOrdererConfig(group, mspConfigHandler)
9898
case ConsortiumsGroupKey:
99-
cc.consortiumsConfig, err = NewConsortiumsConfig(group, cc.mspConfigHandler)
99+
cc.consortiumsConfig, err = NewConsortiumsConfig(group, mspConfigHandler)
100100
default:
101101
return nil, fmt.Errorf("Disallowed channel group: %s", group)
102102
}
@@ -105,17 +105,16 @@ func NewChannelConfig(channelGroup *cb.ConfigGroup) (*ChannelConfig, error) {
105105
}
106106
}
107107

108-
if err := cc.mspConfigHandler.PreCommit(""); err != nil {
108+
if cc.mspManager, err = mspConfigHandler.CreateMSPManager(); err != nil {
109109
return nil, err
110110
}
111-
cc.mspConfigHandler.CommitProposals("")
112111

113112
return cc, nil
114113
}
115114

116115
// MSPManager returns the MSP manager for this config
117-
func (cc *ChannelConfig) MSPManager() *msp.MSPConfigHandler {
118-
return cc.mspConfigHandler
116+
func (cc *ChannelConfig) MSPManager() outermsp.MSPManager {
117+
return cc.mspManager
119118
}
120119

121120
// OrdererConfig returns the orderer config associated with this channel

common/config/channel/msp/config.go

Lines changed: 20 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,39 @@
11
/*
2-
Copyright IBM Corp. 2017 All Rights Reserved.
2+
Copyright IBM Corp. All Rights Reserved.
33
4-
Licensed under the Apache License, Version 2.0 (the "License");
5-
you may not use this file except in compliance with the License.
6-
You may obtain a copy of the License at
7-
8-
http://www.apache.org/licenses/LICENSE-2.0
9-
10-
Unless required by applicable law or agreed to in writing, software
11-
distributed under the License is distributed on an "AS IS" BASIS,
12-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
See the License for the specific language governing permissions and
14-
limitations under the License.
4+
SPDX-License-Identifier: Apache-2.0
155
*/
166

177
package msp
188

199
import (
2010
"fmt"
21-
"reflect"
22-
"sync"
2311

2412
"github.com/hyperledger/fabric/msp"
2513
"github.com/hyperledger/fabric/msp/cache"
2614
mspprotos "github.com/hyperledger/fabric/protos/msp"
15+
16+
"github.com/golang/protobuf/proto"
2717
)
2818

2919
type pendingMSPConfig struct {
3020
mspConfig *mspprotos.MSPConfig
3121
msp msp.MSP
3222
}
3323

34-
type mspConfigStore struct {
35-
idMap map[string]*pendingMSPConfig
36-
proposedMgr msp.MSPManager
37-
}
38-
3924
// MSPConfigHandler
4025
type MSPConfigHandler struct {
41-
pendingConfig map[interface{}]*mspConfigStore
42-
pendingLock sync.RWMutex
43-
msp.MSPManager
26+
idMap map[string]*pendingMSPConfig
4427
}
4528

4629
func NewMSPConfigHandler() *MSPConfigHandler {
4730
return &MSPConfigHandler{
48-
pendingConfig: make(map[interface{}]*mspConfigStore),
49-
}
50-
}
51-
52-
// BeginConfig called when a config proposal is begun
53-
func (bh *MSPConfigHandler) BeginConfig(tx interface{}) {
54-
bh.pendingLock.Lock()
55-
defer bh.pendingLock.Unlock()
56-
_, ok := bh.pendingConfig[tx]
57-
if ok {
58-
panic("Programming error, called BeginConfig multiply for the same tx")
59-
}
60-
bh.pendingConfig[tx] = &mspConfigStore{
6131
idMap: make(map[string]*pendingMSPConfig),
6232
}
6333
}
6434

65-
// RollbackProposals called when a config proposal is abandoned
66-
func (bh *MSPConfigHandler) RollbackProposals(tx interface{}) {
67-
bh.pendingLock.Lock()
68-
defer bh.pendingLock.Unlock()
69-
delete(bh.pendingConfig, tx)
70-
}
71-
72-
// CommitProposals called when a config proposal is committed
73-
func (bh *MSPConfigHandler) CommitProposals(tx interface{}) {
74-
bh.pendingLock.Lock()
75-
defer bh.pendingLock.Unlock()
76-
pendingConfig, ok := bh.pendingConfig[tx]
77-
if !ok {
78-
panic("Programming error, called BeginConfig multiply for the same tx")
79-
}
80-
81-
bh.MSPManager = pendingConfig.proposedMgr
82-
delete(bh.pendingConfig, tx)
83-
}
84-
85-
// ProposeValue called when config is added to a proposal
86-
func (bh *MSPConfigHandler) ProposeMSP(tx interface{}, mspConfig *mspprotos.MSPConfig) (msp.MSP, error) {
87-
bh.pendingLock.RLock()
88-
pendingConfig, ok := bh.pendingConfig[tx]
89-
bh.pendingLock.RUnlock()
90-
if !ok {
91-
panic("Programming error, called BeginConfig multiply for the same tx")
92-
}
93-
35+
// ProposeValue called when an org defines an MSP
36+
func (bh *MSPConfigHandler) ProposeMSP(mspConfig *mspprotos.MSPConfig) (msp.MSP, error) {
9437
// check that the type for that MSP is supported
9538
if mspConfig.Type != int32(msp.FABRIC) {
9639
return nil, fmt.Errorf("Setup error: unsupported msp type %d", mspConfig.Type)
@@ -116,36 +59,30 @@ func (bh *MSPConfigHandler) ProposeMSP(tx interface{}, mspConfig *mspprotos.MSPC
11659
// add the MSP to the map of pending MSPs
11760
mspID, _ := mspInst.GetIdentifier()
11861

119-
existingPendingMSPConfig, ok := pendingConfig.idMap[mspID]
120-
if ok && !reflect.DeepEqual(existingPendingMSPConfig.mspConfig, mspConfig) {
62+
existingPendingMSPConfig, ok := bh.idMap[mspID]
63+
if ok && !proto.Equal(existingPendingMSPConfig.mspConfig, mspConfig) {
12164
return nil, fmt.Errorf("Attempted to define two different versions of MSP: %s", mspID)
12265
}
12366

124-
pendingConfig.idMap[mspID] = &pendingMSPConfig{
125-
mspConfig: mspConfig,
126-
msp: mspInst,
67+
if !ok {
68+
bh.idMap[mspID] = &pendingMSPConfig{
69+
mspConfig: mspConfig,
70+
msp: mspInst,
71+
}
12772
}
12873

12974
return mspInst, nil
13075
}
13176

132-
// PreCommit instantiates the MSP manager
133-
func (bh *MSPConfigHandler) PreCommit(tx interface{}) error {
134-
bh.pendingLock.RLock()
135-
pendingConfig, ok := bh.pendingConfig[tx]
136-
bh.pendingLock.RUnlock()
137-
if !ok {
138-
panic("Programming error, called PreCommit for tx which was not started")
139-
}
140-
141-
mspList := make([]msp.MSP, len(pendingConfig.idMap))
77+
func (bh *MSPConfigHandler) CreateMSPManager() (msp.MSPManager, error) {
78+
mspList := make([]msp.MSP, len(bh.idMap))
14279
i := 0
143-
for _, pendingMSP := range pendingConfig.idMap {
80+
for _, pendingMSP := range bh.idMap {
14481
mspList[i] = pendingMSP.msp
14582
i++
14683
}
14784

148-
pendingConfig.proposedMgr = msp.NewMSPManager()
149-
err := pendingConfig.proposedMgr.Setup(mspList)
150-
return err
85+
manager := msp.NewMSPManager()
86+
err := manager.Setup(mspList)
87+
return manager, err
15188
}

common/config/channel/msp/config_test.go

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,47 +36,36 @@ func TestMSPConfigManager(t *testing.T) {
3636

3737
// test success:
3838

39-
// begin/propose/commit
4039
mspCH := NewMSPConfigHandler()
4140

42-
assert.Panics(t, func() {
43-
mspCH.PreCommit(t)
44-
}, "Expected panic calling PreCommit before beginning transaction")
45-
assert.Panics(t, func() {
46-
mspCH.CommitProposals(t)
47-
}, "Expected panic calling CommitProposals before beginning transaction")
48-
assert.Panics(t, func() {
49-
_, err = mspCH.ProposeMSP(t, conf)
50-
}, "Expected panic calling ProposeMSP before beginning transaction")
41+
_, err = mspCH.ProposeMSP(conf)
42+
assert.NoError(t, err)
5143

52-
mspCH.BeginConfig(t)
53-
_, err = mspCH.ProposeMSP(t, conf)
44+
mgr, err := mspCH.CreateMSPManager()
5445
assert.NoError(t, err)
55-
mspCH.PreCommit(t)
56-
mspCH.CommitProposals(t)
46+
assert.NotNil(t, mgr)
5747

58-
msps, err := mspCH.GetMSPs()
48+
msps, err := mgr.GetMSPs()
5949
assert.NoError(t, err)
6050

6151
if msps == nil || len(msps) == 0 {
6252
t.Fatalf("There are no MSPS in the manager")
6353
}
54+
}
6455

65-
mspCH.BeginConfig(t)
66-
_, err = mspCH.ProposeMSP(t, conf)
67-
mspCH.RollbackProposals(t)
56+
func TestMSPConfigFailure(t *testing.T) {
57+
mspCH := NewMSPConfigHandler()
6858

69-
// test failure
7059
// begin/propose/commit
71-
mspCH.BeginConfig(t)
72-
_, err = mspCH.ProposeMSP(t, conf)
73-
assert.NoError(t, err)
74-
_, err = mspCH.ProposeMSP(t, &mspprotos.MSPConfig{Config: []byte("BARF!")})
75-
assert.Error(t, err)
76-
_, err = mspCH.ProposeMSP(t, &mspprotos.MSPConfig{Type: int32(10)})
77-
assert.Panics(t, func() {
78-
mspCH.BeginConfig(t)
79-
}, "Expected panic calling BeginConfig multiple times for same transaction")
60+
t.Run("Bad proto", func(t *testing.T) {
61+
_, err := mspCH.ProposeMSP(&mspprotos.MSPConfig{Config: []byte("BARF!")})
62+
assert.Error(t, err)
63+
})
64+
65+
t.Run("Bad MSP Type", func(t *testing.T) {
66+
_, err := mspCH.ProposeMSP(&mspprotos.MSPConfig{Type: int32(10)})
67+
assert.Error(t, err)
68+
})
8069
}
8170

8271
func TestTemplates(t *testing.T) {

common/config/channel/organization.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (oc *OrganizationConfig) validateMSP() error {
8080
var err error
8181

8282
logger.Debugf("Setting up MSP for org %s", oc.name)
83-
oc.msp, err = oc.mspConfigHandler.ProposeMSP("", oc.protos.MSP)
83+
oc.msp, err = oc.mspConfigHandler.ProposeMSP(oc.protos.MSP)
8484
if err != nil {
8585
return err
8686
}

msp/mgmt/deserializer_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"os"
2222
"testing"
2323

24-
msp2 "github.com/hyperledger/fabric/common/config/channel/msp"
2524
"github.com/hyperledger/fabric/core/config"
2625
"github.com/hyperledger/fabric/msp"
2726
"github.com/stretchr/testify/assert"
@@ -87,7 +86,7 @@ func TestMain(m *testing.M) {
8786
os.Exit(-1)
8887
}
8988

90-
XXXSetMSPManager("foo", &msp2.MSPConfigHandler{MSPManager: msp.NewMSPManager()})
89+
XXXSetMSPManager("foo", msp.NewMSPManager())
9190
retVal := m.Run()
9291
os.Exit(retVal)
9392
}

msp/mgmt/mgmt.go

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"errors"
2424

2525
"github.com/hyperledger/fabric/bccsp/factory"
26-
configvaluesmsp "github.com/hyperledger/fabric/common/config/channel/msp"
2726
"github.com/hyperledger/fabric/common/flogging"
2827
"github.com/hyperledger/fabric/core/config"
2928
"github.com/hyperledger/fabric/msp"
@@ -76,24 +75,12 @@ func GetManagerForChain(chainID string) msp.MSPManager {
7675
mspMgr = msp.NewMSPManager()
7776
mspMap[chainID] = mspMgr
7877
} else {
79-
switch mgr := mspMgr.(type) {
80-
case *configvaluesmsp.MSPConfigHandler:
81-
// check for nil MSPManager interface as it can exist but not be
82-
// instantiated
83-
if mgr.MSPManager == nil {
84-
mspLogger.Debugf("MSPManager is not instantiated; no MSPs are defined for this channel.")
85-
// return nil so the MSPManager methods cannot be accidentally called,
86-
// which would result in a panic
87-
return nil
88-
}
89-
default:
90-
// check for internal mspManagerImpl type. if a different type is found,
91-
// it's because a developer has added a new type that implements the
92-
// MSPManager interface and should add a case to the logic above to handle
93-
// it.
94-
if reflect.TypeOf(mgr).Elem().Name() != "mspManagerImpl" {
95-
panic("Found unexpected MSPManager type.")
96-
}
78+
// check for internal mspManagerImpl type. if a different type is found,
79+
// it's because a developer has added a new type that implements the
80+
// MSPManager interface and should add a case to the logic above to handle
81+
// it.
82+
if reflect.TypeOf(mspMgr).Elem().Name() != "mspManagerImpl" {
83+
panic("Found unexpected MSPManager type.")
9784
}
9885
mspLogger.Debugf("Returning existing manager for channel '%s'", chainID)
9986
}

msp/mgmt/mgmt_test.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package mgmt
1919
import (
2020
"testing"
2121

22-
configvaluesmsp "github.com/hyperledger/fabric/common/config/channel/msp"
2322
"github.com/hyperledger/fabric/msp"
2423
"github.com/stretchr/testify/assert"
2524
)
@@ -41,14 +40,7 @@ func TestGetManagerForChains(t *testing.T) {
4140
}
4241

4342
func TestGetManagerForChains_usingMSPConfigHandlers(t *testing.T) {
44-
XXXSetMSPManager("test", &configvaluesmsp.MSPConfigHandler{MSPManager: nil})
45-
msp1 := GetManagerForChain("test")
46-
// return value should be nil because the MSPManager was not initialized
47-
if msp1 != nil {
48-
t.Fatal("MSPManager should have been nil")
49-
}
50-
51-
XXXSetMSPManager("foo", &configvaluesmsp.MSPConfigHandler{MSPManager: msp.NewMSPManager()})
43+
XXXSetMSPManager("foo", msp.NewMSPManager())
5244
msp2 := GetManagerForChain("foo")
5345
// return value should be set because the MSPManager was initialized
5446
if msp2 == nil {
@@ -57,7 +49,7 @@ func TestGetManagerForChains_usingMSPConfigHandlers(t *testing.T) {
5749
}
5850

5951
func TestGetIdentityDeserializer(t *testing.T) {
60-
XXXSetMSPManager("baz", &configvaluesmsp.MSPConfigHandler{MSPManager: msp.NewMSPManager()})
52+
XXXSetMSPManager("baz", msp.NewMSPManager())
6153
ids := GetIdentityDeserializer("baz")
6254
assert.NotNil(t, ids)
6355
ids = GetIdentityDeserializer("")

0 commit comments

Comments
 (0)