mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-05-02 00:23:07 +00:00
deploy: c1f14c7d62af43b606df45e32bd8ce8a1584c879
This commit is contained in:
parent
f78ffabdca
commit
2a7d5eaaa2
@ -11,6 +11,7 @@ This release contains the following:
|
|||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
- GossipSub [prune backoff period](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange) is now the recommended 1 minute
|
- GossipSub [prune backoff period](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#prune-backoff-and-peer-exchange) is now the recommended 1 minute
|
||||||
|
- Bridge now uses content topic format according to [23/WAKU2-TOPICS](https://rfc.vac.dev/spec/23/)
|
||||||
|
|
||||||
#### General refactoring
|
#### General refactoring
|
||||||
|
|
||||||
@ -22,9 +23,11 @@ This release contains the following:
|
|||||||
|
|
||||||
#### API
|
#### API
|
||||||
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Bridge no longer re-encodes already encoded payloads when publishing to V1
|
||||||
|
- Bridge now populates WakuMessage timestamps when publishing to V2
|
||||||
|
|
||||||
## 2021-07-26 v0.5.1
|
## 2021-07-26 v0.5.1
|
||||||
|
|
||||||
This patch release contains the following fix:
|
This patch release contains the following fix:
|
||||||
|
|||||||
@ -50,11 +50,12 @@ procSuite "WakuBridge":
|
|||||||
v2NodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
v2NodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||||
v2Node = WakuNode.new(v2NodeKey, ValidIpAddress.init("0.0.0.0"), Port(60002))
|
v2Node = WakuNode.new(v2NodeKey, ValidIpAddress.init("0.0.0.0"), Port(60002))
|
||||||
|
|
||||||
contentTopic = ContentTopic("/waku/1/0x1a2b3c4d/rlp")
|
contentTopic = ContentTopic("/waku/1/0x1a2b3c4d/rfc26")
|
||||||
topic = [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
|
topic = [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
|
||||||
payloadV1 = "hello from V1".toBytes()
|
payloadV1 = "hello from V1".toBytes()
|
||||||
payloadV2 = "hello from V2".toBytes()
|
payloadV2 = "hello from V2".toBytes()
|
||||||
message = WakuMessage(payload: payloadV2, contentTopic: contentTopic)
|
encodedPayloadV2 = Payload(payload: payloadV2, dst: some(nodev1Key.pubKey))
|
||||||
|
message = WakuMessage(payload: encodedPayloadV2.encode(1, rng[]).get(), contentTopic: contentTopic, version: 1)
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# Tests setup/teardown #
|
# Tests setup/teardown #
|
||||||
@ -74,14 +75,14 @@ procSuite "WakuBridge":
|
|||||||
# Expected cases
|
# Expected cases
|
||||||
|
|
||||||
check:
|
check:
|
||||||
toV1Topic(ContentTopic("/waku/1/0x00000000/rlp")) == [byte 0x00, byte 0x00, byte 0x00, byte 0x00]
|
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/rlp")
|
toV2ContentTopic([byte 0x00, byte 0x00, byte 0x00, byte 0x00]) == ContentTopic("/waku/1/0x00000000/rfc26")
|
||||||
toV1Topic(ContentTopic("/waku/1/0xffffffff/rlp")) == [byte 0xff, byte 0xff, byte 0xff, byte 0xff]
|
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/rlp")
|
toV2ContentTopic([byte 0xff, byte 0xff, byte 0xff, byte 0xff]) == ContentTopic("/waku/1/0xffffffff/rfc26")
|
||||||
toV1Topic(ContentTopic("/waku/1/0x1a2b3c4d/rlp")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
|
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/rlp")
|
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>
|
# Topic conversion should still work where '0x' prefix is omitted from <v1 topic byte array>
|
||||||
toV1Topic(ContentTopic("/waku/1/1a2b3c4d/rlp")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
|
toV1Topic(ContentTopic("/waku/1/1a2b3c4d/rfc26")) == [byte 0x1a, byte 0x2b, byte 0x3c, byte 0x4d]
|
||||||
|
|
||||||
# Invalid cases
|
# Invalid cases
|
||||||
|
|
||||||
@ -91,11 +92,11 @@ procSuite "WakuBridge":
|
|||||||
|
|
||||||
expect ValueError:
|
expect ValueError:
|
||||||
# Content topic name too short
|
# Content topic name too short
|
||||||
discard toV1Topic(ContentTopic("/waku/1/0x112233/rlp"))
|
discard toV1Topic(ContentTopic("/waku/1/0x112233/rfc26"))
|
||||||
|
|
||||||
expect ValueError:
|
expect ValueError:
|
||||||
# Content topic name not hex
|
# Content topic name not hex
|
||||||
discard toV1Topic(ContentTopic("/waku/1/my-content/rlp"))
|
discard toV1Topic(ContentTopic("/waku/1/my-content/rfc26"))
|
||||||
|
|
||||||
asyncTest "Messages are bridged between Waku v1 and Waku v2":
|
asyncTest "Messages are bridged between Waku v1 and Waku v2":
|
||||||
# Setup test
|
# Setup test
|
||||||
@ -103,7 +104,7 @@ procSuite "WakuBridge":
|
|||||||
waitFor bridge.start()
|
waitFor bridge.start()
|
||||||
|
|
||||||
waitFor v2Node.start()
|
waitFor v2Node.start()
|
||||||
v2Node.mountRelay(@[DefaultBridgeTopic])
|
v2Node.mountRelay(@[DefaultBridgeTopic], triggerSelf = false)
|
||||||
|
|
||||||
discard waitFor v1Node.rlpxConnect(newNode(bridge.nodev1.toENode()))
|
discard waitFor v1Node.rlpxConnect(newNode(bridge.nodev1.toENode()))
|
||||||
waitFor v2Node.connectToNodes(@[bridge.nodev2.peerInfo])
|
waitFor v2Node.connectToNodes(@[bridge.nodev2.peerInfo])
|
||||||
@ -134,12 +135,14 @@ procSuite "WakuBridge":
|
|||||||
# v1Node received message published by v2Node
|
# v1Node received message published by v2Node
|
||||||
v1Node.protocolState(Waku).queue.items.len == 1
|
v1Node.protocolState(Waku).queue.items.len == 1
|
||||||
|
|
||||||
let msg = v1Node.protocolState(Waku).queue.items[0]
|
let
|
||||||
|
msg = v1Node.protocolState(Waku).queue.items[0]
|
||||||
|
decodedPayload = msg.env.data.decode(some(nodev1Key.seckey), none[SymKey]()).get()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Message fields are as expected
|
# Message fields are as expected
|
||||||
msg.env.topic == topic # Topic translation worked
|
msg.env.topic == topic # Topic translation worked
|
||||||
string.fromBytes(msg.env.data).contains("from V2")
|
string.fromBytes(decodedPayload.payload).contains("from V2")
|
||||||
|
|
||||||
# Test bridging from V1 to V2
|
# Test bridging from V1 to V2
|
||||||
check:
|
check:
|
||||||
|
|||||||
@ -65,7 +65,7 @@ proc containsOrAdd(sequence: var seq[hashes.Hash], hash: hashes.Hash): bool =
|
|||||||
|
|
||||||
proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
|
proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
|
||||||
## Convert a 4-byte array v1 topic to a namespaced content topic
|
## Convert a 4-byte array v1 topic to a namespaced content topic
|
||||||
## with format `/waku/1/<v1-topic-bytes-as-hex>/proto`
|
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
||||||
##
|
##
|
||||||
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
|
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
|
||||||
|
|
||||||
@ -74,13 +74,13 @@ proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
|
|||||||
namespacedTopic.application = "waku"
|
namespacedTopic.application = "waku"
|
||||||
namespacedTopic.version = "1"
|
namespacedTopic.version = "1"
|
||||||
namespacedTopic.topicName = "0x" & v1Topic.toHex()
|
namespacedTopic.topicName = "0x" & v1Topic.toHex()
|
||||||
namespacedTopic.encoding = "rlp"
|
namespacedTopic.encoding = "rfc26"
|
||||||
|
|
||||||
return ContentTopic($namespacedTopic)
|
return ContentTopic($namespacedTopic)
|
||||||
|
|
||||||
proc toV1Topic*(contentTopic: ContentTopic): waku_protocol.Topic {.raises: [Defect, LPError, ValueError]} =
|
proc toV1Topic*(contentTopic: ContentTopic): waku_protocol.Topic {.raises: [Defect, LPError, ValueError]} =
|
||||||
## Extracts the 4-byte array v1 topic from a content topic
|
## Extracts the 4-byte array v1 topic from a content topic
|
||||||
## with format `/waku/1/<v1-topic-bytes-as-hex>/proto`
|
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
||||||
|
|
||||||
hexToByteArray(hexStr = NamespacedTopic.fromString(contentTopic).tryGet().topicName,
|
hexToByteArray(hexStr = NamespacedTopic.fromString(contentTopic).tryGet().topicName,
|
||||||
N = 4) # Byte array length
|
N = 4) # Byte array length
|
||||||
@ -91,6 +91,7 @@ func toWakuMessage(env: Envelope): WakuMessage =
|
|||||||
# Translate a Waku v1 envelope to a Waku v2 message
|
# Translate a Waku v1 envelope to a Waku v2 message
|
||||||
WakuMessage(payload: env.data,
|
WakuMessage(payload: env.data,
|
||||||
contentTopic: toV2ContentTopic(env.topic),
|
contentTopic: toV2ContentTopic(env.topic),
|
||||||
|
timestamp: float64(env.expiry - env.ttl),
|
||||||
version: 1)
|
version: 1)
|
||||||
|
|
||||||
proc toWakuV2(bridge: WakuBridge, env: Envelope) {.async.} =
|
proc toWakuV2(bridge: WakuBridge, env: Envelope) {.async.} =
|
||||||
@ -121,10 +122,16 @@ proc toWakuV1(bridge: WakuBridge, msg: WakuMessage) {.gcsafe, raises: [Defect, L
|
|||||||
|
|
||||||
# @TODO: use namespacing to map v2 contentTopics to v1 topics
|
# @TODO: use namespacing to map v2 contentTopics to v1 topics
|
||||||
let v1TopicSeq = msg.contentTopic.toBytes()[0..3]
|
let v1TopicSeq = msg.contentTopic.toBytes()[0..3]
|
||||||
|
|
||||||
discard bridge.nodev1.postMessage(ttl = DefaultTTL,
|
case msg.version:
|
||||||
topic = toV1Topic(msg.contentTopic),
|
of 1:
|
||||||
payload = msg.payload)
|
discard bridge.nodev1.postEncoded(ttl = DefaultTTL,
|
||||||
|
topic = toV1Topic(msg.contentTopic),
|
||||||
|
encodedPayload = msg.payload) # The payload is already encoded according to https://rfc.vac.dev/spec/26/
|
||||||
|
else:
|
||||||
|
discard bridge.nodev1.postMessage(ttl = DefaultTTL,
|
||||||
|
topic = toV1Topic(msg.contentTopic),
|
||||||
|
payload = msg.payload)
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# Public API #
|
# Public API #
|
||||||
|
|||||||
@ -497,6 +497,55 @@ proc queueMessage(node: EthereumNode, msg: Message): bool =
|
|||||||
|
|
||||||
# Public EthereumNode calls ----------------------------------------------------
|
# Public EthereumNode calls ----------------------------------------------------
|
||||||
|
|
||||||
|
proc postEncoded*(node: EthereumNode, ttl: uint32,
|
||||||
|
topic: Topic, encodedPayload: seq[byte],
|
||||||
|
powTime = 1'f,
|
||||||
|
powTarget = defaultMinPow,
|
||||||
|
targetPeer = none[NodeId]()): bool =
|
||||||
|
## Post a message from pre-encoded payload on the message queue.
|
||||||
|
## This will be processed at the next `messageInterval`.
|
||||||
|
## The encodedPayload must be encoded according to RFC 26/WAKU-PAYLOAD
|
||||||
|
## at https://rfc.vac.dev/spec/26/
|
||||||
|
|
||||||
|
var env = Envelope(expiry:epochTime().uint32 + ttl,
|
||||||
|
ttl: ttl, topic: topic, data: encodedPayload, nonce: 0)
|
||||||
|
|
||||||
|
# Allow lightnode to post only direct p2p messages
|
||||||
|
if targetPeer.isSome():
|
||||||
|
return node.sendP2PMessage(targetPeer.get(), [env])
|
||||||
|
else:
|
||||||
|
# non direct p2p message can not have ttl of 0
|
||||||
|
if env.ttl == 0:
|
||||||
|
return false
|
||||||
|
var msg = initMessage(env, powCalc = false)
|
||||||
|
# XXX: make this non blocking or not?
|
||||||
|
# In its current blocking state, it could be noticed by a peer that no
|
||||||
|
# messages are send for a while, and thus that mining PoW is done, and
|
||||||
|
# that next messages contains a message originated from this peer
|
||||||
|
# zah: It would be hard to execute this in a background thread at the
|
||||||
|
# moment. We'll need a way to send custom "tasks" to the async message
|
||||||
|
# loop (e.g. AD2 support for AsyncChannels).
|
||||||
|
if not msg.sealEnvelope(powTime, powTarget):
|
||||||
|
return false
|
||||||
|
|
||||||
|
# need to check expiry after mining PoW
|
||||||
|
if not msg.env.valid():
|
||||||
|
return false
|
||||||
|
|
||||||
|
result = node.queueMessage(msg)
|
||||||
|
|
||||||
|
# Allows light nodes to post via untrusted messages packet.
|
||||||
|
# Queue gets processed immediatly as the node sends only its own messages,
|
||||||
|
# so the privacy ship has already sailed anyhow.
|
||||||
|
# TODO:
|
||||||
|
# - Could be still a concern in terms of efficiency, if multiple messages
|
||||||
|
# need to be send.
|
||||||
|
# - For Waku Mode, the checks in processQueue are rather useless as the
|
||||||
|
# idea is to connect only to 1 node? Also refactor in that case.
|
||||||
|
if node.protocolState(Waku).config.isLightNode:
|
||||||
|
for peer in node.peers(Waku):
|
||||||
|
peer.processQueue()
|
||||||
|
|
||||||
proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
|
proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
|
||||||
symKey = none[SymKey](), src = none[PrivateKey](),
|
symKey = none[SymKey](), src = none[PrivateKey](),
|
||||||
ttl: uint32, topic: Topic, payload: seq[byte],
|
ttl: uint32, topic: Topic, payload: seq[byte],
|
||||||
@ -511,44 +560,7 @@ proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
|
|||||||
let payload = encode(node.rng[], Payload(
|
let payload = encode(node.rng[], Payload(
|
||||||
payload: payload, src: src, dst: pubKey, symKey: symKey, padding: padding))
|
payload: payload, src: src, dst: pubKey, symKey: symKey, padding: padding))
|
||||||
if payload.isSome():
|
if payload.isSome():
|
||||||
var env = Envelope(expiry:epochTime().uint32 + ttl,
|
return node.postEncoded(ttl, topic, payload.get(), powTime, powTarget, targetPeer)
|
||||||
ttl: ttl, topic: topic, data: payload.get(), nonce: 0)
|
|
||||||
|
|
||||||
# Allow lightnode to post only direct p2p messages
|
|
||||||
if targetPeer.isSome():
|
|
||||||
return node.sendP2PMessage(targetPeer.get(), [env])
|
|
||||||
else:
|
|
||||||
# non direct p2p message can not have ttl of 0
|
|
||||||
if env.ttl == 0:
|
|
||||||
return false
|
|
||||||
var msg = initMessage(env, powCalc = false)
|
|
||||||
# XXX: make this non blocking or not?
|
|
||||||
# In its current blocking state, it could be noticed by a peer that no
|
|
||||||
# messages are send for a while, and thus that mining PoW is done, and
|
|
||||||
# that next messages contains a message originated from this peer
|
|
||||||
# zah: It would be hard to execute this in a background thread at the
|
|
||||||
# moment. We'll need a way to send custom "tasks" to the async message
|
|
||||||
# loop (e.g. AD2 support for AsyncChannels).
|
|
||||||
if not msg.sealEnvelope(powTime, powTarget):
|
|
||||||
return false
|
|
||||||
|
|
||||||
# need to check expiry after mining PoW
|
|
||||||
if not msg.env.valid():
|
|
||||||
return false
|
|
||||||
|
|
||||||
result = node.queueMessage(msg)
|
|
||||||
|
|
||||||
# Allows light nodes to post via untrusted messages packet.
|
|
||||||
# Queue gets processed immediatly as the node sends only its own messages,
|
|
||||||
# so the privacy ship has already sailed anyhow.
|
|
||||||
# TODO:
|
|
||||||
# - Could be still a concern in terms of efficiency, if multiple messages
|
|
||||||
# need to be send.
|
|
||||||
# - For Waku Mode, the checks in processQueue are rather useless as the
|
|
||||||
# idea is to connect only to 1 node? Also refactor in that case.
|
|
||||||
if node.protocolState(Waku).config.isLightNode:
|
|
||||||
for peer in node.peers(Waku):
|
|
||||||
peer.processQueue()
|
|
||||||
else:
|
else:
|
||||||
error "Encoding of payload failed"
|
error "Encoding of payload failed"
|
||||||
return false
|
return false
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user