mirror of
https://github.com/waku-org/nwaku.git
synced 2025-01-26 14:51:49 +00:00
refactor(wakubridge): split message compatibility from wakubridge
This commit is contained in:
parent
9c3e5f5bc5
commit
a853bf52f0
52
apps/wakubridge/message_compat.nim
Normal file
52
apps/wakubridge/message_compat.nim
Normal 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
|
@ -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 =
|
||||
|
@ -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):
|
||||
|
6
tests/all_tests_wakubridge.nim
Normal file
6
tests/all_tests_wakubridge.nim
Normal file
@ -0,0 +1,6 @@
|
||||
{.used.}
|
||||
|
||||
# Wakubridge test suite
|
||||
import
|
||||
./wakubridge/test_message_compat,
|
||||
./wakubridge/test_wakubridge
|
@ -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"
|
||||
|
57
tests/wakubridge/test_message_compat.nim
Normal file
57
tests/wakubridge/test_message_compat.nim
Normal 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
|
||||
|
@ -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)
|
Loading…
x
Reference in New Issue
Block a user