2022-07-01 18:19:57 +00:00
|
|
|
# Nim-Libp2p
|
|
|
|
# Copyright (c) 2022 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-05-08 20:58:23 +00:00
|
|
|
{.used.}
|
|
|
|
|
2021-05-21 16:27:01 +00:00
|
|
|
import sequtils, options, tables, sets
|
2020-06-03 02:21:11 +00:00
|
|
|
import chronos, stew/byteutils
|
2019-12-08 21:06:58 +00:00
|
|
|
import utils,
|
2020-04-21 01:24:42 +00:00
|
|
|
../../libp2p/[errors,
|
|
|
|
switch,
|
2020-06-19 17:29:43 +00:00
|
|
|
stream/connection,
|
2019-12-17 05:24:03 +00:00
|
|
|
crypto/crypto,
|
|
|
|
protocols/pubsub/pubsub,
|
2020-04-21 01:24:42 +00:00
|
|
|
protocols/pubsub/floodsub,
|
2020-07-15 19:18:55 +00:00
|
|
|
protocols/pubsub/rpc/messages,
|
|
|
|
protocols/pubsub/peertable]
|
2022-02-21 15:04:17 +00:00
|
|
|
import ../../libp2p/protocols/pubsub/errors as pubsub_errors
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-05-08 20:10:06 +00:00
|
|
|
import ../helpers
|
2020-04-21 01:24:42 +00:00
|
|
|
|
|
|
|
proc waitSub(sender, receiver: auto; key: string) {.async, gcsafe.} =
|
|
|
|
# turn things deterministic
|
|
|
|
# this is for testing purposes only
|
|
|
|
var ceil = 15
|
2020-08-12 00:05:49 +00:00
|
|
|
let fsub = cast[FloodSub](sender)
|
2020-04-21 01:24:42 +00:00
|
|
|
while not fsub.floodsub.hasKey(key) or
|
2021-12-16 10:05:20 +00:00
|
|
|
not fsub.floodsub.hasPeerId(key, receiver.peerInfo.peerId):
|
2020-04-21 01:24:42 +00:00
|
|
|
await sleepAsync(100.millis)
|
|
|
|
dec ceil
|
|
|
|
doAssert(ceil > 0, "waitSub timeout!")
|
|
|
|
|
2019-12-06 02:16:18 +00:00
|
|
|
suite "FloodSub":
|
2020-04-21 01:24:42 +00:00
|
|
|
teardown:
|
2020-09-21 17:48:19 +00:00
|
|
|
checkTrackers()
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
asyncTest "FloodSub basic publish/subscribe A -> B":
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
completionFut.complete(true)
|
|
|
|
|
|
|
|
let
|
|
|
|
nodes = generateNodes(2)
|
|
|
|
|
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
nodes[0].switch.start(),
|
|
|
|
nodes[1].switch.start(),
|
|
|
|
)
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[1].subscribe("foobar", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[0], nodes[1], "foobar")
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
check (await nodes[0].publish("foobar", "Hello!".toBytes())) > 0
|
|
|
|
check (await completionFut.wait(5.seconds)) == true
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes[0].switch.stop(),
|
|
|
|
nodes[1].switch.stop()
|
|
|
|
)
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(nodesFut.concat())
|
2020-05-23 17:14:22 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
asyncTest "FloodSub basic publish/subscribe B -> A":
|
|
|
|
var completionFut = newFuture[bool]()
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
completionFut.complete(true)
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(2)
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
nodes[0].switch.start(),
|
|
|
|
nodes[1].switch.start(),
|
|
|
|
)
|
2020-08-12 00:05:49 +00:00
|
|
|
|
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[0].subscribe("foobar", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[1], nodes[0], "foobar")
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
check (await nodes[1].publish("foobar", "Hello!".toBytes())) > 0
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
check (await completionFut.wait(5.seconds)) == true
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes[0].switch.stop(),
|
|
|
|
nodes[1].switch.stop()
|
|
|
|
)
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(nodesFut)
|
2020-07-27 19:33:51 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
asyncTest "FloodSub validation should succeed":
|
|
|
|
var handlerFut = newFuture[bool]()
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
handlerFut.complete(true)
|
2019-12-06 02:16:18 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(2)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
nodes[0].switch.start(),
|
|
|
|
nodes[1].switch.start(),
|
|
|
|
)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[1].subscribe("foobar", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[0], nodes[1], "foobar")
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
var validatorFut = newFuture[bool]()
|
|
|
|
proc validator(topic: string,
|
|
|
|
message: Message): Future[ValidationResult] {.async.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
validatorFut.complete(true)
|
|
|
|
result = ValidationResult.Accept
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
nodes[1].addValidator("foobar", validator)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
check (await nodes[0].publish("foobar", "Hello!".toBytes())) > 0
|
|
|
|
check (await handlerFut) == true
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes[0].switch.stop(),
|
|
|
|
nodes[1].switch.stop()
|
|
|
|
)
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(nodesFut)
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
asyncTest "FloodSub validation should fail":
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check false # if we get here, it should fail
|
2020-07-27 19:33:51 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(2)
|
2020-02-12 14:43:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
nodes[0].switch.start(),
|
|
|
|
nodes[1].switch.start(),
|
|
|
|
)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[1].subscribe("foobar", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[0], nodes[1], "foobar")
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
var validatorFut = newFuture[bool]()
|
|
|
|
proc validator(topic: string,
|
|
|
|
message: Message): Future[ValidationResult] {.async.} =
|
|
|
|
validatorFut.complete(true)
|
|
|
|
result = ValidationResult.Reject
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
nodes[1].addValidator("foobar", validator)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
discard await nodes[0].publish("foobar", "Hello!".toBytes())
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes[0].switch.stop(),
|
|
|
|
nodes[1].switch.stop()
|
|
|
|
)
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(nodesFut)
|
2020-07-27 19:33:51 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
asyncTest "FloodSub validation one fails and one succeeds":
|
|
|
|
var handlerFut = newFuture[bool]()
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foo"
|
|
|
|
handlerFut.complete(true)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(2)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
nodes[0].switch.start(),
|
|
|
|
nodes[1].switch.start(),
|
|
|
|
)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[1].subscribe("foo", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[0], nodes[1], "foo")
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[1].subscribe("bar", handler)
|
2020-11-13 03:44:02 +00:00
|
|
|
await waitSub(nodes[0], nodes[1], "bar")
|
|
|
|
|
|
|
|
proc validator(topic: string,
|
|
|
|
message: Message): Future[ValidationResult] {.async.} =
|
|
|
|
if topic == "foo":
|
|
|
|
result = ValidationResult.Accept
|
|
|
|
else:
|
|
|
|
result = ValidationResult.Reject
|
2020-08-12 00:05:49 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
nodes[1].addValidator("foo", "bar", validator)
|
|
|
|
|
|
|
|
check (await nodes[0].publish("foo", "Hello!".toBytes())) > 0
|
|
|
|
check (await nodes[0].publish("bar", "Hello!".toBytes())) > 0
|
|
|
|
|
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes[0].switch.stop(),
|
|
|
|
nodes[1].switch.stop()
|
|
|
|
)
|
|
|
|
|
|
|
|
await allFuturesThrowing(nodesFut)
|
|
|
|
|
|
|
|
asyncTest "FloodSub multiple peers, no self trigger":
|
|
|
|
var runs = 10
|
|
|
|
|
|
|
|
var futs = newSeq[(Future[void], TopicHandler, ref int)](runs)
|
|
|
|
for i in 0..<runs:
|
|
|
|
closureScope:
|
|
|
|
var
|
|
|
|
fut = newFuture[void]()
|
|
|
|
counter = new int
|
|
|
|
futs[i] = (
|
|
|
|
fut,
|
|
|
|
(proc(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
inc counter[]
|
|
|
|
if counter[] == runs - 1:
|
|
|
|
fut.complete()),
|
|
|
|
counter
|
2020-08-12 00:05:49 +00:00
|
|
|
)
|
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(runs, triggerSelf = false)
|
|
|
|
nodesFut = nodes.mapIt(it.switch.start())
|
|
|
|
|
|
|
|
await subscribeNodes(nodes)
|
|
|
|
|
|
|
|
for i in 0..<runs:
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[i].subscribe("foobar", futs[i][1])
|
2020-11-13 03:44:02 +00:00
|
|
|
|
|
|
|
var subs: seq[Future[void]]
|
|
|
|
for i in 0..<runs:
|
|
|
|
for y in 0..<runs:
|
|
|
|
if y != i:
|
|
|
|
subs &= waitSub(nodes[i], nodes[y], "foobar")
|
|
|
|
await allFuturesThrowing(subs)
|
|
|
|
|
|
|
|
var pubs: seq[Future[int]]
|
|
|
|
for i in 0..<runs:
|
|
|
|
pubs &= nodes[i].publish("foobar", ("Hello!" & $i).toBytes())
|
|
|
|
await allFuturesThrowing(pubs)
|
|
|
|
|
|
|
|
await allFuturesThrowing(futs.mapIt(it[0]))
|
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes.mapIt(
|
|
|
|
allFutures(
|
|
|
|
it.switch.stop())))
|
|
|
|
|
|
|
|
await allFuturesThrowing(nodesFut)
|
|
|
|
|
|
|
|
asyncTest "FloodSub multiple peers, with self trigger":
|
|
|
|
var runs = 10
|
|
|
|
|
|
|
|
var futs = newSeq[(Future[void], TopicHandler, ref int)](runs)
|
|
|
|
for i in 0..<runs:
|
|
|
|
closureScope:
|
|
|
|
var
|
|
|
|
fut = newFuture[void]()
|
|
|
|
counter = new int
|
|
|
|
futs[i] = (
|
|
|
|
fut,
|
|
|
|
(proc(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check topic == "foobar"
|
|
|
|
inc counter[]
|
|
|
|
if counter[] == runs - 1:
|
|
|
|
fut.complete()),
|
|
|
|
counter
|
|
|
|
)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
let
|
|
|
|
nodes = generateNodes(runs, triggerSelf = true)
|
|
|
|
nodesFut = nodes.mapIt(it.switch.start())
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await subscribeNodes(nodes)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
for i in 0..<runs:
|
2020-12-19 14:43:32 +00:00
|
|
|
nodes[i].subscribe("foobar", futs[i][1])
|
2020-04-21 01:24:42 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
var subs: seq[Future[void]]
|
|
|
|
for i in 0..<runs:
|
|
|
|
for y in 0..<runs:
|
|
|
|
if y != i:
|
|
|
|
subs &= waitSub(nodes[i], nodes[y], "foobar")
|
|
|
|
await allFuturesThrowing(subs)
|
2019-12-17 05:24:03 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
var pubs: seq[Future[int]]
|
|
|
|
for i in 0..<runs:
|
|
|
|
pubs &= nodes[i].publish("foobar", ("Hello!" & $i).toBytes())
|
|
|
|
await allFuturesThrowing(pubs)
|
2020-08-03 05:20:11 +00:00
|
|
|
|
2021-03-15 19:16:03 +00:00
|
|
|
# wait the test task
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(futs.mapIt(it[0]))
|
2021-03-15 19:16:03 +00:00
|
|
|
|
|
|
|
# test calling unsubscribeAll for coverage
|
|
|
|
for node in nodes:
|
|
|
|
node.unsubscribeAll("foobar")
|
|
|
|
check:
|
|
|
|
# we keep the peers in table
|
|
|
|
FloodSub(node).floodsub["foobar"].len == 9
|
|
|
|
# remove the topic tho
|
|
|
|
node.topics.len == 0
|
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(
|
|
|
|
nodes.mapIt(
|
|
|
|
allFutures(
|
|
|
|
it.switch.stop())))
|
2020-08-03 05:20:11 +00:00
|
|
|
|
2020-11-13 03:44:02 +00:00
|
|
|
await allFuturesThrowing(nodesFut)
|
2021-10-25 10:58:38 +00:00
|
|
|
|
|
|
|
asyncTest "FloodSub message size validation":
|
|
|
|
var messageReceived = 0
|
|
|
|
proc handler(topic: string, data: seq[byte]) {.async, gcsafe.} =
|
|
|
|
check data.len < 50
|
|
|
|
inc(messageReceived)
|
|
|
|
|
|
|
|
let
|
|
|
|
bigNode = generateNodes(1)
|
|
|
|
smallNode = generateNodes(1, maxMessageSize = 200)
|
|
|
|
|
|
|
|
# start switches
|
|
|
|
nodesFut = await allFinished(
|
|
|
|
bigNode[0].switch.start(),
|
|
|
|
smallNode[0].switch.start(),
|
|
|
|
)
|
|
|
|
|
|
|
|
await subscribeNodes(bigNode & smallNode)
|
|
|
|
bigNode[0].subscribe("foo", handler)
|
|
|
|
smallNode[0].subscribe("foo", handler)
|
|
|
|
await waitSub(bigNode[0], smallNode[0], "foo")
|
|
|
|
|
|
|
|
let
|
|
|
|
bigMessage = newSeq[byte](1000)
|
|
|
|
smallMessage1 = @[1.byte]
|
|
|
|
smallMessage2 = @[3.byte]
|
|
|
|
|
|
|
|
# Need two different messages, otherwise they are the same when anonymized
|
|
|
|
check (await smallNode[0].publish("foo", smallMessage1)) > 0
|
|
|
|
check (await bigNode[0].publish("foo", smallMessage2)) > 0
|
|
|
|
|
|
|
|
check (await checkExpiring(messageReceived == 2)) == true
|
|
|
|
|
|
|
|
check (await smallNode[0].publish("foo", bigMessage)) > 0
|
|
|
|
check (await bigNode[0].publish("foo", bigMessage)) > 0
|
|
|
|
|
|
|
|
await allFuturesThrowing(
|
|
|
|
smallNode[0].switch.stop(),
|
|
|
|
bigNode[0].switch.stop()
|
|
|
|
)
|
|
|
|
|
|
|
|
await allFuturesThrowing(nodesFut)
|