Skip to content

Commit 150d17e

Browse files
committed
[FAB-3493] Fix LAST_CONFIG value on new channels
The recent consortium-related stack of changes touched on the channel creation path and introduced a bug in the LAST_CONFIG value that is set on all on blocks between the genesis and the first actual config block. Specifically, their LAST_CONFIG value is set to 1 when it should be set to 0 (i.e. point to the genesis block). This changeset: 1. Extends a test so that this check is performed 2. Fixes this bug in a minimally invasive manner, and modifies all dependent tests accordingly. Details behind the bug, as well as the thinking behind this fix can be found in the JIRA issue. Change-Id: Ief105744503df21d289b1e2113c9038998bdda92 Signed-off-by: Kostas Christidis <[email protected]>
1 parent f3c61e6 commit 150d17e

15 files changed

+175
-100
lines changed

common/configtx/manager.go

+15-6
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,24 @@ func (cm *configManager) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigE
175175

176176
func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]comparable, *configResult, error) {
177177
if configEnv == nil {
178-
return nil, nil, fmt.Errorf("Attempted to apply config with nil envelope")
178+
return nil, nil, fmt.Errorf("attempted to apply config with nil envelope")
179179
}
180180

181181
if configEnv.Config == nil {
182-
return nil, nil, fmt.Errorf("Config cannot be nil")
182+
return nil, nil, fmt.Errorf("config cannot be nil")
183183
}
184184

185-
if configEnv.Config.Sequence != cm.current.sequence+1 {
186-
return nil, nil, fmt.Errorf("Config at sequence %d, cannot prepare to update to %d", cm.current.sequence, configEnv.Config.Sequence)
185+
var expectedSequence uint64
186+
if configEnv.Config.NewChannel {
187+
expectedSequence = FixNewChannelConfig(configEnv).Config.Sequence
188+
if configEnv.Config.Sequence != expectedSequence {
189+
return nil, nil, fmt.Errorf("should prepare to update to %d (new channel) got %d instead", expectedSequence, configEnv.Config.Sequence)
190+
}
191+
} else {
192+
expectedSequence = cm.current.sequence + 1
193+
if configEnv.Config.Sequence != expectedSequence {
194+
return nil, nil, fmt.Errorf("expected sequence %d (config at: %d), cannot prepare to update to %d", expectedSequence, cm.current.sequence, configEnv.Config.Sequence)
195+
}
187196
}
188197

189198
configUpdateEnv, err := envelopeToConfigUpdate(configEnv.LastUpdate)
@@ -198,11 +207,11 @@ func (cm *configManager) prepareApply(configEnv *cb.ConfigEnvelope) (map[string]
198207

199208
channelGroup, err := configMapToConfig(configMap)
200209
if err != nil {
201-
return nil, nil, fmt.Errorf("Could not turn configMap back to channelGroup: %s", err)
210+
return nil, nil, fmt.Errorf("could not turn configMap back to channelGroup: %s", err)
202211
}
203212

204213
if !reflect.DeepEqual(channelGroup, configEnv.Config.ChannelGroup) {
205-
return nil, nil, fmt.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
214+
return nil, nil, fmt.Errorf("configEnvelope LastUpdate did not produce the supplied config result")
206215
}
207216

208217
result, err := cm.processConfig(channelGroup)

common/configtx/util.go

+15
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,18 @@ func UnmarshalConfigEnvelopeOrPanic(data []byte) *cb.ConfigEnvelope {
9797
}
9898
return result
9999
}
100+
101+
// FixNewChannelConfig is to be applied on messages of type ConfigEnvelope that
102+
// are used to create a new channel. It returns a ConfigEnvelope whose
103+
// Config.Sequence and Config.NewChannel fields are set so that the config
104+
// manager of the new channel starts at the right sequence number.
105+
func FixNewChannelConfig(configEnv *cb.ConfigEnvelope) *cb.ConfigEnvelope {
106+
properSequence := uint64(0)
107+
if configEnv.Config.Sequence != properSequence {
108+
logger.Debugf("Received a new channel config env whose sequence is incorrectly set to %d, setting to %d instead",
109+
configEnv.Config.Sequence, properSequence)
110+
}
111+
configEnv.Config.Sequence = properSequence
112+
configEnv.Config.NewChannel = true
113+
return configEnv
114+
}

common/configtx/util_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ package configtx
1919
import (
2020
"math/rand"
2121
"testing"
22+
23+
cb "github.com/hyperledger/fabric/protos/common"
24+
25+
"github.com/stretchr/testify/assert"
2226
)
2327

2428
// TestValidchainID checks that the constraints on chain IDs are enforced properly
@@ -59,6 +63,20 @@ func TestValidChainID(t *testing.T) {
5963
})
6064
}
6165

