Skip to content

Commit 366e978

Browse files
author
Jason Yellick
committed
[FAB-6072] Panic on incompatibilities
Although gracefully leaving a channel might be a better option in the future, because this behavior is not compatible with v1.0.x gossip, and because of the complexity of this operation, a good first pass for v1.1 is simply to ensure that all processes panic if incompatible configuration is pushed to the channel. This CR causes the orderer to check for capabilities during the config validatiion process it already undertakes. It also causes the orderer to panic if a config block is committed which contains capabilities unknown to the orderer. Finally, on join channel and on config update, the peer checks the capabilities and panics on incompatibility. Change-Id: Ieeeaf7a1924c42e001a3a973873a4343f134a0ae Signed-off-by: Binh Q. Nguyen <binhn@us.ibm.com> Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent 456283e commit 366e978

File tree

11 files changed

+235
-69
lines changed

11 files changed

+235
-69
lines changed

common/mocks/config/channel.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,14 @@ func (scm *Channel) OrdererAddresses() []string {
5959
func (scm *Channel) Capabilities() channelconfig.ChannelCapabilities {
6060
return scm.CapabilitiesVal
6161
}
62+
63+
// ChannelCapabilities mocks the channelconfig.ChannelCapabilities interface
64+
type ChannelCapabilities struct {
65+
// SupportedErr is returned by Supported()
66+
SupportedErr error
67+
}
68+
69+
// Supported returns SupportedErr
70+
func (oc *ChannelCapabilities) Supported() error {
71+
return oc.SupportedErr
72+
}

common/mocks/config/orderer.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,14 @@ func (scm *Orderer) Organizations() map[string]channelconfig.Org {
6565
func (scm *Orderer) Capabilities() channelconfig.OrdererCapabilities {
6666
return scm.CapabilitiesVal
6767
}
68+
69+
// OrdererCapabilities mocks the channelconfig.OrdererCapabilities interface
70+
type OrdererCapabilities struct {
71+
// SupportedErr is returned by Supported()
72+
SupportedErr error
73+
}
74+
75+
// Supported returns SupportedErr
76+
func (oc *OrdererCapabilities) Supported() error {
77+
return oc.SupportedErr
78+
}

common/mocks/config/resources.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ func (r *Resources) ChannelConfig() channelconfig.Channel {
5656

5757
// Returns the OrdererConfigVal
5858
func (r *Resources) OrdererConfig() (channelconfig.Orderer, bool) {
59-
return r.OrdererConfigVal, r.OrdererConfigVal == nil
59+
return r.OrdererConfigVal, r.OrdererConfigVal != nil
6060
}
6161

6262
// Returns the ApplicationConfigVal
6363
func (r *Resources) ApplicationConfig() (channelconfig.Application, bool) {
64-
return r.ApplicationConfigVal, r.ApplicationConfigVal == nil
64+
return r.ApplicationConfigVal, r.ApplicationConfigVal != nil
6565
}
6666

6767
func (r *Resources) ConsortiumsConfig() (channelconfig.Consortiums, bool) {

core/peer/peer.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,29 @@ func (cs *chainSupport) Apply(configtx *common.ConfigEnvelope) error {
9393
if err != nil {
9494
return err
9595
}
96+
97+
capabilitiesSupportedOrPanic(bundle)
98+
9699
cs.bundleSource.Update(bundle)
97100
}
98101
return nil
99102
}
100103

104+
func capabilitiesSupportedOrPanic(res channelconfig.Resources) {
105+
ac, ok := res.ApplicationConfig()
106+
if !ok {
107+
peerLogger.Panicf("[channel %s] does not have application config so is incompatible", res.ConfigtxManager().ChainID())
108+
}
109+
110+
if err := ac.Capabilities().Supported(); err != nil {
111+
peerLogger.Panicf("[channel %s] incompatible %s", res.ConfigtxManager(), err)
112+
}
113+
114+
if err := res.ChannelConfig().Capabilities().Supported(); err != nil {
115+
peerLogger.Panicf("[channel %s] incompatible %s", res.ConfigtxManager(), err)
116+
}
117+
}
118+
101119
func (cs *chainSupport) Ledger() ledger.PeerLedger {
102120
return cs.ledger
103121
}
@@ -171,7 +189,7 @@ func Initialize(init func(string)) {
171189
}
172190
}
173191

174-
// Take care to initialize chain after peer joined, for example deploys system CCs
192+
// InitChain takes care to initialize chain after peer joined, for example deploys system CCs
175193
func InitChain(cid string) {
176194
if chainInitializer != nil {
177195
// Initialize chaincode, namely deploy system CC
@@ -222,6 +240,8 @@ func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block) error {
222240
return err
223241
}
224242

243+
capabilitiesSupportedOrPanic(bundle)
244+
225245
channelconfig.LogSanityChecks(bundle)
226246

227247
gossipEventer := service.GetGossipService().NewConfigEventer()

orderer/common/msgprocessor/systemchannel.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
// ChannelConfigTemplator can be used to generate config templates.
2424
type ChannelConfigTemplator interface {
2525
// NewChannelConfig creates a new template configuration manager.
26-
NewChannelConfig(env *cb.Envelope) (configtxapi.Manager, error)
26+
NewChannelConfig(env *cb.Envelope) (channelconfig.Resources, error)
2727
}
2828

2929
// SystemChannel implements the Processor interface for the system channel.
@@ -95,12 +95,12 @@ func (s *SystemChannel) ProcessConfigUpdateMsg(envConfigUpdate *cb.Envelope) (co
9595

9696
// If the channel ID does not match the system channel, then this must be a channel creation transaction
9797

98-
ctxm, err := s.templator.NewChannelConfig(envConfigUpdate)
98+
bundle, err := s.templator.NewChannelConfig(envConfigUpdate)
9999
if err != nil {
100100
return nil, 0, err
101101
}
102102

103-
newChannelConfigEnv, err := ctxm.ProposeConfigUpdate(envConfigUpdate)
103+
newChannelConfigEnv, err := bundle.ConfigtxManager().ProposeConfigUpdate(envConfigUpdate)
104104
if err != nil {
105105
return nil, 0, err
106106
}
@@ -205,7 +205,7 @@ func NewDefaultTemplator(support DefaultTemplatorSupport) *DefaultTemplator {
205205
}
206206

207207
// NewChannelConfig creates a new template channel configuration based on the current config in the ordering system channel.
208-
func (dt *DefaultTemplator) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) {
208+
func (dt *DefaultTemplator) NewChannelConfig(envConfigUpdate *cb.Envelope) (channelconfig.Resources, error) {
209209
configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload)
210210
if err != nil {
211211
return nil, fmt.Errorf("Failing initial channel config creation because of payload unmarshaling error: %s", err)
@@ -323,5 +323,5 @@ func (dt *DefaultTemplator) NewChannelConfig(envConfigUpdate *cb.Envelope) (conf
323323
return nil, err
324324
}
325325

326-
return bundle.ConfigtxManager(), nil
326+
return bundle, nil
327327
}

orderer/common/msgprocessor/systemchannel_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212

1313
"github.com/hyperledger/fabric/common/channelconfig"
1414
"github.com/hyperledger/fabric/common/configtx"
15-
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
1615
"github.com/hyperledger/fabric/common/crypto"
16+
mockchannelconfig "github.com/hyperledger/fabric/common/mocks/config"
1717
mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx"
1818
genesisconfig "github.com/hyperledger/fabric/common/tools/configtxgen/localconfig"
1919
"github.com/hyperledger/fabric/common/tools/configtxgen/provisional"
@@ -27,8 +27,10 @@ type mockSystemChannelSupport struct {
2727
NewChannelConfigErr error
2828
}
2929

30-
func (mscs *mockSystemChannelSupport) NewChannelConfig(env *cb.Envelope) (configtxapi.Manager, error) {
31-
return mscs.NewChannelConfigVal, mscs.NewChannelConfigErr
30+
func (mscs *mockSystemChannelSupport) NewChannelConfig(env *cb.Envelope) (channelconfig.Resources, error) {
31+
return &mockchannelconfig.Resources{
32+
ConfigtxManagerVal: mscs.NewChannelConfigVal,
33+
}, mscs.NewChannelConfigErr
3234
}
3335

3436
func TestProcessSystemChannelNormalMsg(t *testing.T) {

orderer/common/msgprocessor/systemchannelfilter.go

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,21 @@ SPDX-License-Identifier: Apache-2.0
77
package msgprocessor
88

99
import (
10-
"fmt"
11-
1210
"github.com/hyperledger/fabric/common/channelconfig"
13-
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
1411
cb "github.com/hyperledger/fabric/protos/common"
1512
"github.com/hyperledger/fabric/protos/utils"
1613

1714
"github.com/golang/protobuf/proto"
15+
"github.com/pkg/errors"
1816
)
1917

2018
// ChainCreator defines the methods necessary to simulate channel creation.
2119
type ChainCreator interface {
2220
// NewChannelConfig returns a template config for a new channel.
23-
NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error)
21+
NewChannelConfig(envConfigUpdate *cb.Envelope) (channelconfig.Resources, error)
22+
23+
// CreateBundle parses the config into resources
24+
CreateBundle(channelID string, config *cb.Config) (channelconfig.Resources, error)
2425

2526
// ChannelsCount returns the count of channels which currently exist.
2627
ChannelsCount() int
@@ -51,16 +52,16 @@ func (scf *SystemChainFilter) Apply(env *cb.Envelope) error {
5152

5253
err := proto.Unmarshal(env.Payload, msgData)
5354
if err != nil {
54-
return fmt.Errorf("bad payload: %s", err)
55+
return errors.Errorf("bad payload: %s", err)
5556
}
5657

5758
if msgData.Header == nil {
58-
return fmt.Errorf("missing payload header")
59+
return errors.Errorf("missing payload header")
5960
}
6061

6162
chdr, err := utils.UnmarshalChannelHeader(msgData.Header.ChannelHeader)
6263
if err != nil {
63-
return fmt.Errorf("bad channel header: %s", err)
64+
return errors.Errorf("bad channel header: %s", err)
6465
}
6566

6667
if chdr.Type != int32(cb.HeaderType_ORDERER_TRANSACTION) {
@@ -76,83 +77,85 @@ func (scf *SystemChainFilter) Apply(env *cb.Envelope) error {
7677
if maxChannels > 0 {
7778
// We check for strictly greater than to accommodate the system channel
7879
if uint64(scf.cc.ChannelsCount()) > maxChannels {
79-
return fmt.Errorf("channel creation would exceed maximimum number of channels: %d", maxChannels)
80+
return errors.Errorf("channel creation would exceed maximimum number of channels: %d", maxChannels)
8081
}
8182
}
8283

8384
configTx := &cb.Envelope{}
8485
err = proto.Unmarshal(msgData.Data, configTx)
8586
if err != nil {
86-
return fmt.Errorf("payload data error unmarshaling to envelope: %s", err)
87+
return errors.Errorf("payload data error unmarshaling to envelope: %s", err)
8788
}
8889

8990
return scf.authorizeAndInspect(configTx)
9091
}
9192

92-
func (scf *SystemChainFilter) authorize(configEnvelope *cb.ConfigEnvelope) (*cb.ConfigEnvelope, error) {
93-
if configEnvelope.LastUpdate == nil {
94-
return nil, fmt.Errorf("updated config does not include a config update")
95-
}
96-
97-
configManager, err := scf.cc.NewChannelConfig(configEnvelope.LastUpdate)
98-
if err != nil {
99-
return nil, fmt.Errorf("error constructing new channel config from update: %s", err)
100-
}
101-
102-
newChannelConfigEnv, err := configManager.ProposeConfigUpdate(configEnvelope.LastUpdate)
103-
if err != nil {
104-
return nil, fmt.Errorf("error proposing channel update to new channel config: %s", err)
105-
}
106-
107-
err = configManager.Validate(newChannelConfigEnv)
108-
if err != nil {
109-
return nil, fmt.Errorf("error applying channel update to new channel config: %s", err)
110-
}
111-
112-
return newChannelConfigEnv, nil
113-
}
114-
11593
func (scf *SystemChainFilter) authorizeAndInspect(configTx *cb.Envelope) error {
11694
payload := &cb.Payload{}
11795
err := proto.Unmarshal(configTx.Payload, payload)
11896
if err != nil {
119-
return fmt.Errorf("error unmarshaling wrapped configtx envelope payload: %s", err)
97+
return errors.Errorf("error unmarshaling wrapped configtx envelope payload: %s", err)
12098
}
12199

122100
if payload.Header == nil {
123-
return fmt.Errorf("wrapped configtx envelope missing header")
101+
return errors.Errorf("wrapped configtx envelope missing header")
124102
}
125103

126104
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
127105
if err != nil {
128-
return fmt.Errorf("error unmarshaling wrapped configtx envelope channel header: %s", err)
106+
return errors.Errorf("error unmarshaling wrapped configtx envelope channel header: %s", err)
129107
}
130108

131109
if chdr.Type != int32(cb.HeaderType_CONFIG) {
132-
return fmt.Errorf("wrapped configtx envelope not a config transaction")
110+
return errors.Errorf("wrapped configtx envelope not a config transaction")
133111
}
134112

135113
configEnvelope := &cb.ConfigEnvelope{}
136114
err = proto.Unmarshal(payload.Data, configEnvelope)
137115
if err != nil {
138-
return fmt.Errorf("error unmarshalling wrapped configtx config envelope from payload: %s", err)
116+
return errors.Errorf("error unmarshalling wrapped configtx config envelope from payload: %s", err)
117+
}
118+
119+
if configEnvelope.LastUpdate == nil {
120+
return errors.Errorf("updated config does not include a config update")
121+
}
122+
123+
res, err := scf.cc.NewChannelConfig(configEnvelope.LastUpdate)
124+
if err != nil {
125+
return errors.Errorf("error constructing new channel config from update: %s", err)
139126
}
140127

141128
// Make sure that the config was signed by the appropriate authorized entities
142-
proposedEnv, err := scf.authorize(configEnvelope)
129+
newChannelConfigEnv, err := res.ConfigtxManager().ProposeConfigUpdate(configEnvelope.LastUpdate)
143130
if err != nil {
144-
return err
131+
return errors.Errorf("error proposing channel update to new channel config: %s", err)
145132
}
146133

147134
// reflect.DeepEqual will not work here, because it considers nil and empty maps as unequal
148-
if !proto.Equal(proposedEnv.Config, configEnvelope.Config) {
149-
return fmt.Errorf("config proposed by the channel creation request did not match the config received with the channel creation request")
135+
if !proto.Equal(newChannelConfigEnv, configEnvelope) {
136+
return errors.Errorf("config proposed by the channel creation request did not match the config received with the channel creation request")
150137
}
151138

152-
// Make sure the config can be parsed into a bundle
153-
_, err = channelconfig.NewBundle(chdr.ChannelId, configEnvelope.Config)
139+
bundle, err := scf.cc.CreateBundle(res.ConfigtxManager().ChainID(), newChannelConfigEnv.Config)
154140
if err != nil {
155-
return fmt.Errorf("failed to create config bundle: %s", err)
141+
return errors.Wrap(err, "config does not validly parse")
142+
}
143+
144+
if err = res.ValidateNew(bundle); err != nil {
145+
return errors.Wrap(err, "new bundle invalid")
146+
}
147+
148+
oc, ok := bundle.OrdererConfig()
149+
if !ok {
150+
return errors.New("config is missing orderer group")
151+
}
152+
153+
if err = oc.Capabilities().Supported(); err != nil {
154+
return errors.Wrap(err, "config update is not compatible")
155+
}
156+
157+
if err = bundle.ChannelConfig().Capabilities().Supported(); err != nil {
158+
return errors.Wrap(err, "config update is not compatible")
156159
}
157160

158161
return nil

orderer/common/msgprocessor/systemchannelfilter_test.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"github.com/hyperledger/fabric/common/channelconfig"
1414
"github.com/hyperledger/fabric/common/configtx"
15-
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
1615
"github.com/hyperledger/fabric/common/crypto"
1716
mockconfig "github.com/hyperledger/fabric/common/mocks/config"
1817
mockconfigtx "github.com/hyperledger/fabric/common/mocks/configtx"
@@ -85,15 +84,31 @@ func (mcc *mockChainCreator) ChannelsCount() int {
8584
return len(mcc.newChains)
8685
}
8786

88-
func (mcc *mockChainCreator) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) {
87+
func (mcc *mockChainCreator) CreateBundle(channelID string, config *cb.Config) (channelconfig.Resources, error) {
88+
return &mockconfig.Resources{
89+
ConfigtxManagerVal: &mockconfigtx.Manager{
90+
ChainIDVal: channelID,
91+
},
92+
OrdererConfigVal: &mockconfig.Orderer{
93+
CapabilitiesVal: &mockconfig.OrdererCapabilities{},
94+
},
95+
ChannelConfigVal: &mockconfig.Channel{
96+
CapabilitiesVal: &mockconfig.ChannelCapabilities{},
97+
},
98+
}, nil
99+
}
100+
101+
func (mcc *mockChainCreator) NewChannelConfig(envConfigUpdate *cb.Envelope) (channelconfig.Resources, error) {
89102
if mcc.NewChannelConfigErr != nil {
90103
return nil, mcc.NewChannelConfigErr
91104
}
92105
confUpdate := configtx.UnmarshalConfigUpdateOrPanic(configtx.UnmarshalConfigUpdateEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(envConfigUpdate.Payload).Data).ConfigUpdate)
93-
return &mockconfigtx.Manager{
94-
ProposeConfigUpdateVal: &cb.ConfigEnvelope{
95-
Config: &cb.Config{Sequence: 1, ChannelGroup: confUpdate.WriteSet},
96-
LastUpdate: envConfigUpdate,
106+
return &mockconfig.Resources{
107+
ConfigtxManagerVal: &mockconfigtx.Manager{
108+
ProposeConfigUpdateVal: &cb.ConfigEnvelope{
109+
Config: &cb.Config{Sequence: 1, ChannelGroup: confUpdate.WriteSet},
110+
LastUpdate: envConfigUpdate,
111+
},
97112
},
98113
}, nil
99114
}

orderer/common/multichannel/chainsupport.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/hyperledger/fabric/orderer/consensus"
1515
cb "github.com/hyperledger/fabric/protos/common"
1616
"github.com/hyperledger/fabric/protos/utils"
17+
18+
"github.com/pkg/errors"
1719
)
1820

1921
// ChainSupport holds the resources for a particular channel.
@@ -101,10 +103,16 @@ func (cs *ChainSupport) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEn
101103
if err != nil {
102104
return nil, err
103105
}
106+
104107
bundle, err := cs.CreateBundle(cs.ChainID(), env.Config)
105108
if err != nil {
106109
return nil, err
107110
}
111+
112+
if err = checkResources(bundle); err != nil {
113+
return nil, errors.Wrap(err, "config update is not compatible")
114+
}
115+
108116
return env, cs.ValidateNew(bundle)
109117
}
110118

0 commit comments

Comments
 (0)