Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions tests/pubsub/integration/testgossipsubsignatureflags.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Nim-LibP2P
# Copyright (c) 2023-2025 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.

{.used.}

import unittest2
import chronos
import stew/byteutils
import ../utils
import ../../../libp2p/protocols/pubsub/[gossipsub, pubsub]
import ../../../libp2p/protocols/pubsub/rpc/[messages]
import ../../helpers
import ../../utils/futures

suite "GossipSub Integration - Signature Flags":
const
topic = "foobar"
testData = "test message".toBytes()

teardown:
checkTrackers()

asyncTest "Default - messages are signed when sign=true and contain fromPeer and seqno when anonymize=false":
let nodes = generateNodes(
2, gossip = true, sign = true, verifySignature = true, anonymize = false
)

startNodesAndDeferStop(nodes)
await connectNodesStar(nodes)

nodes.subscribeAllNodes(topic, voidTopicHandler)

var (receivedMessages, checkForMessage) = createCheckForMessages()
nodes[1].addOnRecvObserver(checkForMessage)

tryPublish await nodes[0].publish(topic, testData), 1

checkUntilTimeout:
receivedMessages[].len > 0

let receivedMessage = receivedMessages[][0]
check:
receivedMessage.data == testData
receivedMessage.fromPeer.data.len > 0
receivedMessage.seqno.len > 0
receivedMessage.signature.len > 0
receivedMessage.key.len > 0

asyncTest "Sign flag - messages are not signed when sign=false":
let nodes = generateNodes(
2, gossip = true, sign = false, verifySignature = false, anonymize = false
)

startNodesAndDeferStop(nodes)
await connectNodesStar(nodes)

nodes.subscribeAllNodes(topic, voidTopicHandler)

var (receivedMessages, checkForMessage) = createCheckForMessages()
nodes[1].addOnRecvObserver(checkForMessage)

tryPublish await nodes[0].publish(topic, testData), 1

checkUntilTimeout:
receivedMessages[].len > 0

let receivedMessage = receivedMessages[][0]
check:
receivedMessage.data == testData
receivedMessage.signature.len == 0
receivedMessage.key.len == 0

asyncTest "Anonymize flag - messages are anonymous when anonymize=true":
let nodes = generateNodes(
2, gossip = true, sign = true, verifySignature = true, anonymize = true
) # anonymize = true takes precedence
startNodesAndDeferStop(nodes)
await connectNodesStar(nodes)

nodes.subscribeAllNodes(topic, voidTopicHandler)

var (receivedMessages, checkForMessage) = createCheckForMessages()
nodes[1].addOnRecvObserver(checkForMessage)

let testData = "anonymous message".toBytes()
tryPublish await nodes[0].publish(topic, testData), 1

checkUntilTimeout:
receivedMessages[].len > 0

let receivedMessage = receivedMessages[][0]
check:
receivedMessage.data == testData
receivedMessage.fromPeer.data.len == 0
receivedMessage.seqno.len == 0
receivedMessage.signature.len == 0
receivedMessage.key.len == 0

type NodeConfig = object
sign: bool
verify: bool
anonymize: bool

type Scenario = object
senderConfig: NodeConfig
receiverConfig: NodeConfig
shouldWork: bool

let scenarios: seq[Scenario] =
@[
# valid combos
# S default, R default
Scenario(
senderConfig: NodeConfig(sign: true, verify: true, anonymize: false),
receiverConfig: NodeConfig(sign: true, verify: true, anonymize: false),
shouldWork: true,
),
# S default, R anonymous
Scenario(
senderConfig: NodeConfig(sign: true, verify: true, anonymize: false),
receiverConfig: NodeConfig(sign: false, verify: false, anonymize: true),
shouldWork: true,
),
# S anonymous, R anonymous
Scenario(
senderConfig: NodeConfig(sign: false, verify: false, anonymize: true),
receiverConfig: NodeConfig(sign: false, verify: false, anonymize: true),
shouldWork: true,
),
# S only sign, R only verify
Scenario(
senderConfig: NodeConfig(sign: true, verify: false, anonymize: false),
receiverConfig: NodeConfig(sign: false, verify: true, anonymize: false),
shouldWork: true,
),
# S only verify, R only sign
Scenario(
senderConfig: NodeConfig(sign: true, verify: true, anonymize: true),
receiverConfig: NodeConfig(sign: false, verify: false, anonymize: false),
shouldWork: true,
),
# S anonymous (not signed despite the flag), R minimal
Scenario(
senderConfig: NodeConfig(sign: false, verify: true, anonymize: true),
receiverConfig: NodeConfig(sign: true, verify: false, anonymize: false),
shouldWork: true,
),
# S unsigned, R unsigned
Scenario(
senderConfig: NodeConfig(sign: false, verify: false, anonymize: false),
receiverConfig: NodeConfig(sign: false, verify: false, anonymize: false),
shouldWork: true,
),

# invalid combos
# S anonymous, R default
Scenario(
senderConfig: NodeConfig(sign: false, verify: false, anonymize: true),
receiverConfig: NodeConfig(sign: true, verify: true, anonymize: false),
shouldWork: false,
),
# S unsigned, R anonymous but verify
Scenario(
senderConfig: NodeConfig(sign: false, verify: false, anonymize: false),
receiverConfig: NodeConfig(sign: true, verify: true, anonymize: true),
shouldWork: false,
),
# S unsigned, R default
Scenario(
senderConfig: NodeConfig(sign: false, verify: false, anonymize: false),
receiverConfig: NodeConfig(sign: true, verify: true, anonymize: false),
shouldWork: false,
),
]

