Skip to content

Commit 61e0907

Browse files
[FAB-3970] Add ledger height to pull
We filter blocks that their seq num is lower than ledger height from incomming digests. Change-Id: I2b49de5c806e3c01fcfdc7f55eb02beba76f4342 Signed-off-by: Gennady Laventman <gennady@il.ibm.com>
1 parent 140edf7 commit 61e0907

File tree

9 files changed

+193
-51
lines changed

9 files changed

+193
-51
lines changed

gossip/state/metastate.go renamed to gossip/common/metastate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
44
SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
package state
7+
package common
88

99
import (
1010
"bytes"

gossip/state/metastate_test.go renamed to gossip/common/metastate_test.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,14 @@ Copyright IBM Corp. All Rights Reserved.
44
SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
package state
7+
package common
88

99
import (
1010
"testing"
1111

12-
"github.com/hyperledger/fabric/gossip/util"
1312
"github.com/stretchr/testify/assert"
1413
)
1514

16-
func init() {
17-
util.SetupTestLogging()
18-
}
19-
2015
func TestNewNodeMetastate(t *testing.T) {
2116
metastate := NewNodeMetastate(0)
2217
assert.Equal(t, metastate.Height(), uint64(0))

gossip/gossip/channel/channel.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package channel
99
import (
1010
"bytes"
1111
"fmt"
12+
"strconv"
1213
"sync"
1314
"sync/atomic"
1415
"time"
@@ -127,6 +128,7 @@ type gossipChannel struct {
127128
stateInfoPublishScheduler *time.Ticker
128129
stateInfoRequestScheduler *time.Ticker
129130
memFilter *membershipFilter
131+
ledgerHeight uint64
130132
}
131133

132134
type membershipFilter struct {
@@ -335,6 +337,27 @@ func (gc *gossipChannel) createBlockPuller() pull.Mediator {
335337
gc.DeMultiplex(msg)
336338
},
337339
}
340+
341+
adapter.IngressDigFilter = func(digestMsg *proto.DataDigest) *proto.DataDigest {
342+
gc.RLock()
343+
height := gc.ledgerHeight
344+
gc.RUnlock()
345+
digests := digestMsg.Digests
346+
digestMsg.Digests = nil
347+
for i := range digests {
348+
seqNum, err := strconv.ParseUint(digests[i], 10, 64)
349+
if err != nil {
350+
gc.logger.Warning("Can't parse digest", digests[i], "err", err)
351+
continue
352+
}
353+
if seqNum >= height {
354+
digestMsg.Digests = append(digestMsg.Digests, digests[i])
355+
}
356+
357+
}
358+
return digestMsg
359+
}
360+
338361
return pull.NewPullMediator(conf, adapter)
339362
}
340363

@@ -478,6 +501,7 @@ func (gc *gossipChannel) HandleMessage(msg proto.ReceivedMessage) {
478501
}
479502
return
480503
}
504+
481505
if m.IsPullMsg() && m.GetPullMsgType() == proto.PullMsgType_BLOCK_MSG {
482506
// If we don't have a StateInfo message from the peer,
483507
// no way of validating its eligibility in the channel.
@@ -701,6 +725,13 @@ func (gc *gossipChannel) UpdateStateInfo(msg *proto.SignedGossipMessage) {
701725
gc.stateInfoMsgStore.Add(msg)
702726
gc.Lock()
703727
defer gc.Unlock()
728+
729+
nodeMeta, err := common.FromBytes(msg.GetStateInfo().Metadata)
730+
if err != nil {
731+
gc.logger.Warning("Can't extract ledger height from metadata", err)
732+
return
733+
}
734+
gc.ledgerHeight = nodeMeta.LedgerHeight
704735
gc.stateInfoMsg = msg
705736
atomic.StoreInt32(&gc.shouldGossipStateInfo, int32(1))
706737
}

gossip/gossip/channel/channel_test.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package channel
99
import (
1010
"errors"
1111
"fmt"
12-
"strconv"
1312
"sync"
1413
"sync/atomic"
1514
"testing"
@@ -400,10 +399,9 @@ func TestChannelPeriodicalPublishStateInfo(t *testing.T) {
400399
msg = m
401400
}
402401

403-
md := msg.GetStateInfo().Metadata
404-
height, err := strconv.ParseInt(string(md), 10, 64)
402+
nodeMeta, err := common.FromBytes(msg.GetStateInfo().Metadata)
405403
assert.NoError(t, err, "ReceivedMetadata is invalid")
406-
assert.Equal(t, ledgerHeight, int(height), "Received different ledger height than expected")
404+
assert.Equal(t, ledgerHeight, int(nodeMeta.LedgerHeight), "Received different ledger height than expected")
407405
}
408406

409407
func TestChannelMsgStoreEviction(t *testing.T) {
@@ -1097,7 +1095,9 @@ func TestChannelStateInfoSnapshot(t *testing.T) {
10971095
stateInfoMsg := &receivedMsg{PKIID: pkiIDInOrg1, msg: stateInfoSnapshotForChannel(channelA, createStateInfoMsg(4, pkiIDInOrg1, channelA))}
10981096
gc.HandleMessage(stateInfoMsg)
10991097
assert.NotEmpty(t, gc.GetPeers())
1100-
assert.Equal(t, "4", string(gc.GetPeers()[0].Metadata))
1098+
nodeMeta, err := common.FromBytes(gc.GetPeers()[0].Metadata)
1099+
assert.NoError(t, err)
1100+
assert.Equal(t, 4, int(nodeMeta.LedgerHeight))
11011101

11021102
// Check we don't respond to stateInfoSnapshot requests with wrong MAC
11031103
sMsg, _ := (&proto.GossipMessage{
@@ -1149,7 +1149,9 @@ func TestChannelStateInfoSnapshot(t *testing.T) {
11491149
assert.Len(t, elements, 1)
11501150
sMsg, err := elements[0].ToGossipMessage()
11511151
assert.NoError(t, err)
1152-
assert.Equal(t, []byte("4"), sMsg.GetStateInfo().Metadata)
1152+
nodeMeta, err := common.FromBytes(sMsg.GetStateInfo().Metadata)
1153+
assert.NoError(t, err)
1154+
assert.Equal(t, 4, int(nodeMeta.LedgerHeight))
11531155
}
11541156

11551157
// Ensure we don't crash if we got an invalid state info message
@@ -1615,6 +1617,44 @@ func TestOnDemandGossip(t *testing.T) {
16151617
}
16161618
}
16171619

1620+
func TestChannelPullWithDigestsFilter(t *testing.T) {
1621+
t.Parallel()
1622+
cs := &cryptoService{}
1623+
cs.On("VerifyBlock", mock.Anything).Return(nil)
1624+
receivedBlocksChan := make(chan *proto.SignedGossipMessage, 2)
1625+
adapter := new(gossipAdapterMock)
1626+
configureAdapter(adapter, discovery.NetworkMember{PKIid: pkiIDInOrg1})
1627+
adapter.On("Gossip", mock.Anything)
1628+
adapter.On("DeMultiplex", mock.Anything).Run(func(arg mock.Arguments) {
1629+
msg := arg.Get(0).(*proto.SignedGossipMessage)
1630+
if !msg.IsDataMsg() {
1631+
return
1632+
}
1633+
// The peer is supposed to de-multiplex 1 ledger block
1634+
assert.True(t, msg.IsDataMsg())
1635+
receivedBlocksChan <- msg
1636+
})
1637+
gc := NewGossipChannel(pkiIDInOrg1, orgInChannelA, cs, channelA, adapter, &joinChanMsg{})
1638+
go gc.HandleMessage(&receivedMsg{PKIID: pkiIDInOrg1, msg: createStateInfoMsg(100, pkiIDInOrg1, channelA)})
1639+
1640+
gc.UpdateStateInfo(createStateInfoMsg(11, pkiIDInOrg1, channelA))
1641+
1642+
var wg sync.WaitGroup
1643+
wg.Add(1)
1644+
1645+
pullPhase := simulatePullPhaseWithVariableDigest(gc, t, &wg, func(envelope *proto.Envelope) {}, []string{"10", "11"}, []string{"11"}, 11)
1646+
adapter.On("Send", mock.Anything, mock.Anything).Run(pullPhase)
1647+
wg.Wait()
1648+
1649+
select {
1650+
case <-time.After(time.Second * 5):
1651+
t.Fatal("Haven't received blocks on time")
1652+
case msg := <-receivedBlocksChan:
1653+
assert.Equal(t, uint64(11), msg.GetDataMsg().Payload.SeqNum)
1654+
}
1655+
1656+
}
1657+
16181658
func createDataUpdateMsg(nonce uint64, seqs ...uint64) *proto.SignedGossipMessage {
16191659
msg := &proto.GossipMessage{
16201660
Nonce: 0,
@@ -1669,13 +1709,15 @@ func dataMsgOfChannel(seqnum uint64, channel common.ChainID) *proto.SignedGossip
16691709
}
16701710

16711711
func createStateInfoMsg(ledgerHeight int, pkiID common.PKIidType, channel common.ChainID) *proto.SignedGossipMessage {
1712+
nodeMeta := common.NewNodeMetastate(uint64(ledgerHeight))
1713+
metaBytes, _ := nodeMeta.Bytes()
16721714
sMsg, _ := (&proto.GossipMessage{
16731715
Tag: proto.GossipMessage_CHAN_OR_ORG,
16741716
Content: &proto.GossipMessage_StateInfo{
16751717
StateInfo: &proto.StateInfo{
16761718
Channel_MAC: GenerateMAC(pkiID, channel),
16771719
Timestamp: &proto.PeerTime{IncNum: uint64(time.Now().UnixNano()), SeqNum: 1},
1678-
Metadata: []byte(fmt.Sprintf("%d", ledgerHeight)),
1720+
Metadata: metaBytes,
16791721
PkiId: []byte(pkiID),
16801722
},
16811723
},
@@ -1719,6 +1761,10 @@ func createDataMsg(seqnum uint64, channel common.ChainID) *proto.SignedGossipMes
17191761
}
17201762

17211763
func simulatePullPhase(gc GossipChannel, t *testing.T, wg *sync.WaitGroup, mutator msgMutator, seqs ...uint64) func(args mock.Arguments) {
1764+
return simulatePullPhaseWithVariableDigest(gc, t, wg, mutator, []string{"10", "11"}, []string{"10", "11"}, seqs...)
1765+
}
1766+
1767+
func simulatePullPhaseWithVariableDigest(gc GossipChannel, t *testing.T, wg *sync.WaitGroup, mutator msgMutator, proposedDigestSeqs []string, resultDigestSeqs []string, seqs ...uint64) func(args mock.Arguments) {
17221768
var l sync.Mutex
17231769
var sentHello bool
17241770
var sentReq bool
@@ -1735,7 +1781,7 @@ func simulatePullPhase(gc GossipChannel, t *testing.T, wg *sync.WaitGroup, mutat
17351781
Content: &proto.GossipMessage_DataDig{
17361782
DataDig: &proto.DataDigest{
17371783
MsgType: proto.PullMsgType_BLOCK_MSG,
1738-
Digests: []string{"10", "11"},
1784+
Digests: proposedDigestSeqs,
17391785
Nonce: msg.GetHello().Nonce,
17401786
},
17411787
},
@@ -1749,10 +1795,10 @@ func simulatePullPhase(gc GossipChannel, t *testing.T, wg *sync.WaitGroup, mutat
17491795
if msg.IsDataReq() && !sentReq {
17501796
sentReq = true
17511797
dataReq := msg.GetDataReq()
1752-
for _, expectedDigest := range []string{"10", "11"} {
1798+
for _, expectedDigest := range resultDigestSeqs {
17531799
assert.Contains(t, dataReq.Digests, expectedDigest)
17541800
}
1755-
assert.Equal(t, 2, len(dataReq.Digests))
1801+
assert.Equal(t, len(resultDigestSeqs), len(dataReq.Digests))
17561802
// When we send a data request, simulate a response of a data update
17571803
// from the imaginary peer that got the request
17581804
dataUpdateMsg := new(receivedMsg)

gossip/gossip/gossip_impl.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -985,11 +985,11 @@ func (g *gossipServiceImpl) createCertStorePuller() pull.Mediator {
985985
g.logger.Info("Learned of a new certificate:", idMsg.Cert)
986986
}
987987
adapter := &pull.PullAdapter{
988-
Sndr: g.comm,
989-
MemSvc: g.disc,
990-
IdExtractor: pkiIDFromMsg,
991-
MsgCons: certConsumer,
992-
DigFilter: g.sameOrgOrOurOrgPullFilter,
988+
Sndr: g.comm,
989+
MemSvc: g.disc,
990+
IdExtractor: pkiIDFromMsg,
991+
MsgCons: certConsumer,
992+
EgressDigFilter: g.sameOrgOrOurOrgPullFilter,
993993
}
994994
return pull.NewPullMediator(conf, adapter)
995995
}

gossip/gossip/pull/pullstore.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ type Config struct {
5555
MsgType proto.PullMsgType
5656
}
5757

58-
// DigestFilter filters digests to be sent to a remote peer, that
58+
// IngressDigestFilter filters out entities in digests that are received from remote peers
59+
type IngressDigestFilter func(digestMsg *proto.DataDigest) *proto.DataDigest
60+
61+
// EgressDigestFilter filters digests to be sent to a remote peer, that
5962
// sent a hello with the following message
60-
type DigestFilter func(helloMsg proto.ReceivedMessage) func(digestItem string) bool
63+
type EgressDigestFilter func(helloMsg proto.ReceivedMessage) func(digestItem string) bool
6164

62-
// byContext converts this DigestFilter to an algo.DigestFilter
63-
func (df DigestFilter) byContext() algo.DigestFilter {
65+
// byContext converts this EgressDigFilter to an algo.DigestFilter
66+
func (df EgressDigestFilter) byContext() algo.DigestFilter {
6467
return func(context interface{}) func(digestItem string) bool {
6568
return func(digestItem string) bool {
6669
return df(context.(proto.ReceivedMessage))(digestItem)
@@ -71,11 +74,12 @@ func (df DigestFilter) byContext() algo.DigestFilter {
7174
// PullAdapter defines methods of the pullStore to interact
7275
// with various modules of gossip
7376
type PullAdapter struct {
74-
Sndr Sender
75-
MemSvc MembershipService
76-
IdExtractor proto.IdentifierExtractor
77-
MsgCons proto.MsgConsumer
78-
DigFilter DigestFilter
77+
Sndr Sender
78+
MemSvc MembershipService
79+
IdExtractor proto.IdentifierExtractor
80+
MsgCons proto.MsgConsumer
81+
EgressDigFilter EgressDigestFilter
82+
IngressDigFilter IngressDigestFilter
7983
}
8084

8185
// Mediator is a component wrap a PullEngine and provides the methods
@@ -115,16 +119,16 @@ type pullMediatorImpl struct {
115119

116120
// NewPullMediator returns a new Mediator
117121
func NewPullMediator(config Config, adapter *PullAdapter) Mediator {
118-
digFilter := adapter.DigFilter
122+
egressDigFilter := adapter.EgressDigFilter
119123

120124
acceptAllFilter := func(_ proto.ReceivedMessage) func(string) bool {
121125
return func(_ string) bool {
122126
return true
123127
}
124128
}
125129

126-
if digFilter == nil {
127-
digFilter = acceptAllFilter
130+
if egressDigFilter == nil {
131+
egressDigFilter = acceptAllFilter
128132
}
129133

130134
p := &pullMediatorImpl{
@@ -135,8 +139,16 @@ func NewPullMediator(config Config, adapter *PullAdapter) Mediator {
135139
itemID2Msg: make(map[string]*proto.SignedGossipMessage),
136140
}
137141

138-
p.engine = algo.NewPullEngineWithFilter(p, config.PullInterval, digFilter.byContext())
142+
p.engine = algo.NewPullEngineWithFilter(p, config.PullInterval, egressDigFilter.byContext())
143+
144+
if adapter.IngressDigFilter == nil {
145+
// Create accept all filter
146+
adapter.IngressDigFilter = func(digestMsg *proto.DataDigest) *proto.DataDigest {
147+
return digestMsg
148+
}
149+
}
139150
return p
151+
140152
}
141153

142154
func (p *pullMediatorImpl) HandleMessage(m proto.ReceivedMessage) {
@@ -160,9 +172,10 @@ func (p *pullMediatorImpl) HandleMessage(m proto.ReceivedMessage) {
160172
p.engine.OnHello(helloMsg.Nonce, m)
161173
}
162174
if digest := msg.GetDataDig(); digest != nil {
163-
itemIDs = digest.Digests
175+
d := p.PullAdapter.IngressDigFilter(digest)
176+
itemIDs = d.Digests
164177
pullMsgType = DigestMsgType
165-
p.engine.OnDigest(digest.Digests, digest.Nonce, m)
178+
p.engine.OnDigest(d.Digests, d.Nonce, m)
166179
}
167180
if req := msg.GetDataReq(); req != nil {
168181
itemIDs = req.Digests

0 commit comments

Comments
 (0)