refactor(wakubridge): split message compatibility from wakubridge

This commit is contained in:
Lorenzo Delgado 2023-02-10 09:54:47 +01:00 committed by GitHub
parent 9c3e5f5bc5
commit a853bf52f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 161 additions and 112 deletions

View File

@ -0,0 +1,52 @@
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
import
stew/byteutils
import
# Waku v1 imports
../../waku/v1/protocol/waku_protocol,
# Waku v2 imports
libp2p/crypto/crypto,
../../waku/v2/utils/namespacing,
../../waku/v2/protocol/waku_message,
../../waku/v2/node/peer_manager
const
ContentTopicApplication = "waku"
ContentTopicAppVersion = "1"
proc toV1Topic*(contentTopic: ContentTopic): waku_protocol.Topic {.raises: [LPError, ValueError]} =
## Extracts the 4-byte array v1 topic from a content topic
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
hexToByteArray(hexStr = NamespacedTopic.fromString(contentTopic).tryGet().topicName, N = 4) # Byte array length
proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
## Convert a 4-byte array v1 topic to a namespaced content topic
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
##
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
var namespacedTopic = NamespacedTopic()
namespacedTopic.application = ContentTopicApplication
namespacedTopic.version = ContentTopicAppVersion
namespacedTopic.topicName = "0x" & v1Topic.toHex()
namespacedTopic.encoding = "rfc26"
return ContentTopic($namespacedTopic)
proc isBridgeable*(msg: WakuMessage): bool =
## Determines if a Waku v2 msg is on a bridgeable content topic
let ns = NamespacedTopic.fromString(msg.contentTopic)
if ns.isOk():
if ns.get().application == ContentTopicApplication and ns.get().version == ContentTopicAppVersion:
return true
return false

View File

@ -33,6 +33,7 @@ import
filter_api,
relay_api,
store_api],
./message_compat,
./config
declarePublicCounter waku_bridge_transfers, "Number of messages transferred between Waku v1 and v2 networks", ["type"]
@ -49,8 +50,6 @@ const
ClientIdV1 = "nim-waku v1 node"
DefaultTTL = 5'u32
DeduplQSize = 20 # Maximum number of seen messages to keep in deduplication queue
ContentTopicApplication = "waku"
ContentTopicAppVersion = "1"
MaintenancePeriod = 1.minutes
TargetV1Peers = 4 # Target number of v1 connections to maintain. Could be made configurable in future.
@ -73,18 +72,6 @@ type
# Helper funtions #
###################
# Validity
proc isBridgeable*(msg: WakuMessage): bool =
## Determines if a Waku v2 msg is on a bridgeable content topic
let ns = NamespacedTopic.fromString(msg.contentTopic)
if ns.isOk():
if ns.get().application == ContentTopicApplication and ns.get().version == ContentTopicAppVersion:
return true
return false
# Deduplication
proc containsOrAdd(sequence: var seq[hashes.Hash], hash: hashes.Hash): bool =
@ -99,30 +86,6 @@ proc containsOrAdd(sequence: var seq[hashes.Hash], hash: hashes.Hash): bool =
return false
# Topic conversion
proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
## Convert a 4-byte array v1 topic to a namespaced content topic
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
##
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
var namespacedTopic = NamespacedTopic()
namespacedTopic.application = ContentTopicApplication
namespacedTopic.version = ContentTopicAppVersion
namespacedTopic.topicName = "0x" & v1Topic.toHex()
namespacedTopic.encoding = "rfc26"
return ContentTopic($namespacedTopic)
proc toV1Topic*(contentTopic: ContentTopic): waku_protocol.Topic {.raises: [Defect, LPError, ValueError]} =
## Extracts the 4-byte array v1 topic from a content topic
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
hexToByteArray(hexStr = NamespacedTopic.fromString(contentTopic).tryGet().topicName,
N = 4) # Byte array length
# Message conversion
func toWakuMessage(env: waku_protocol.Envelope): WakuMessage =

View File

@ -27,6 +27,7 @@ when defined(waku_exp_store_resume):
# TODO: Review store resume test cases (#1282)
import ./v2/waku_store/test_resume
import
# Waku v2 tests
./v2/test_wakunode,
@ -49,7 +50,6 @@ import
./v2/test_rest_relay_api,
./v2/test_peer_manager,
./v2/test_web3, # TODO remove it when rln-relay tests get finalized
./v2/test_waku_bridge,
./v2/test_peer_storage,
./v2/test_waku_keepalive,
./v2/test_namespacing_utils,
@ -60,12 +60,21 @@ import
./v2/test_waku_noise,
./v2/test_waku_noise_sessions,
./v2/test_waku_switch,
# Waku Keystore
./v2/test_waku_keystore_keyfile,
./v2/test_waku_keystore,
# Utils
./v2/test_utils_compat
# Waku Keystore test suite
import
./v2/test_waku_keystore_keyfile,
./v2/test_waku_keystore
## Apps
# Wakubridge test suite
import ./all_tests_wakubridge
## Experimental
when defined(rln):