66+
// TestFixNewChannelConfig checks that returned config envelope has its Sequence
67+
// and NewChannel fields set appropriately.
68+
func TestFixNewChannelConfig(t *testing.T) {
69+
expectedSequenceValue := uint64(0)
70+
expectedNewChannelValue := true
71+
72+
sampleConfigEnvelope := &cb.ConfigEnvelope{Config: &cb.Config{Sequence: uint64(rand.Uint32())}}
73+
returnedConfigEnvelope := FixNewChannelConfig(sampleConfigEnvelope)
74+
75+
assert.Equal(t, expectedSequenceValue, returnedConfigEnvelope.Config.Sequence, "Sequence should be zero %d", expectedSequenceValue)
76+
assert.NotNil(t, returnedConfigEnvelope.Config.NewChannel, "NewChannel field should be set")
77+
assert.Equal(t, expectedNewChannelValue, returnedConfigEnvelope.Config.NewChannel, "NewChannel field should be set to %t", expectedNewChannelValue)
78+
}
79+
6280
// Helper functions
6381

6482
func randomAlphaString(size int) string {

common/mocks/configtx/configtx.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ type Manager struct {
177177
// ProposeConfigUpdateError is returned as the error value for ProposeConfigUpdate
178178
ProposeConfigUpdateError error
179179

180-
// ProposeConfigUpdateVal is returns as the value for ProposeConfigUpdate
180+
// ProposeConfigUpdateVal returns as the value for ProposeConfigUpdate
181181
ProposeConfigUpdateVal *cb.ConfigEnvelope
182182

183183
// ConfigEnvelopeVal is returned as the value for ConfigEnvelope()

orderer/common/broadcast/broadcast.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func (bh *handlerImpl) Handle(srv ab.AtomicBroadcast_BroadcastServer) error {
162162
}
163163

164164
if logger.IsEnabledFor(logging.DEBUG) {
165-
logger.Debugf("Broadcast has successfully enqueued message of type %d for chain %s", chdr.Type, chdr.ChannelId)
165+
logger.Debugf("Broadcast has successfully enqueued message of type %s for chain %s", cb.HeaderType_name[chdr.Type], chdr.ChannelId)
166166
}
167167

168168
err = srv.Send(&ab.BroadcastResponse{Status: cb.Status_SUCCESS})

orderer/configupdate/configupdate.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package configupdate
2323
import (
2424
"fmt"
2525

26+
"github.com/hyperledger/fabric/common/configtx"
2627
configtxapi "github.com/hyperledger/fabric/common/configtx/api"
2728
"github.com/hyperledger/fabric/common/crypto"
2829
cb "github.com/hyperledger/fabric/protos/common"
@@ -141,7 +142,7 @@ func (p *Processor) newChannelConfig(channelID string, envConfigUpdate *cb.Envel
141142
return nil, err
142143
}
143144

144-
newChannelEnvConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, newChannelConfigEnv, msgVersion, epoch)
145+
newChannelEnvConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, p.signer, configtx.FixNewChannelConfig(newChannelConfigEnv), msgVersion, epoch)
145146
if err != nil {
146147
return nil, err
147148
}

orderer/configupdate/configupdate_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ func (msm *mockSupportManager) GetChain(chainID string) (Support, bool) {
5858
func (msm *mockSupportManager) NewChannelConfig(env *cb.Envelope) (configtxapi.Manager, error) {
5959
return &mockconfigtx.Manager{
6060
ProposeConfigUpdateVal: &cb.ConfigEnvelope{
61+
Config: &cb.Config{
62+
Sequence: 0,
63+
},
6164
LastUpdate: env,
6265
},
6366
}, nil

orderer/multichain/chainsupport.go

+3
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ func (cs *chainSupport) addLastConfigSignature(block *cb.Block) {
231231
}
232232

233233
lastConfigValue := utils.MarshalOrPanic(&cb.LastConfig{Index: cs.lastConfig})
234+
logger.Debugf("[channel: %s] About to write block, setting its LAST_CONFIG to %d", cs.ChainID(), cs.lastConfig)
234235

235236
lastConfigSignature.Signature = utils.SignOrPanic(cs.signer, util.ConcatenateBytes(lastConfigValue, lastConfigSignature.SignatureHeader, block.Header.Bytes()))
236237

@@ -257,6 +258,8 @@ func (cs *chainSupport) WriteBlock(block *cb.Block, committers []filter.Committe
257258
if err != nil {
258259
logger.Panicf("[channel: %s] Could not append block: %s", cs.ChainID(), err)
259260
}
261+
262+
logger.Debugf("[channel: %s] Wrote block %d", cs.ChainID(), block.GetHeader().Number)
260263
return block
261264
}
262265

orderer/multichain/manager.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ func (ml *multiLedger) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxa
290290
channelGroup.Groups[config.ApplicationGroupKey] = applicationGroup
291291
channelGroup.Values[config.ConsortiumKey] = config.TemplateConsortium(consortium.Name).Values[config.ConsortiumKey]
292292

293-
templateConfig, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, configUpdate.ChannelId, ml.signer, &cb.ConfigEnvelope{
293+
templateConfig, _ := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, configUpdate.ChannelId, ml.signer, &cb.ConfigEnvelope{
294294
Config: &cb.Config{
295295
ChannelGroup: channelGroup,
296296
},

orderer/multichain/manager_test.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"testing"
2222
"time"
2323

24+
"github.com/golang/protobuf/proto"
2425
"github.com/hyperledger/fabric/common/configtx"
2526
genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
2627
"github.com/hyperledger/fabric/common/configtx/tool/provisional"
@@ -253,7 +254,7 @@ func TestNewChain(t *testing.T) {
253254
configEnv, err := cm.ProposeConfigUpdate(envConfigUpdate)
254255
assert.NoError(t, err, "Proposing initial update")
255256

256-
ingressTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch)
257+
ingressTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configtx.FixNewChannelConfig(configEnv), msgVersion, epoch)
257258
assert.NoError(t, err, "Creating ingresstx")
258259

259260
wrapped := wrapConfigTx(ingressTx)
@@ -315,6 +316,16 @@ func TestNewChain(t *testing.T) {
315316
if status != cb.Status_SUCCESS {
316317
t.Fatalf("Could not retrieve block on new chain")
317318
}
319+
320+
// Inspect LAST_CONFIG value
321+
metadataItem := &cb.Metadata{}
322+
err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadataItem)
323+
assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item")
324+
lastConfig := &cb.LastConfig{}
325+
err = proto.Unmarshal(metadataItem.Value, lastConfig)
326+
assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value")
327+
assert.Equal(t, uint64(0), lastConfig.Index, "LAST_CONFIG value should point to genesis block")
328+
318329
for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ {
319330
if !reflect.DeepEqual(utils.ExtractEnvelopeOrPanic(block, i), messages[i]) {
320331
t.Errorf("Block contents wrong at index %d in new chain", i)

orderer/multichain/systemchain.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (scf *systemChainFilter) authorize(configEnvelope *cb.ConfigEnvelope) (conf
129129
return nil, err
130130
}
131131

132-
err = configManager.Apply(newChannelConfigEnv)
132+
err = configManager.Apply(configtx.FixNewChannelConfig(newChannelConfigEnv))
133133
if err != nil {
134134
return nil, err
135135
}

orderer/multichain/systemchain_test.go

+40-40
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"github.com/stretchr/testify/assert"
3333
)
3434

35+
var newChannelID = "newChannel"
36+
3537
type mockSupport struct {
3638
msc *mockconfig.Orderer
3739
}
@@ -71,96 +73,94 @@ func (mcc *mockChainCreator) NewChannelConfig(envConfigUpdate *cb.Envelope) (con
7173
if mcc.NewChannelConfigErr != nil {
7274
return nil, mcc.NewChannelConfigErr
7375
}
74-
confUpdate := configtx.UnmarshalConfigUpdateOrPanic(configtx.UnmarshalConfigUpdateEnvelopeOrPanic(utils.UnmarshalPayloadOrPanic(envConfigUpdate.Payload).Data).ConfigUpdate)
75-
return &mockconfigtx.Manager{
76-
ConfigEnvelopeVal: &cb.ConfigEnvelope{
77-
Config: &cb.Config{Sequence: 1, ChannelGroup: confUpdate.WriteSet},
78-
LastUpdate: envConfigUpdate,
79-
},
80-
}, nil
76+
77+
pldConfigUpdate := utils.UnmarshalPayloadOrPanic(envConfigUpdate.Payload)
78+
configUpdateEnv := configtx.UnmarshalConfigUpdateEnvelopeOrPanic(pldConfigUpdate.Data)
79+
configUpdate := configtx.UnmarshalConfigUpdateOrPanic(configUpdateEnv.ConfigUpdate)
80+
81+
configEnvelopeVal := &cb.ConfigEnvelope{
82+
Config: &cb.Config{ChannelGroup: configUpdate.WriteSet},
83+
}
84+
85+
proposeConfigUpdateVal := configEnvelopeVal
86+
proposeConfigUpdateVal.Config.Sequence = 1
87+
proposeConfigUpdateVal.LastUpdate = envConfigUpdate
88+
89+
ctxm := &mockconfigtx.Manager{
90+
ConfigEnvelopeVal: configEnvelopeVal,
91+
ProposeConfigUpdateVal: proposeConfigUpdateVal,
92+
}
93+
94+
return ctxm, nil
8195
}
8296

8397
func TestGoodProposal(t *testing.T) {
84-
newChainID := "NewChainID"
85-
8698
mcc := newMockChainCreator()
8799

88-
configEnv, err := configtx.NewCompositeTemplate(
100+
configUpdateEnv, _ := configtx.NewCompositeTemplate(
89101
configtx.NewSimpleTemplate(
90102
config.DefaultHashingAlgorithm(),
91103
config.DefaultBlockDataHashingStructure(),
92104
config.TemplateOrdererAddresses([]string{"foo"}),
93105
),
94106
configtx.NewChainCreationTemplate("SampleConsortium", []string{}),
95-
).Envelope(newChainID)
96-
if err != nil {
97-
t.Fatalf("Error constructing configtx")
98-
}
99-
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv)
100-
wrapped := wrapConfigTx(ingressTx)
107+
).Envelope(newChannelID)
101108

102-
sysFilter := newSystemChainFilter(mcc.ms, mcc)
103-
action, committer := sysFilter.Apply(wrapped)
109+
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChannelID, configUpdateEnv, true)
110+
ordererTx := wrapConfigTx(ingressTx)
104111

112+
sysFilter := newSystemChainFilter(mcc.ms, mcc)
113+
action, committer := sysFilter.Apply(ordererTx)
105114
assert.EqualValues(t, action, filter.Accept, "Did not accept valid transaction")
106115
assert.True(t, committer.Isolated(), "Channel creation belong in its own block")
107116

108117
committer.Commit()
109118
assert.Len(t, mcc.newChains, 1, "Proposal should only have created 1 new chain")
110-
111119
assert.Equal(t, ingressTx, mcc.newChains[0], "New chain should have been created with ingressTx")
112120
}
113121

114122
func TestProposalRejectedByConfig(t *testing.T) {
115-
newChainID := "NewChainID"
116-
117123
mcc := newMockChainCreator()
118124
mcc.NewChannelConfigErr = fmt.Errorf("Error creating channel")
119125

120-
configEnv, err := configtx.NewCompositeTemplate(
126+
configUpdateEnv, _ := configtx.NewCompositeTemplate(
121127
configtx.NewSimpleTemplate(
122128
config.DefaultHashingAlgorithm(),
123129
config.DefaultBlockDataHashingStructure(),
124130
config.TemplateOrdererAddresses([]string{"foo"}),
125131
),
126132
configtx.NewChainCreationTemplate("SampleConsortium", []string{}),
127-
).Envelope(newChainID)
128-
if err != nil {
129-
t.Fatalf("Error constructing configtx")
130-
}
131-
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv)
132-
wrapped := wrapConfigTx(ingressTx)
133+
).Envelope(newChannelID)
134+
135+
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChannelID, configUpdateEnv, true)
136+
ordererTx := wrapConfigTx(ingressTx)
133137

134138
sysFilter := newSystemChainFilter(mcc.ms, mcc)
135-
action, _ := sysFilter.Apply(wrapped)
139+
action, _ := sysFilter.Apply(ordererTx)
136140

137-
assert.EqualValues(t, action, filter.Reject, "Did not accept valid transaction")
141+
assert.EqualValues(t, action, filter.Reject, "Did not reject invalid transaction")
138142
assert.Len(t, mcc.newChains, 0, "Proposal should not have created a new chain")
139143
}
140144

141145
func TestNumChainsExceeded(t *testing.T) {
142-
newChainID := "NewChainID"
143-
144146
mcc := newMockChainCreator()
145147
mcc.ms.msc.MaxChannelsCountVal = 1
146148
mcc.newChains = make([]*cb.Envelope, 2)
147149

148-
configEnv, err := configtx.NewCompositeTemplate(
150+
configUpdateEnv, _ := configtx.NewCompositeTemplate(
149151
configtx.NewSimpleTemplate(
150152
config.DefaultHashingAlgorithm(),
151153
config.DefaultBlockDataHashingStructure(),
152154
config.TemplateOrdererAddresses([]string{"foo"}),
153155
),
154156
configtx.NewChainCreationTemplate("SampleConsortium", []string{}),
155-
).Envelope(newChainID)
156-
if err != nil {
157-
t.Fatalf("Error constructing configtx")
158-
}
159-
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv)
160-
wrapped := wrapConfigTx(ingressTx)
157+
).Envelope(newChannelID)
158+
159+
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChannelID, configUpdateEnv, true)
160+
ordererTx := wrapConfigTx(ingressTx)
161161

