Skip to content

Commit 056a980

Browse files
cceonetechnical
authored andcommitted
network: don't send VP in MOI messages if unsupported by remote peer (algorand#6484)
1 parent 4b4fd9f commit 056a980

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

network/wsNetwork.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,17 @@ func (wn *WebsocketNetwork) maybeSendMessagesOfInterest(peer *wsPeer, messagesOf
11911191
messagesOfInterestEnc = wn.messagesOfInterestEnc
11921192
wn.messagesOfInterestMu.Unlock()
11931193
}
1194+
11941195
if messagesOfInterestEnc != nil {
1196+
// Filter VP tag for peers lacking stateful compression support
1197+
// older nodes (<= v4.3) treat unknown tags as protocol violations and disconnect.
1198+
if !peer.vpackStatefulCompressionSupported() {
1199+
tags, err := unmarshallMessageOfInterest(messagesOfInterestEnc)
1200+
if err == nil && tags[protocol.VotePackedTag] {
1201+
delete(tags, protocol.VotePackedTag)
1202+
messagesOfInterestEnc = marshallMessageOfInterestMap(tags)
1203+
}
1204+
}
11951205
peer.sendMessagesOfInterest(messagesOfInterestGeneration, messagesOfInterestEnc)
11961206
} else {
11971207
wn.log.Infof("msgOfInterest Enc=nil, MOIGen=%d", messagesOfInterestGeneration)

network/wsNetwork_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"encoding/json"
2525
"fmt"
2626
"io"
27+
"maps"
2728
"math/rand"
2829
"net"
2930
"net/http"
@@ -4773,3 +4774,125 @@ func TestPeerComparisonInBroadcast(t *testing.T) {
47734774
require.Equal(t, 1, len(testPeer.sendBufferBulk))
47744775
require.Equal(t, 0, len(exceptPeer.sendBufferBulk))
47754776
}
4777+
4778+
func TestMaybeSendMessagesOfInterestLegacyPeer(t *testing.T) {
4779+
partitiontest.PartitionTest(t)
4780+
t.Parallel()
4781+
4782+
makePeer := func(wn *WebsocketNetwork, features peerFeatureFlag) (*wsPeer, chan sendMessage) {
4783+
ch := make(chan sendMessage, 1)
4784+
return &wsPeer{
4785+
wsPeerCore: makePeerCore(wn.ctx, wn, wn.log, nil, "test-addr", nil, ""),
4786+
features: features,
4787+
sendBufferHighPrio: ch,
4788+
sendBufferBulk: make(chan sendMessage, 1),
4789+
closing: make(chan struct{}),
4790+
processed: make(chan struct{}, 1),
4791+
}, ch
4792+
}
4793+
4794+
newTestNetwork := func(tags map[protocol.Tag]bool) *WebsocketNetwork {
4795+
wn := &WebsocketNetwork{
4796+
log: logging.TestingLog(t),
4797+
}
4798+
wn.ctx = context.Background()
4799+
cloned := maps.Clone(tags)
4800+
wn.messagesOfInterest = cloned
4801+
wn.messagesOfInterestEnc = marshallMessageOfInterestMap(cloned)
4802+
wn.messagesOfInterestGeneration.Store(1)
4803+
return wn
4804+
}
4805+
4806+
t.Run("filters VP for peers without stateful support", func(t *testing.T) {
4807+
wn := newTestNetwork(map[protocol.Tag]bool{
4808+
protocol.AgreementVoteTag: true,
4809+
protocol.VotePackedTag: true,
4810+
})
4811+
4812+
peer, ch := makePeer(wn, pfCompressedProposal|pfCompressedVoteVpack)
4813+
wn.maybeSendMessagesOfInterest(peer, nil)
4814+
4815+
select {
4816+
case msg := <-ch:
4817+
require.Equal(t, protocol.MsgOfInterestTag, protocol.Tag(msg.data[:2]))
4818+
4819+
decoded, err := unmarshallMessageOfInterest(msg.data[2:])
4820+
require.NoError(t, err)
4821+
4822+
require.Contains(t, decoded, protocol.AgreementVoteTag)
4823+
require.True(t, decoded[protocol.AgreementVoteTag])
4824+
_, hasVP := decoded[protocol.VotePackedTag]
4825+
require.False(t, hasVP, "VP tag should be filtered for legacy peers")
4826+
default:
4827+
t.Fatal("expected MOI message for legacy peer")
4828+
}
4829+
})
4830+
4831+
t.Run("retains VP for peers with stateful support", func(t *testing.T) {
4832+
wn := newTestNetwork(map[protocol.Tag]bool{
4833+
protocol.AgreementVoteTag: true,
4834+
protocol.VotePackedTag: true,
4835+
})
4836+
4837+
peer, ch := makePeer(wn, pfCompressedProposal|pfCompressedVoteVpack|pfCompressedVoteVpackStateful256)
4838+
4839+
wn.maybeSendMessagesOfInterest(peer, nil)
4840+
4841+
select {
4842+
case msg := <-ch:
4843+
require.Equal(t, protocol.MsgOfInterestTag, protocol.Tag(msg.data[:2]))
4844+
4845+
decoded, err := unmarshallMessageOfInterest(msg.data[2:])
4846+
require.NoError(t, err)
4847+
4848+
require.Contains(t, decoded, protocol.AgreementVoteTag)
4849+
require.True(t, decoded[protocol.AgreementVoteTag])
4850+
require.Contains(t, decoded, protocol.VotePackedTag)
4851+
require.True(t, decoded[protocol.VotePackedTag], "expected VP tag for peer with stateful support")
4852+
default:
4853+
t.Fatal("expected MOI message for stateful peer")
4854+
}
4855+
})
4856+
4857+
t.Run("gracefully handles configuration without VP tag", func(t *testing.T) {
4858+
wn := newTestNetwork(map[protocol.Tag]bool{
4859+
protocol.AgreementVoteTag: true,
4860+
})
4861+
4862+
peer, ch := makePeer(wn, pfCompressedProposal|pfCompressedVoteVpack)
4863+
wn.maybeSendMessagesOfInterest(peer, nil)
4864+
4865+
select {
4866+
case msg := <-ch:
4867+
require.Equal(t, protocol.MsgOfInterestTag, protocol.Tag(msg.data[:2]))
4868+
4869+
decoded, err := unmarshallMessageOfInterest(msg.data[2:])
4870+
require.NoError(t, err)
4871+
4872+
require.Contains(t, decoded, protocol.AgreementVoteTag)
4873+
require.True(t, decoded[protocol.AgreementVoteTag])
4874+
_, hasVP := decoded[protocol.VotePackedTag]
4875+
require.False(t, hasVP)
4876+
default:
4877+
t.Fatal("expected MOI message when VP is absent from configuration")
4878+
}
4879+
})
4880+
4881+
t.Run("skips sending when peer generation matches", func(t *testing.T) {
4882+
wn := newTestNetwork(map[protocol.Tag]bool{
4883+
protocol.AgreementVoteTag: true,
4884+
protocol.VotePackedTag: true,
4885+
})
4886+
4887+
peer, ch := makePeer(wn, pfCompressedProposal|pfCompressedVoteVpack)
4888+
peer.messagesOfInterestGeneration.Store(wn.messagesOfInterestGeneration.Load())
4889+
4890+
wn.maybeSendMessagesOfInterest(peer, nil)
4891+
4892+
select {
4893+
case <-ch:
4894+
t.Fatal("did not expect MOI message when generations already match")
4895+
default:
4896+
}
4897+
})
4898+
}

0 commit comments

Comments
 (0)