Skip to content

Commit aa8d06b

Browse files
kopaygorodskydenyeart
authored andcommitted
Do not create new chain of type etcdraft.Chain if such exists in map of chains. This can happen when in Raft protocol a channel was created, but not marked as done in WAL logs, so at orderer startup it will try to rerun creation tx and panic because the channel already exists.
Signed-off-by: Vladyslav Kopaihorodskyi <[email protected]>
1 parent 8999ce7 commit aa8d06b

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

orderer/common/multichannel/registrar.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/hyperledger/fabric/orderer/common/msgprocessor"
3333
"github.com/hyperledger/fabric/orderer/common/types"
3434
"github.com/hyperledger/fabric/orderer/consensus"
35+
"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
3536
"github.com/hyperledger/fabric/protoutil"
3637
"github.com/pkg/errors"
3738
)
@@ -537,7 +538,10 @@ func (r *Registrar) CreateChain(chainName string) {
537538
if chain != nil {
538539
logger.Infof("A chain of type %T for channel %s already exists. "+
539540
"Halting it.", chain.Chain, chainName)
541+
r.lock.Lock()
540542
chain.Halt()
543+
delete(r.chains, chainName)
544+
r.lock.Unlock()
541545
}
542546
r.newChain(configTx(lf))
543547
}
@@ -546,6 +550,20 @@ func (r *Registrar) newChain(configtx *cb.Envelope) {
546550
r.lock.Lock()
547551
defer r.lock.Unlock()
548552

553+
channelName, err := channelNameFromConfigTx(configtx)
554+
if err != nil {
555+
logger.Warnf("Failed extracting channel name: %v", err)
556+
return
557+
}
558+
559+
// fixes https://github.com/hyperledger/fabric/issues/2931
560+
if existingChain, exists := r.chains[channelName]; exists {
561+
if _, isRaftChain := existingChain.Chain.(*etcdraft.Chain); isRaftChain {
562+
logger.Infof("Channel %s already created, skipping its creation", channelName)
563+
return
564+
}
565+
}
566+
549567
cs := r.createNewChain(configtx)
550568
cs.start()
551569
logger.Infof("Created and started new channel %s", cs.ChannelID())
@@ -1113,3 +1131,21 @@ func (r *Registrar) ReportConsensusRelationAndStatusMetrics(channelID string, re
11131131
r.channelParticipationMetrics.reportConsensusRelation(channelID, relation)
11141132
r.channelParticipationMetrics.reportStatus(channelID, status)
11151133
}
1134+
1135+
func channelNameFromConfigTx(configtx *cb.Envelope) (string, error) {
1136+
payload, err := protoutil.UnmarshalPayload(configtx.Payload)
1137+
if err != nil {
1138+
return "", errors.WithMessage(err, "error umarshaling envelope to payload")
1139+
}
1140+
1141+
if payload.Header == nil {
1142+
return "", errors.New("missing channel header")
1143+
}
1144+
1145+
chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
1146+
if err != nil {
1147+
return "", errors.WithMessage(err, "error unmarshalling channel header")
1148+
}
1149+
1150+
return chdr.ChannelId, nil
1151+
}

orderer/common/multichannel/registrar_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/hyperledger/fabric/orderer/common/multichannel/mocks"
3939
"github.com/hyperledger/fabric/orderer/common/types"
4040
"github.com/hyperledger/fabric/orderer/consensus"
41+
"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
4142
"github.com/hyperledger/fabric/protoutil"
4243
"github.com/pkg/errors"
4344
"github.com/stretchr/testify/assert"
@@ -661,6 +662,35 @@ func TestCreateChain(t *testing.T) {
661662
close(chain2.Chain.(*mockChainCluster).queue)
662663
})
663664

665+
t.Run("chain of type etcdraft.Chain is already created", func(t *testing.T) {
666+
tmpdir, err := ioutil.TempDir("", "registrar_test-")
667+
require.NoError(t, err)
668+
defer os.RemoveAll(tmpdir)
669+
670+
lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys)
671+
672+
consenter := &mocks.Consenter{}
673+
consenter.HandleChainCalls(handleChain)
674+
consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter}
675+
676+
manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil)
677+
manager.Initialize(consenters)
678+
679+
testChainSupport := &ChainSupport{Chain: &etcdraft.Chain{}}
680+
manager.chains["test"] = testChainSupport
681+
682+
orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir())
683+
envConfigUpdate, err := encoder.MakeChannelCreationTransaction("test", mockCrypto(), orglessChannelConf)
684+
require.NoError(t, err, "Constructing chain creation tx")
685+
686+
manager.newChain(envConfigUpdate)
687+
688+
testChainSupport2 := manager.GetChain("test")
689+
require.NotNil(t, testChainSupport2)
690+
691+
assert.Same(t, testChainSupport, testChainSupport2)
692+
})
693+
664694
// This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain
665695
t.Run("New chain", func(t *testing.T) {
666696
expectedLastConfigSeq := uint64(1)

0 commit comments

Comments
 (0)