View File

@ -0,0 +1,6 @@
{.used.}
# Wakubridge test suite
import
./wakubridge/test_message_compat,
./wakubridge/test_wakubridge

View File

@ -1,47 +1,46 @@
{.used.}
import
testutils/unittests,
chronos,
stew/results,
testutils/unittests
import
../../waku/v2/utils/namespacing
procSuite "Namespacing utils":
suite "Namespacing utils":
asyncTest "Create from string":
test "Create from string":
# Expected case
let ns = NamespacedTopic.fromString("/waku/2/default-waku/proto").tryGet()
let nsRes = NamespacedTopic.fromString("/waku/2/default-waku/proto")
require nsRes.isOk()
let ns = nsRes.get()
check:
ns.application == "waku"
ns.version == "2"
ns.topicName == "default-waku"
ns.encoding == "proto"
# Invalid cases
expect CatchableError:
# Topic should be namespaced
discard NamespacedTopic.fromString("this-is-not-namespaced").tryGet()
expect CatchableError:
# Topic should start with '/'
discard NamespacedTopic.fromString("waku/2/default-waku/proto").tryGet()
expect CatchableError:
# Topic has too few parts
discard NamespacedTopic.fromString("/waku/2/default-waku").tryGet()
test "Invalid string - Topic is not namespaced":
check NamespacedTopic.fromString("this-is-not-namespaced").isErr()
expect CatchableError:
# Topic has too many parts
discard NamespacedTopic.fromString("/waku/2/default-waku/proto/2").tryGet()
test "Invalid string - Topic should start with slash":
check NamespacedTopic.fromString("waku/2/default-waku/proto").isErr()
asyncTest "Stringify namespaced topic":
test "Invalid string - Topic has too few parts":
check NamespacedTopic.fromString("/waku/2/default-waku").isErr()
test "Invalid string - Topic has too many parts":
check NamespacedTopic.fromString("/waku/2/default-waku/proto/2").isErr()
test "Stringify namespaced topic":
var ns = NamespacedTopic()
ns.application = "waku"
ns.version = "2"
ns.topicName = "default-waku"
ns.encoding = "proto"
check:
$ns == "/waku/2/default-waku/proto"

View File

