From 317fa6df185bc047abf8a8308309c726ae045dc9 Mon Sep 17 00:00:00 2001 From: Lorenzo Delgado Date: Fri, 10 Feb 2023 09:54:47 +0100 Subject: [PATCH] refactor(wakubridge): split message compatibility from wakubridge --- apps/wakubridge/message_compat.nim | 52 +++++++++++++++++ apps/wakubridge/wakubridge.nim | 39 +------------ tests/all_tests_v2.nim | 19 +++++-- tests/all_tests_wakubridge.nim | 6 ++ tests/v2/test_namespacing_utils.nim | 45 +++++++-------- tests/wakubridge/test_message_compat.nim | 57 +++++++++++++++++++ .../test_wakubridge.nim} | 55 +++--------------- 7 files changed, 161 insertions(+), 112 deletions(-) create mode 100644 apps/wakubridge/message_compat.nim create mode 100644 tests/all_tests_wakubridge.nim create mode 100644 tests/wakubridge/test_message_compat.nim rename tests/{v2/test_waku_bridge.nim => wakubridge/test_wakubridge.nim} (72%) diff --git a/apps/wakubridge/message_compat.nim b/apps/wakubridge/message_compat.nim new file mode 100644 index 000000000..7a7e5e153 --- /dev/null +++ b/apps/wakubridge/message_compat.nim @@ -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//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//rfc26` + ## + ## 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 diff --git a/apps/wakubridge/wakubridge.nim b/apps/wakubridge/wakubridge.nim index 82b287916..7ffa20015 100644 --- a/apps/wakubridge/wakubridge.nim +++ b/apps/wakubridge/wakubridge.nim @@ -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//rfc26` - ## - ## 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//rfc26` - - hexToByteArray(hexStr = NamespacedTopic.fromString(contentTopic).tryGet().topicName, - N = 4) # Byte array length - # Message conversion func toWakuMessage(env: waku_protocol.Envelope): WakuMessage = diff --git a/tests/all_tests_v2.nim b/tests/all_tests_v2.nim index f034258e1..e86cc9adf 100644 --- a/tests/all_tests_v2.nim +++ b/tests/all_tests_v2.nim @@ -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): diff --git a/tests/all_tests_wakubridge.nim b/tests/all_tests_wakubridge.nim new file mode 100644 index 000000000..6a4693178 --- /dev/null +++ b/tests/all_tests_wakubridge.nim @@ -0,0 +1,6 @@ +{.used.} + +# Wakubridge test suite +import + ./wakubridge/test_message_compat, + ./wakubridge/test_wakubridge diff --git a/tests/v2/test_namespacing_utils.nim b/tests/v2/test_namespacing_utils.nim index a4e851e5f..b5602a73a 100644 --- a/tests/v2/test_namespacing_utils.nim +++ b/tests/v2/test_namespacing_utils.nim @@ -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" diff --git a/tests/wakubridge/test_message_compat.nim b/tests/wakubridge/test_message_compat.nim new file mode 100644 index 000000000..ee06a9f7d --- /dev/null +++ b/tests/wakubridge/test_message_compat.nim @@ -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 + 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 + diff --git a/tests/v2/test_waku_bridge.nim b/tests/wakubridge/test_wakubridge.nim similarity index 72% rename from tests/v2/test_waku_bridge.nim rename to tests/wakubridge/test_wakubridge.nim index 133c87769..127911bf1 100644 --- a/tests/v2/test_waku_bridge.nim +++ b/tests/wakubridge/test_wakubridge.nim @@ -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 - 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)