Skip to content

Commit 1219131

Browse files
author
Jason Yellick
committed
[FAB-2364] Create common orderer configupdate path
https://jira.hyperledger.org/browse/FAB-2364 The multichannel code has a special handling for config update messages, but this same config update mechanism needs to be used for channel reconfiguration. This changeset adds a new set of common configuration update processing code. It does not hook it into the broadcast path, or remove the duplicated functionality in the multichain package, so as to make this CR easier to review. Change-Id: Idc746c666a455bfba917ec88a1acbec0a2e1d33e Signed-off-by: Jason Yellick <[email protected]>
1 parent 3c812af commit 1219131

File tree

4 files changed

+367
-0
lines changed

4 files changed

+367
-0
lines changed

common/configtx/util.go

+36
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ func UnmarshalConfig(data []byte) (*cb.Config, error) {
3535
return config, nil
3636
}
3737

38+
// UnmarshalConfig attempts to unmarshal bytes to a *cb.Config
39+
func UnmarshalConfigOrPanic(data []byte) *cb.Config {
40+
result, err := UnmarshalConfig(data)
41+
if err != nil {
42+
panic(err)
43+
}
44+
return result
45+
}
46+
3847
// UnmarshalConfigUpdate attempts to unmarshal bytes to a *cb.ConfigUpdate
3948
func UnmarshalConfigUpdate(data []byte) (*cb.ConfigUpdate, error) {
4049
configUpdate := &cb.ConfigUpdate{}
@@ -45,6 +54,15 @@ func UnmarshalConfigUpdate(data []byte) (*cb.ConfigUpdate, error) {
4554
return configUpdate, nil
4655
}
4756

57+
// UnmarshalConfigUpdate attempts to unmarshal bytes to a *cb.ConfigUpdate or panics
58+
func UnmarshalConfigUpdateOrPanic(data []byte) *cb.ConfigUpdate {
59+
result, err := UnmarshalConfigUpdate(data)
60+
if err != nil {
61+
panic(err)
62+
}
63+
return result
64+
}
65+
4866
// UnmarshalConfigUpdateEnvelope attempts to unmarshal bytes to a *cb.ConfigUpdate
4967
func UnmarshalConfigUpdateEnvelope(data []byte) (*cb.ConfigUpdateEnvelope, error) {
5068
configUpdateEnvelope := &cb.ConfigUpdateEnvelope{}
@@ -55,6 +73,15 @@ func UnmarshalConfigUpdateEnvelope(data []byte) (*cb.ConfigUpdateEnvelope, error
5573
return configUpdateEnvelope, nil
5674
}
5775

76+
// UnmarshalConfigUpdateEnvelope attempts to unmarshal bytes to a *cb.ConfigUpdateEnvelope or panics
77+
func UnmarshalConfigUpdateEnvelopeOrPanic(data []byte) *cb.ConfigUpdateEnvelope {
78+
result, err := UnmarshalConfigUpdateEnvelope(data)
79+
if err != nil {
80+
panic(err)
81+
}
82+
return result
83+
}
84+
5885
// UnmarshalConfigEnvelope attempts to unmarshal bytes to a *cb.ConfigEnvelope
5986
func UnmarshalConfigEnvelope(data []byte) (*cb.ConfigEnvelope, error) {
6087
configEnv := &cb.ConfigEnvelope{}
@@ -65,6 +92,15 @@ func UnmarshalConfigEnvelope(data []byte) (*cb.ConfigEnvelope, error) {
6592
return configEnv, nil
6693
}
6794

95+
// UnmarshalConfigEnvelope attempts to unmarshal bytes to a *cb.ConfigEnvelope or panics
96+
func UnmarshalConfigEnvelopeOrPanic(data []byte) *cb.ConfigEnvelope {
97+
result, err := UnmarshalConfigEnvelope(data)
98+
if err != nil {
99+
panic(err)
100+
}
101+
return result
102+
}
103+
68104
// ConfigEnvelopeFromBlock extract the config envelope from a config block
69105
func ConfigEnvelopeFromBlock(block *cb.Block) (*cb.ConfigEnvelope, error) {
70106
if block.Data == nil || len(block.Data.Data) != 1 {

orderer/configupdate/configupdate.go

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
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.
15+
*/
16+
17+
// configupdate is an implementation of the broadcast.Proccessor interface
18+
// It facilitates the preprocessing of CONFIG_UPDATE transactions which can
19+
// generate either new CONFIG transactions or new channel creation
20+
// ORDERER_TRANSACTION messages.
21+
package configupdate
22+
23+
import (
24+
"fmt"
25+
26+
"github.com/hyperledger/fabric/common/configtx"
27+
"github.com/hyperledger/fabric/common/crypto"
28+
cb "github.com/hyperledger/fabric/protos/common"
29+
"github.com/hyperledger/fabric/protos/utils"
30+
31+
"github.com/op/go-logging"
32+
)
33+
34+
var logger = logging.MustGetLogger("orderer/multichain")
35+
36+
const (
37+
// These should eventually be derived from the channel support once enabled
38+
msgVersion = int32(0)
39+
epoch = 0
40+
)
41+
42+
// SupportManager provides a way for the Handler to look up the Support for a chain
43+
type SupportManager interface {
44+
// GetChain gets the chain support for a given ChannelId
45+
GetChain(chainID string) (Support, bool)
46+
}
47+
48+
// Support enumerates a subset of the full channel support function which is required for this package
49+
type Support interface {
50+
// ProposeConfigUpdate applies a CONFIG_UPDATE to an existing config to produce a *cb.ConfigEnvelope
51+
ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error)
52+
}
53+
54+
type Processor struct {
55+
signer crypto.LocalSigner
56+
manager SupportManager
57+
systemChannelID string
58+
}
59+
60+
func New(systemChannelID string, supportManager SupportManager, signer crypto.LocalSigner) *Processor {
61+
return &Processor{
62+
systemChannelID: systemChannelID,
63+
manager: supportManager,
64+
signer: signer,
65+
}
66+
}
67+
68+
func channelID(env *cb.Envelope) (string, error) {
69+
envPayload, err := utils.UnmarshalPayload(env.Payload)
70+
if err != nil {
71+
return "", fmt.Errorf("Failing to process config update because of payload unmarshaling error: %s", err)
72+
}
73+
74+
if envPayload.Header == nil || envPayload.Header.ChannelHeader == nil || envPayload.Header.ChannelHeader.ChannelId == "" {
75+
return "", fmt.Errorf("Failing to process config update because no channel ID was set")
76+
}
77+
78+
return envPayload.Header.ChannelHeader.ChannelId, nil
79+
}
80+
81+
// Process takes in an envelope of type CONFIG_UPDATE and proceses it
82+
// to transform it either into to a new channel creation request, or
83+
// into a channel CONFIG transaction (or errors on failure)
84+
func (p *Processor) Process(envConfigUpdate *cb.Envelope) (*cb.Envelope, error) {
85+
channelID, err := channelID(envConfigUpdate)
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
support, ok := p.manager.GetChain(channelID)
91+
if ok {
92+
logger.Debugf("Processing channel reconfiguration request for channel %s", channelID)
93+
return p.existingChannelConfig(envConfigUpdate, channelID, support)
94+
}
95+
96+
logger.Debugf("Processing channel creation request for channel %s", channelID)
97+
return p.newChannelConfig(channelID, envConfigUpdate)
98+
}
99+
100+
func (p *Processor) existingChannelConfig(envConfigUpdate *cb.Envelope, channelID string, support Support) (*cb.Envelope, error) {
101+
configEnvelope, err := support.ProposeConfigUpdate(envConfigUpdate)
102+
if err != nil {
103+
return nil, err
104+
}
105+
106+
return utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, configEnvelope, msgVersion, epoch)
107+
}
108+
109+
func createInitialConfig(envConfigUpdate *cb.Envelope) (*cb.ConfigEnvelope, error) {
110+
// TODO, for now, this assumes the update contains the entire initial config
111+
// in the future, a subset of config needs to be allowed
112+
113+
configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload)
114+
if err != nil {
115+
return nil, fmt.Errorf("Failing initial channel config creation because of payload unmarshaling error: %s", err)
116+
}
117+
118+
configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data)
119+
if err != nil {
120+
return nil, fmt.Errorf("Failing initial channel config creation because of config update envelope unmarshaling error: %s", err)
121+
}
122+
123+
configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
124+
if err != nil {
125+
return nil, fmt.Errorf("Failing initial channel config creation because of config update unmarshaling error: %s", err)
126+
}
127+
128+
return &cb.ConfigEnvelope{
129+
Config: &cb.Config{
130+
Header: configUpdate.Header,
131+
Channel: configUpdate.WriteSet,
132+
},
133+
134+
LastUpdate: envConfigUpdate,
135+
}, nil
136+
}
137+
138+
func (p *Processor) newChannelConfig(channelID string, envConfigUpdate *cb.Envelope) (*cb.Envelope, error) {
139+
initialConfig, err := createInitialConfig(envConfigUpdate)
140+
if err != nil {
141+
return nil, err
142+
}
143+
144+
envConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, initialConfig, msgVersion, epoch)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
return utils.CreateSignedEnvelope(cb.HeaderType_ORDERER_TRANSACTION, p.systemChannelID, p.signer, envConfig, msgVersion, epoch)
150+
}
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
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.
15+
*/
16+
17+
package configupdate
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
"github.com/hyperledger/fabric/common/configtx"
24+
mockcrypto "github.com/hyperledger/fabric/common/mocks/crypto"
25+
cb "github.com/hyperledger/fabric/protos/common"
26+
"github.com/hyperledger/fabric/protos/utils"
27+
28+
"github.com/op/go-logging"
29+
"github.com/stretchr/testify/assert"
30+
)
31+
32+
func init() {
33+
logging.SetLevel(logging.DEBUG, "")
34+
}
35+
36+
type mockSupport struct {
37+
ProposeConfigUpdateVal *cb.ConfigEnvelope
38+
}
39+
40+
func (ms *mockSupport) ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error) {
41+
var err error
42+
if ms.ProposeConfigUpdateVal == nil {
43+
err = fmt.Errorf("Nil result implies error in mock")
44+
}
45+
return ms.ProposeConfigUpdateVal, err
46+
}
47+
48+
type mockSupportManager struct {
49+
GetChainVal *mockSupport
50+
}
51+
52+
func (msm *mockSupportManager) GetChain(chainID string) (Support, bool) {
53+
return msm.GetChainVal, msm.GetChainVal != nil
54+
}
55+
56+
func TestChannelID(t *testing.T) {
57+
makeEnvelope := func(payload *cb.Payload) *cb.Envelope {
58+
return &cb.Envelope{
59+
Payload: utils.MarshalOrPanic(payload),
60+
}
61+
}
62+
63+
testChannelID := "foo"
64+
65+
result, err := channelID(makeEnvelope(&cb.Payload{
66+
Header: &cb.Header{
67+
ChannelHeader: &cb.ChannelHeader{
68+
ChannelId: testChannelID,
69+
},
70+
},
71+
}))
72+
assert.NoError(t, err, "Channel ID was present")
73+
assert.Equal(t, testChannelID, result, "Channel ID was present")
74+
75+
_, err = channelID(makeEnvelope(&cb.Payload{
76+
Header: &cb.Header{
77+
ChannelHeader: &cb.ChannelHeader{},
78+
},
79+
}))
80+
assert.Error(t, err, "Channel ID was empty")
81+
82+
_, err = channelID(makeEnvelope(&cb.Payload{
83+
Header: &cb.Header{},
84+
}))
85+
assert.Error(t, err, "ChannelHeader was missing")
86+
87+
_, err = channelID(makeEnvelope(&cb.Payload{}))
88+
assert.Error(t, err, "Header was missing")
89+
90+
_, err = channelID(&cb.Envelope{})
91+
assert.Error(t, err, "Payload was missing")
92+
}
93+
94+
const systemChannelID = "system_channel"
95+
const testUpdateChannelID = "update_channel"
96+
97+
func newTestInstance() (*mockSupportManager, *Processor) {
98+
msm := &mockSupportManager{}
99+
return msm, New(systemChannelID, msm, mockcrypto.FakeLocalSigner)
100+
}
101+
102+
func testConfigUpdate() *cb.Envelope {
103+
ch := &cb.ChannelHeader{
104+
ChannelId: testUpdateChannelID,
105+
}
106+
107+
return &cb.Envelope{
108+
Payload: utils.MarshalOrPanic(&cb.Payload{
109+
Header: &cb.Header{
110+
ChannelHeader: ch,
111+
},
112+
Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{
113+
ConfigUpdate: utils.MarshalOrPanic(&cb.ConfigUpdate{
114+
Header: ch,
115+
WriteSet: cb.NewConfigGroup(),
116+
}),
117+
}),
118+
}),
119+
}
120+
}
121+
122+
func TestExistingChannel(t *testing.T) {
123+
msm, p := newTestInstance()
124+
125+
testUpdate := testConfigUpdate()
126+
127+
dummyResult := &cb.ConfigEnvelope{LastUpdate: &cb.Envelope{Payload: []byte("DUMMY")}}
128+
129+
msm.GetChainVal = &mockSupport{ProposeConfigUpdateVal: dummyResult}
130+
env, err := p.Process(testUpdate)
131+
assert.NoError(t, err, "Valid config update")
132+
_ = utils.UnmarshalPayloadOrPanic(env.Payload)
133+
assert.Equal(t, dummyResult, configtx.UnmarshalConfigEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(env.Payload).Data), "Valid config update")
134+
135+
msm.GetChainVal = &mockSupport{}
136+
_, err = p.Process(testUpdate)
137+
assert.Error(t, err, "Invald ProposeUpdate result")
138+
}
139+
140+
func TestNewChannel(t *testing.T) {
141+
_, p := newTestInstance()
142+
143+
testUpdate := testConfigUpdate()
144+
145+
env, err := p.Process(testUpdate)
146+
assert.NoError(t, err, "Valid config update")
147+
148+
resultChan, err := channelID(env)
149+
assert.NoError(t, err, "Invalid envelope produced")
150+
151+
assert.Equal(t, systemChannelID, resultChan, "Wrapper TX should be bound for system channel")
152+
assert.Equal(t, int32(cb.HeaderType_ORDERER_TRANSACTION), utils.UnmarshalPayloadOrPanic(env.Payload).Header.ChannelHeader.Type, "Wrong wrapper tx type")
153+
}

0 commit comments

Comments
 (0)