162162
sysFilter := newSystemChainFilter(mcc.ms, mcc)
163-
action, _ := sysFilter.Apply(wrapped)
163+
action, _ := sysFilter.Apply(ordererTx)
164164

165165
assert.EqualValues(t, filter.Reject, action, "Transaction had created too many channels")
166166
}

orderer/multichain/util_test.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func makeConfigTx(chainID string, i int) *cb.Envelope {
9393
if err != nil {
9494
panic(err)
9595
}
96-
return makeConfigTxFromConfigUpdateEnvelope(chainID, configEnv)
96+
return makeConfigTxFromConfigUpdateEnvelope(chainID, configEnv, false)
9797
}
9898

9999
func wrapConfigTx(env *cb.Envelope) *cb.Envelope {
@@ -104,18 +104,30 @@ func wrapConfigTx(env *cb.Envelope) *cb.Envelope {
104104
return result
105105
}
106106

107-
func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.ConfigUpdateEnvelope) *cb.Envelope {
107+
func makeConfigTxFromConfigUpdateEnvelope(chainID string, configUpdateEnv *cb.ConfigUpdateEnvelope, newChannel bool) *cb.Envelope {
108108
configUpdateTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, chainID, mockCrypto(), configUpdateEnv, msgVersion, epoch)
109109
if err != nil {
110110
panic(err)
111111
}
112-
configTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, chainID, mockCrypto(), &cb.ConfigEnvelope{
113-
Config: &cb.Config{Sequence: 1, ChannelGroup: configtx.UnmarshalConfigUpdateOrPanic(configUpdateEnv.ConfigUpdate).WriteSet},
114-
LastUpdate: configUpdateTx},
112+
113+
configEnv := &cb.ConfigEnvelope{
114+
Config: &cb.Config{
115+
Sequence: 1,
116+
ChannelGroup: configtx.UnmarshalConfigUpdateOrPanic(configUpdateEnv.ConfigUpdate).WriteSet,
117+
},
118+
LastUpdate: configUpdateTx,
119+
}
120+
121+
if newChannel {
122+
configEnv = configtx.FixNewChannelConfig(configEnv)
123+
}
124+
125+
configTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, chainID, mockCrypto(), configEnv,
115126
msgVersion, epoch)
116127
if err != nil {
117128
panic(err)
118129
}
130+
119131
return configTx
120132
}
121133

0 commit comments

Comments
 (0)