@ -0,0 +1,57 @@
{.used.}
import
stew/shims/net as stewNet,
testutils/unittests,
chronos,
libp2p/switch
import
../../waku/v2/protocol/waku_message
import
../../apps/wakubridge/message_compat
suite "WakuBridge - Message compat":
test "Topics are correctly converted between Waku v1 and Waku v2":
# Expected cases
check:
toV1Topic(ContentTopic("/waku/1/0x00000000/rfc26")) == [byte 0x00, byte 0x00, byte 0x00, byte 0x00]
toV2ContentTopic([byte 0x00, byte 0x00, byte 0x00, byte 0x00]) == ContentTopic("/waku/1/0x00000000/rfc26")
toV1Topic(ContentTopic("/waku/1/0xffffffff/rfc26")) == [byte 0xff, byte 0xff, byte 0xff, byte 0xff]
toV2ContentTopic([byte 0xff, byte 0xff, byte 0xff, byte 0xff]) == ContentTopic("/waku/1/0xffffffff/rfc26")
toV1Topic(ContentTopic("/waku/1/0x1a2b3c4d/rfc26")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
toV2ContentTopic([byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]) == ContentTopic("/waku/1/0x1a2b3c4d/rfc26")
# Topic conversion should still work where '0x' prefix is omitted from <v1 topic byte array>
toV1Topic(ContentTopic("/waku/1/1a2b3c4d/rfc26")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
# Invalid cases
expect LPError:
# Content topic not namespaced
discard toV1Topic(ContentTopic("this-is-my-content"))
expect ValueError:
# Content topic name too short
discard toV1Topic(ContentTopic("/waku/1/0x112233/rfc26"))
expect ValueError:
# Content topic name not hex
discard toV1Topic(ContentTopic("/waku/1/my-content/rfc26"))
test "Verify that WakuMessages are on bridgeable content topics":
let
validCT = ContentTopic("/waku/1/my-content/rfc26")
unnamespacedCT = ContentTopic("just_a_bunch_of_words")
invalidAppCT = ContentTopic("/facebook/1/my-content/rfc26")
invalidVersionCT = ContentTopic("/waku/2/my-content/rfc26")
check:
WakuMessage(contentTopic: validCT).isBridgeable() == true
WakuMessage(contentTopic: unnamespacedCT).isBridgeable() == false
WakuMessage(contentTopic: invalidAppCT).isBridgeable() == false
WakuMessage(contentTopic: invalidVersionCT).isBridgeable() == false

View File

@ -21,9 +21,12 @@ import
../../waku/v2/node/waku_node,
../../waku/v2/utils/compat,
../../waku/v2/utils/peers,
../../apps/wakubridge/wakubridge,
../test_helpers
import
../../apps/wakubridge/wakubridge
procSuite "WakuBridge":
###############
# Suite setup #
@ -40,11 +43,11 @@ procSuite "WakuBridge":
nodev2Key = crypto.PrivateKey.random(Secp256k1, cryptoRng[])[]
bridge = WakuBridge.new(
nodev1Key= nodev1Key,
nodev1Address = localAddress(30302),
nodev1Address = localAddress(62200),
powRequirement = 0.002,
rng = rng,
nodev2Key = nodev2Key,
nodev2BindIp = ValidIpAddress.init("0.0.0.0"), nodev2BindPort= Port(62200),
nodev2BindIp = ValidIpAddress.init("0.0.0.0"), nodev2BindPort= Port(62201),
nodev2PubsubTopic = DefaultBridgeTopic)
# Waku v1 node
@ -52,7 +55,7 @@ procSuite "WakuBridge":
# Waku v2 node
v2NodeKey = crypto.PrivateKey.random(Secp256k1, cryptoRng[])[]
v2Node = WakuNode.new(v2NodeKey, ValidIpAddress.init("0.0.0.0"), Port(62202))
v2Node = WakuNode.new(v2NodeKey, ValidIpAddress.init("0.0.0.0"), Port(62203))
contentTopic = ContentTopic("/waku/1/0x1a2b3c4d/rfc26")
topic = [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
@ -75,46 +78,6 @@ procSuite "WakuBridge":
# Suite tests #
###############
asyncTest "Topics are correctly converted between Waku v1 and Waku v2":
# Expected cases
check:
toV1Topic(ContentTopic("/waku/1/0x00000000/rfc26")) == [byte 0x00, byte 0x00, byte 0x00, byte 0x00]
toV2ContentTopic([byte 0x00, byte 0x00, byte 0x00, byte 0x00]) == ContentTopic("/waku/1/0x00000000/rfc26")
toV1Topic(ContentTopic("/waku/1/0xffffffff/rfc26")) == [byte 0xff, byte 0xff, byte 0xff, byte 0xff]
toV2ContentTopic([byte 0xff, byte 0xff, byte 0xff, byte 0xff]) == ContentTopic("/waku/1/0xffffffff/rfc26")
toV1Topic(ContentTopic("/waku/1/0x1a2b3c4d/rfc26")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
toV2ContentTopic([byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]) == ContentTopic("/waku/1/0x1a2b3c4d/rfc26")
# Topic conversion should still work where '0x' prefix is omitted from <v1 topic byte array>
toV1Topic(ContentTopic("/waku/1/1a2b3c4d/rfc26")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
# Invalid cases
expect LPError:
# Content topic not namespaced
discard toV1Topic(ContentTopic("this-is-my-content"))
expect ValueError:
# Content topic name too short
discard toV1Topic(ContentTopic("/waku/1/0x112233/rfc26"))
expect ValueError:
# Content topic name not hex
discard toV1Topic(ContentTopic("/waku/1/my-content/rfc26"))
asyncTest "Verify that WakuMessages are on bridgeable content topics":
let
validCT = ContentTopic("/waku/1/my-content/rfc26")
unnamespacedCT = ContentTopic("just_a_bunch_of_words")
invalidAppCT = ContentTopic("/facebook/1/my-content/rfc26")
invalidVersionCT = ContentTopic("/waku/2/my-content/rfc26")
check:
WakuMessage(contentTopic: validCT).isBridgeable() == true
WakuMessage(contentTopic: unnamespacedCT).isBridgeable() == false
WakuMessage(contentTopic: invalidAppCT).isBridgeable() == false
WakuMessage(contentTopic: invalidVersionCT).isBridgeable() == false
asyncTest "Messages are bridged between Waku v1 and Waku v2":
# Setup test
@ -197,11 +160,11 @@ procSuite "WakuBridge":
# Bridge
v1Bridge = WakuBridge.new(
nodev1Key= nodev1Key,
nodev1Address = localAddress(30303),
nodev1Address = localAddress(62210),
powRequirement = 0.002,
rng = rng,
nodev2Key = nodev2Key,
nodev2BindIp = ValidIpAddress.init("0.0.0.0"), nodev2BindPort= Port(62207),
nodev2BindIp = ValidIpAddress.init("0.0.0.0"), nodev2BindPort= Port(62211),
nodev2PubsubTopic = DefaultBridgeTopic,
v1Pool = v1NodePool.mapIt(newNode(it.toEnode())),
targetV1Peers = targetV1Peers)