Skip to content

Commit

Permalink
[FAB-3613] JoinChan gossip broken if no anchorPeers
Browse files Browse the repository at this point in the history
Since https://gerrit.hyperledger.org/r/#/c/7105/ is merged,
when a channel is created- no anchor peers are supplied inside.

There is a bug in gossip_service.go that if a config with an empty
set of anchor peers for each org is given, the channel's membership
(list of orgs) is considered empty, and then each peer considers
itself alone in the channel.

It "recovers" when the update config that does contain the anchor
peers comes, but until then - it doesn't consider even peers of
its own org as members of the channel because of the bug.

I fixed it and added a test that simulates the flow that caused
the bug.

Change-Id: Ibc71747f2fd56add1361e996c1af89c8c3db479b
Signed-off-by: Yacov Manevich <yacovm@il.ibm.com>
  • Loading branch information
yacovm committed May 3, 2017
1 parent 052b81b commit 0991d53
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
3 changes: 2 additions & 1 deletion gossip/gossip/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ func (gc *gossipChannel) HandleMessage(msg proto.ReceivedMessage) {
return
}
if !gc.IsOrgInChannel(orgID) {
gc.logger.Warning("Point to point message came from", msg.GetConnectionInfo().ID, "but it's not eligible for the channel", string(gc.chainID))
gc.logger.Warning("Point to point message came from", msg.GetConnectionInfo().ID,
", org(", string(orgID), ") but it's not eligible for the channel", string(gc.chainID))
return
}

Expand Down
1 change: 1 addition & 0 deletions gossip/service/gossip_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func (g *gossipServiceImpl) configUpdated(config Config) {
jcm := &joinChannelMessage{seqNum: config.Sequence(), members2AnchorPeers: map[string][]api.AnchorPeer{}}
for _, appOrg := range config.Organizations() {
logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
for _, ap := range appOrg.AnchorPeers() {
anchorPeer := api.AnchorPeer{
Host: ap.Host,
Expand Down
66 changes: 55 additions & 11 deletions gossip/service/join_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package service

import (
"sync"
"testing"
"time"

Expand Down Expand Up @@ -80,7 +81,7 @@ func (*gossipMock) Accept(acceptor common.MessageAcceptor, passThrough bool) (<-
}

func (g *gossipMock) JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID) {
g.Called()
g.Called(joinMsg, chainID)
}

func (*gossipMock) Stop() {
Expand All @@ -99,21 +100,20 @@ func (ao *appOrgMock) MSPID() string {
return ao.id
}

func (*appOrgMock) AnchorPeers() []*peer.AnchorPeer {
return []*peer.AnchorPeer{{Host: "1.2.3.4", Port: 5611}}
func (ao *appOrgMock) AnchorPeers() []*peer.AnchorPeer {
return []*peer.AnchorPeer{}
}

type configMock struct {
orgs2AppOrgs map[string]config.ApplicationOrg
}

func (*configMock) ChainID() string {
return "A"
}

func (*configMock) Organizations() map[string]config.ApplicationOrg {
return map[string]config.ApplicationOrg{
"Org0": &appOrgMock{"Org0"},
}
func (c *configMock) Organizations() map[string]config.ApplicationOrg {
return c.orgs2AppOrgs
}

func (*configMock) Sequence() uint64 {
Expand All @@ -127,11 +127,15 @@ func TestJoinChannelConfig(t *testing.T) {

failChan := make(chan struct{}, 1)
g1SvcMock := &gossipMock{}
g1SvcMock.On("JoinChan", mock.Anything).Run(func(_ mock.Arguments) {
g1SvcMock.On("JoinChan", mock.Anything, mock.Anything).Run(func(_ mock.Arguments) {
failChan <- struct{}{}
})
g1 := &gossipServiceImpl{secAdv: &secAdvMock{}, peerIdentity: api.PeerIdentityType("OrgMSP0"), gossipSvc: g1SvcMock}
g1.configUpdated(&configMock{})
g1.configUpdated(&configMock{
orgs2AppOrgs: map[string]config.ApplicationOrg{
"Org0": &appOrgMock{id: "Org0"},
},
})
select {
case <-time.After(time.Second):
case <-failChan:
Expand All @@ -140,15 +144,55 @@ func TestJoinChannelConfig(t *testing.T) {

succChan := make(chan struct{}, 1)
g2SvcMock := &gossipMock{}
g2SvcMock.On("JoinChan", mock.Anything).Run(func(_ mock.Arguments) {
g2SvcMock.On("JoinChan", mock.Anything, mock.Anything).Run(func(_ mock.Arguments) {
succChan <- struct{}{}
})
g2 := &gossipServiceImpl{secAdv: &secAdvMock{}, peerIdentity: api.PeerIdentityType("Org0"), gossipSvc: g2SvcMock}
g2.configUpdated(&configMock{})
g2.configUpdated(&configMock{
orgs2AppOrgs: map[string]config.ApplicationOrg{
"Org0": &appOrgMock{id: "Org0"},
},
})
select {
case <-time.After(time.Second):
assert.Fail(t, "Didn't join a channel (should have done so within the time period)")
case <-succChan:

}
}

func TestJoinChannelNoAnchorPeers(t *testing.T) {
// Scenario: The channel we're joining has 2 orgs but no anchor peers
// The test ensures that JoinChan is called with a JoinChannelMessage with Members
// that consist of the organizations of the configuration given.

var joinChanCalled sync.WaitGroup
joinChanCalled.Add(1)
gMock := &gossipMock{}
gMock.On("JoinChan", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
defer joinChanCalled.Done()
jcm := args.Get(0).(api.JoinChannelMessage)
channel := args.Get(1).(common.ChainID)
assert.Len(t, jcm.Members(), 2)
assert.Contains(t, jcm.Members(), api.OrgIdentityType("Org0"))
assert.Contains(t, jcm.Members(), api.OrgIdentityType("Org1"))
assert.Equal(t, "A", string(channel))
})

g := &gossipServiceImpl{secAdv: &secAdvMock{}, peerIdentity: api.PeerIdentityType("Org0"), gossipSvc: gMock}

appOrg0 := &appOrgMock{id: "Org0"}
appOrg1 := &appOrgMock{id: "Org1"}

// Make sure the ApplicationOrgs really have no anchor peers
assert.Empty(t, appOrg0.AnchorPeers())
assert.Empty(t, appOrg1.AnchorPeers())

g.configUpdated(&configMock{
orgs2AppOrgs: map[string]config.ApplicationOrg{
"Org0": appOrg0,
"Org1": appOrg1,
},
})
joinChanCalled.Wait()
}

0 comments on commit 0991d53

Please sign in to comment.