for scenario in scenarios:
let title = "Compatibility matrix: " & $scenario
asyncTest title:
let
sender = generateNodes(
1,
gossip = true,
sign = scenario.senderConfig.sign,
verifySignature = scenario.senderConfig.verify,
anonymize = scenario.senderConfig.anonymize,
)[0]
receiver = generateNodes(
1,
gossip = true,
sign = scenario.receiverConfig.sign,
verifySignature = scenario.receiverConfig.verify,
anonymize = scenario.receiverConfig.anonymize,
)[0]
nodes = @[sender, receiver]

startNodesAndDeferStop(nodes)
await connectNodesStar(nodes)

let (messageReceivedFut, handler) = createCompleteHandler()

nodes.subscribeAllNodes(topic, handler)
await waitForHeartbeat()

discard await sender.publish(topic, testData)

let messageReceived = await waitForState(messageReceivedFut, HEARTBEAT_TIMEOUT)
check:
if scenario.shouldWork:
messageReceived.isCompleted(true)
else:
messageReceived.isCancelled()
16 changes: 7 additions & 9 deletions tests/pubsub/integration/testgossipsubskipmcache.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import chronos
import stew/byteutils
import ../utils
import ../../../libp2p/protocols/pubsub/[gossipsub, peertable, pubsubpeer]
import ../../../libp2p/protocols/pubsub/[gossipsub, peertable]
import ../../../libp2p/protocols/pubsub/rpc/[messages]
import ../../helpers

Expand All @@ -31,15 +31,14 @@ suite "GossipSub Integration - Skip MCache Support":
nodes[1].subscribe(topic, voidTopicHandler)
await waitSub(nodes[0], nodes[1], topic)

let
publishData = "hello".toBytes()
msgId = nodes[0].getMsgId(topic, publishData)
let publishData = "hello".toBytes()

tryPublish await nodes[0].publish(
topic, publishData, publishParams = some(PublishParams(skipMCache: true))
), 1

check msgId notin nodes[0].mcache.msgs
check:
nodes[0].mcache.msgs.len == 0

asyncTest "publish without skipMCache adds message to mcache":
let
Expand All @@ -52,12 +51,11 @@ suite "GossipSub Integration - Skip MCache Support":
nodes[1].subscribe(topic, voidTopicHandler)
await waitSub(nodes[0], nodes[1], topic)

let
publishData = "hello".toBytes()
msgId = nodes[0].getMsgId(topic, publishData)
let publishData = "hello".toBytes()

tryPublish await nodes[0].publish(
topic, publishData, publishParams = none(PublishParams)
), 1

check msgId in nodes[0].mcache.msgs
check:
nodes[0].mcache.msgs.len == 1
3 changes: 2 additions & 1 deletion tests/pubsub/integration/testpubsubintegration.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import
testfloodsub, testgossipsubcontrolmessages, testgossipsubcustomconn,
testgossipsubfanout, testgossipsubgossip, testgossipsubgossipcompatibility,
testgossipsubheartbeat, testgossipsubmeshmanagement, testgossipsubmessagecache,
testgossipsubmessagehandling, testgossipsubscoring
testgossipsubmessagehandling, testgossipsubscoring, testgossipsubsignatureflags,
testgossipsubskipmcache
12 changes: 3 additions & 9 deletions tests/pubsub/utils.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# compile time options here
const
libp2p_pubsub_sign {.booldefine.} = true
libp2p_pubsub_verify {.booldefine.} = true
libp2p_pubsub_anonymize {.booldefine.} = false
Comment on lines -2 to -5
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if we had opened issue to remove these consts or we just discussed for removing them? anyway if there is issue let's close it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about it and I created this issue: #1434, which is linked.


import hashes, random, tables, sets, sequtils
import chronos, results, stew/byteutils, chronos/ratelimit
import
Expand Down Expand Up @@ -173,9 +167,9 @@ proc generateNodes*(
msgIdProvider: MsgIdProvider = defaultMsgIdProvider,
gossip: bool = false,
triggerSelf: bool = false,
verifySignature: bool = libp2p_pubsub_verify,
anonymize: bool = libp2p_pubsub_anonymize,
sign: bool = libp2p_pubsub_sign,
sign: bool = true,
verifySignature: bool = true,
anonymize: bool = false,
sendSignedPeerRecord = false,
unsubscribeBackoff = 1.seconds,
pruneBackoff = 1.minutes,
Expand Down
Loading