@@ -125,6 +125,7 @@ class GossipV1_1Tests : GossipTestsBase() {
125
125
super .initChannelWithHandler(streamHandler, handler)
126
126
}
127
127
}
128
+
128
129
val test = TwoRoutersTest (mockRouterFactory = { exec, _, _ -> MalformedMockRouter (exec) })
129
130
val mockRouter = test.router2.router as MalformedMockRouter
130
131
@@ -1128,34 +1129,207 @@ class GossipV1_1Tests : GossipTestsBase() {
1128
1129
// 2 heartbeats - the topic should be GRAFTed
1129
1130
test.fuzz.timeController.addTime(2 .seconds)
1130
1131
1131
- fun createPruneMessage (peersCount : Int ): Rpc .RPC {
1132
- val peerInfos = List (peersCount) {
1133
- Rpc .PeerInfo .newBuilder()
1134
- .setPeerID(PeerId .random().bytes.toProtobuf())
1135
- .setSignedPeerRecord(ByteString .EMPTY )
1136
- .build()
1137
- }
1138
- return Rpc .RPC .newBuilder().setControl(
1139
- Rpc .ControlMessage .newBuilder().addPrune(
1140
- Rpc .ControlPrune .newBuilder()
1141
- .setTopicID(topic)
1142
- .addAllPeers(peerInfos)
1143
- )
1144
- ).build()
1145
- }
1146
-
1147
1132
test.mockRouter.sendToSingle(
1148
- createPruneMessage(test.gossipRouter.params.maxPeersAcceptedInPruneMsg + 1 )
1133
+ createPruneMessage(topic, test.gossipRouter.params.maxPeersAcceptedInPruneMsg + 1 )
1149
1134
)
1150
1135
1151
1136
// prune message should be dropped because too many peers
1152
1137
assertEquals(1 , test.gossipRouter.mesh[topic]!! .size)
1153
1138
1154
1139
test.mockRouter.sendToSingle(
1155
- createPruneMessage(test.gossipRouter.params.maxPeersAcceptedInPruneMsg)
1140
+ createPruneMessage(topic, test.gossipRouter.params.maxPeersAcceptedInPruneMsg)
1156
1141
)
1157
1142
1158
1143
// prune message should now be processed
1159
1144
assertEquals(0 , test.gossipRouter.mesh[topic]!! .size)
1160
1145
}
1146
+
1147
+ @Test
1148
+ fun `when a peer leaves the mesh it should still be considered for publishing` () {
1149
+ val test = TwoRoutersTest ()
1150
+ val topic = " topic1"
1151
+
1152
+ test.mockRouter.subscribe(topic)
1153
+ test.gossipRouter.subscribe(topic)
1154
+
1155
+ // 2 heartbeats - the topic should be GRAFTed
1156
+ test.fuzz.timeController.addTime(2 .seconds)
1157
+
1158
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == 1 )
1159
+
1160
+ // remote peer leaves the mesh
1161
+ test.mockRouter.sendToSingle(createPruneMessage(topic))
1162
+ test.fuzz.timeController.addTime(1 .seconds)
1163
+
1164
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == 0 )
1165
+
1166
+ val message1 = newMessage(topic, 0L , " Hello-0" .toByteArray())
1167
+ test.gossipRouter.publish(message1)
1168
+
1169
+ test.mockRouter.waitForMessage { it.publishCount > 0 }
1170
+ }
1171
+
1172
+ @Test
1173
+ fun `should publish to all mesh peers when mesh exceeds D` () {
1174
+ val gossipParams = GossipParams (D = 6 , DHigh = 10 )
1175
+ val test = ManyRoutersTest (params = gossipParams, mockRouterCount = gossipParams.DHigh )
1176
+ val topic = " topic1"
1177
+ test.connectAll()
1178
+
1179
+ test.mockRouters.forEach {
1180
+ it.subscribe(topic)
1181
+ }
1182
+ test.gossipRouter.subscribe(topic)
1183
+
1184
+ // 2 heartbeats - the topic should be GRAFTed
1185
+ test.fuzz.timeController.addTime(2 .seconds)
1186
+
1187
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == gossipParams.D )
1188
+
1189
+ test.mockRouters.forEach {
1190
+ it.sendToSingle(createGraftMessage(topic))
1191
+ }
1192
+
1193
+ test.fuzz.timeController.addTime(2 .seconds)
1194
+
1195
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == gossipParams.DHigh )
1196
+
1197
+ // remote peer leaves the mesh
1198
+ val message1 = newMessage(topic, 0L , " Hello-0" .toByteArray())
1199
+ test.gossipRouter.publish(message1)
1200
+
1201
+ val routerReceivedMessageCount =
1202
+ test.mockRouters.count { mockRouter ->
1203
+ mockRouter.inboundMessages.any { msg ->
1204
+ msg.publishCount > 0
1205
+ }
1206
+ }
1207
+
1208
+ assertTrue(routerReceivedMessageCount == gossipParams.DHigh )
1209
+ }
1210
+
1211
+ @Test
1212
+ fun `publishing should collect at least D peers if mesh is smaller` () {
1213
+ val params = GossipParams ()
1214
+
1215
+ val test = ManyRoutersTest (params = params, mockRouterCount = params.D )
1216
+ val topic = " topic1"
1217
+ test.connectAll()
1218
+
1219
+ test.mockRouters.forEach { it.subscribe(topic) }
1220
+ test.gossipRouter.subscribe(topic)
1221
+
1222
+ // 2 heartbeats - the topic should be GRAFTed
1223
+ test.fuzz.timeController.addTime(2 .seconds)
1224
+
1225
+ val topicMeshRouters = test.gossipRouter.mesh[topic]!!
1226
+ assertTrue((topicMeshRouters.size) >= params.DLow )
1227
+
1228
+ // leave just 2 peers in the mesh
1229
+ topicMeshRouters.drop(2 )
1230
+ .forEach {
1231
+ test.getMockRouter(it.peerId).sendToSingle(createPruneMessage(topic))
1232
+ }
1233
+ test.fuzz.timeController.addTime(1 .seconds)
1234
+
1235
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == 2 )
1236
+
1237
+ val message1 = newMessage(topic, 0L , " Hello-0" .toByteArray())
1238
+ test.gossipRouter.publish(message1)
1239
+
1240
+ val routerReceivedMessageCount =
1241
+ test.mockRouters.count { mockRouter ->
1242
+ mockRouter.inboundMessages.any { msg ->
1243
+ msg.publishCount > 0
1244
+ }
1245
+ }
1246
+
1247
+ assertTrue(routerReceivedMessageCount >= params.D )
1248
+ }
1249
+
1250
+ @Test
1251
+ fun `publishing should collect at least D peers if mesh is smaller and prefer well scored peers` () {
1252
+ val params = GossipParams ()
1253
+ val peerAppScores = mutableMapOf<PeerId , Int >()
1254
+ val gossipScoreParams = GossipScoreParams (
1255
+ peerScoreParams = GossipPeerScoreParams (
1256
+ appSpecificScore = {
1257
+ peerAppScores[it]?.toDouble() ? : 0.0
1258
+ },
1259
+ appSpecificWeight = 1.0
1260
+ )
1261
+ )
1262
+
1263
+ val test = ManyRoutersTest (params = params, scoreParams = gossipScoreParams, mockRouterCount = 10 )
1264
+ val topic = " topic1"
1265
+ test.connectAll()
1266
+
1267
+ test.mockRouters.forEach { it.subscribe(topic) }
1268
+ test.gossipRouter.subscribe(topic)
1269
+
1270
+ // 2 heartbeats - the topic should be GRAFTed
1271
+ test.fuzz.timeController.addTime(2 .seconds)
1272
+
1273
+ val topicMeshRouters = test.gossipRouter.mesh[topic]!! .toList()
1274
+ assertTrue((topicMeshRouters.size) == params.D )
1275
+
1276
+ // leave just 2 peers in the mesh
1277
+ topicMeshRouters.drop(2 )
1278
+ .forEach {
1279
+ test.getMockRouter(it.peerId).sendToSingle(createPruneMessage(topic))
1280
+ }
1281
+ // downscore all peers except 5
1282
+ val goodScoredPeers = topicMeshRouters.take(5 ).map { it.peerId }.toSet()
1283
+ test.routers
1284
+ .map { it.peerId }
1285
+ .filter { it !in goodScoredPeers }
1286
+ .forEach { peerAppScores[it] = - gossipScoreParams.publishThreshold.toInt() - 1 }
1287
+
1288
+ // for D = 6: 2 peers in the mesh + 3 peers outside of mesh + others are significantly downscored
1289
+ test.fuzz.timeController.addTime(1 .seconds)
1290
+
1291
+ assertTrue((test.gossipRouter.mesh[topic]?.size ? : 0 ) == 2 )
1292
+
1293
+ val message1 = newMessage(topic, 0L , " Hello-0" .toByteArray())
1294
+ test.gossipRouter.publish(message1)
1295
+
1296
+ // router should take 2 mesh peers, 3 well scored peers and 1 peer scored below publishThreshold
1297
+ val peersReceivedMessage = test.routers
1298
+ .filter {
1299
+ val mockRouter = it.router as MockRouter
1300
+ mockRouter.inboundMessages.any { msg ->
1301
+ msg.publishCount > 0
1302
+ }
1303
+ }
1304
+ .map { it.peerId }
1305
+
1306
+ assertTrue(peersReceivedMessage.size == params.D )
1307
+ assertTrue(peersReceivedMessage.containsAll(goodScoredPeers))
1308
+ }
1309
+
1310
+ private fun createGraftMessage (topic : String ): Rpc .RPC {
1311
+ return Rpc .RPC .newBuilder().setControl(
1312
+ Rpc .ControlMessage .newBuilder().addGraft(
1313
+ Rpc .ControlGraft .newBuilder()
1314
+ .setTopicID(topic)
1315
+ )
1316
+ ).build()
1317
+ }
1318
+
1319
+ private fun createPruneMessage (topic : String , pxPeersCount : Int = 0): Rpc .RPC {
1320
+ val peerInfos = List (pxPeersCount) {
1321
+ Rpc .PeerInfo .newBuilder()
1322
+ .setPeerID(PeerId .random().bytes.toProtobuf())
1323
+ .setSignedPeerRecord(ByteString .EMPTY )
1324
+ .build()
1325
+ }
1326
+ return Rpc .RPC .newBuilder().setControl(
1327
+ Rpc .ControlMessage .newBuilder().addPrune(
1328
+ Rpc .ControlPrune .newBuilder()
1329
+ .setTopicID(topic)
1330
+ .setBackoff(10 )
1331
+ .addAllPeers(peerInfos)
1332
+ )
1333
+ ).build()
1334
+ }
1161
1335
}
0 commit comments