From 46de4fb3f6587f0aa2ebd104f2b47d81f864c8f4 Mon Sep 17 00:00:00 2001 From: Lorenzo Delgado Date: Mon, 29 Aug 2022 16:54:11 +0200 Subject: [PATCH] fix(rest): encode waku message payload in base64 --- tests/v2/test_rest_relay_api.nim | 8 ++--- tests/v2/test_rest_relay_api_serdes.nim | 27 ++++++++------- waku/v2/node/rest/base64.nim | 27 +++++++++++++++ waku/v2/node/rest/relay/api_types.nim | 45 +++++++++++++++---------- waku/v2/node/rest/relay/relay_api.nim | 15 ++++++--- waku/v2/node/rest/relay/relay_api.yaml | 1 + waku/v2/node/rest/serdes.nim | 2 +- 7 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 waku/v2/node/rest/base64.nim diff --git a/tests/v2/test_rest_relay_api.nim b/tests/v2/test_rest_relay_api.nim index 5923d2726..1db8a6ebd 100644 --- a/tests/v2/test_rest_relay_api.nim +++ b/tests/v2/test_rest_relay_api.nim @@ -11,7 +11,7 @@ import libp2p/protocols/pubsub/pubsub import ../../waku/v2/node/wakunode2, - ../../waku/v2/node/rest/[server, client, utils], + ../../waku/v2/node/rest/[server, client, base64, utils], ../../waku/v2/node/rest/relay/[api_types, relay_api, topic_cache] @@ -164,8 +164,8 @@ suite "REST API - Relay": response.contentType == $MIMETYPE_JSON response.data.len == 3 response.data.all do (msg: RelayWakuMessage) -> bool: - msg.payload == "TEST-1" and - string(msg.contentTopic.get()) == "content-topic-x" and + msg.payload == Base64String.encode("TEST-1") and + msg.contentTopic.get().string == "content-topic-x" and msg.version.get() == Natural(1) and msg.timestamp.get() == int64(2022) @@ -211,7 +211,7 @@ suite "REST API - Relay": discard await client.relayPostSubscriptionsV1(newTopics) let response = await client.relayPostMessagesV1(defaultTopic, RelayWakuMessage( - payload: "TEST-PAYLOAD", + payload: Base64String.encode("TEST-PAYLOAD"), contentTopic: some(ContentTopicString(defaultContentTopic)), timestamp: some(int64(2022)) )) diff --git a/tests/v2/test_rest_relay_api_serdes.nim b/tests/v2/test_rest_relay_api_serdes.nim index 3da9eabc2..fe3b2a43b 100644 --- a/tests/v2/test_rest_relay_api_serdes.nim +++ b/tests/v2/test_rest_relay_api_serdes.nim @@ -1,12 +1,13 @@ {.used.} -import std/typetraits -import chronicles, - unittest2, +import stew/[results, byteutils], + chronicles, + unittest2, json_serialization import ../../waku/v2/node/rest/serdes, + ../../waku/v2/node/rest/base64, ../../waku/v2/node/rest/relay/api_types @@ -15,25 +16,27 @@ suite "Relay API - serialization": suite "RelayWakuMessage - decode": test "optional fields are not provided": # Given - let jsonBytes = toBytes("""{ "payload": "MESSAGE" }""") + let payload = Base64String.encode("MESSAGE") + let jsonBytes = toBytes("{\"payload\":\"" & $payload & "\"}") # When let res = decodeFromJsonBytes(RelayWakuMessage, jsonBytes, requireAllFields = true) # Then - require(res.isOk) + require(res.isOk()) let value = res.get() check: - value.payload == "MESSAGE" - value.contentTopic.isNone - value.version.isNone - value.timestamp.isNone + value.payload == payload + value.contentTopic.isNone() + value.version.isNone() + value.timestamp.isNone() suite "RelayWakuMessage - encode": test "optional fields are none": # Given + let payload = Base64String.encode("MESSAGE") let data = RelayWakuMessage( - payload: "MESSAGE", + payload: payload, contentTopic: none(ContentTopicString), version: none(Natural), timestamp: none(int64) @@ -43,7 +46,7 @@ suite "Relay API - serialization": let res = encodeIntoJsonBytes(data) # Then - require(res.isOk) + require(res.isOk()) let value = res.get() check: - value == toBytes("""{"payload":"MESSAGE"}""") + value == toBytes("{\"payload\":\"" & $payload & "\"}") diff --git a/waku/v2/node/rest/base64.nim b/waku/v2/node/rest/base64.nim new file mode 100644 index 000000000..ba40ca933 --- /dev/null +++ b/waku/v2/node/rest/base64.nim @@ -0,0 +1,27 @@ +{.push raises: [Defect].} + +import stew/[results, byteutils, base64] + + +type Base64String* = distinct string + + +proc encode*(t: type Base64String, value: string|seq[byte]): Base64String = + let val = block: + when value is string: + toBytes(value) + else: + value + Base64String(base64.encode(Base64, val)) + +proc decode*(t: Base64String): Result[seq[byte], cstring] = + try: + ok(base64.decode(Base64, string(t))) + except: + err("failed to decode base64 string") + +proc `$`*(t: Base64String): string {.inline.}= + string(t) + +proc `==`*(lhs: Base64String|string, rhs: Base64String|string): bool {.inline.}= + string(lhs) == string(rhs) \ No newline at end of file diff --git a/waku/v2/node/rest/relay/api_types.nim b/waku/v2/node/rest/relay/api_types.nim index c925159c3..74e7962b9 100644 --- a/waku/v2/node/rest/relay/api_types.nim +++ b/waku/v2/node/rest/relay/api_types.nim @@ -1,24 +1,25 @@ -{.push raises: [ Defect ].} +{.push raises: [Defect].} import std/[sets, strformat], - stew/byteutils, chronicles, json_serialization, json_serialization/std/options, presto/[route, client, common] -import ".."/serdes -import ../../wakunode2 +import + ../../../protocol/waku_message, + ../serdes, + ../base64 #### Types type - PubSubTopicString* = distinct string - ContentTopicString* = distinct string + PubSubTopicString* = distinct string + ContentTopicString* = distinct string type RelayWakuMessage* = object - payload*: string + payload*: Base64String contentTopic*: Option[ContentTopicString] version*: Option[Natural] timestamp*: Option[int64] @@ -37,23 +38,29 @@ type proc toRelayWakuMessage*(msg: WakuMessage): RelayWakuMessage = RelayWakuMessage( - payload: string.fromBytes(msg.payload), + payload: base64.encode(Base64String, msg.payload), contentTopic: some(ContentTopicString(msg.contentTopic)), version: some(Natural(msg.version)), timestamp: some(msg.timestamp) ) -proc toWakuMessage*(msg: RelayWakuMessage, version = 0): WakuMessage = +proc toWakuMessage*(msg: RelayWakuMessage, version = 0): Result[WakuMessage, cstring] = const defaultContentTopic = ContentTopicString("/waku/2/default-content/proto") - WakuMessage( - payload: msg.payload.toBytes(), - contentTopic: ContentTopic(msg.contentTopic.get(defaultContentTopic)), - version: uint32(msg.version.get(version)), - timestamp: msg.timestamp.get(0) - ) + let + payload = ?msg.payload.decode() + contentTopic = ContentTopic(msg.contentTopic.get(defaultContentTopic)) + version = uint32(msg.version.get(version)) + timestamp = msg.timestamp.get(0) + + ok(WakuMessage(payload: payload, contentTopic: contentTopic, version: version, timestamp: timestamp)) + #### Serialization and deserialization +proc writeValue*(writer: var JsonWriter[RestJson], value: Base64String) + {.raises: [IOError, Defect].} = + writer.writeValue(string(value)) + proc writeValue*(writer: var JsonWriter[RestJson], value: PubSubTopicString) {.raises: [IOError, Defect].} = writer.writeValue(string(value)) @@ -74,6 +81,10 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: RelayWakuMessage) writer.writeField("timestamp", value.timestamp) writer.endRecord() +proc readValue*(reader: var JsonReader[RestJson], value: var Base64String) + {.raises: [SerializationError, IOError, Defect].} = + value = Base64String(reader.readValue(string)) + proc readValue*(reader: var JsonReader[RestJson], value: var PubSubTopicString) {.raises: [SerializationError, IOError, Defect].} = value = PubSubTopicString(reader.readValue(string)) @@ -85,7 +96,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ContentTopicString) proc readValue*(reader: var JsonReader[RestJson], value: var RelayWakuMessage) {.raises: [SerializationError, IOError, Defect].} = var - payload = none(string) + payload = none(Base64String) contentTopic = none(ContentTopicString) version = none(Natural) timestamp = none(int64) @@ -100,7 +111,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var RelayWakuMessage) case fieldName of "payload": - payload = some(reader.readValue(string)) + payload = some(reader.readValue(Base64String)) of "contentTopic": contentTopic = some(reader.readValue(ContentTopicString)) of "version": diff --git a/waku/v2/node/rest/relay/relay_api.nim b/waku/v2/node/rest/relay/relay_api.nim index c0b9ead1c..1030e7ae7 100644 --- a/waku/v2/node/rest/relay/relay_api.nim +++ b/waku/v2/node/rest/relay/relay_api.nim @@ -7,9 +7,12 @@ import json_serialization, json_serialization/std/options, presto/[route, client, common] -import ".."/[serdes, utils] -import ../../wakunode2 -import "."/[api_types, topic_cache] +import + ../../wakunode2, + ../serdes, + ../utils, + ./api_types, + ./topic_cache logScope: topics = "rest_api_relay" @@ -130,9 +133,11 @@ proc installRelayPostMessagesV1Handler*(router: var RestRouter, node: WakuNode) if reqResult.isErr(): return RestApiResponse.badRequest() - let message: RelayPostMessagesRequest = reqResult.get() + let resMessage = reqResult.value.toWakuMessage(version = 0) + if resMessage.isErr(): + return RestApiResponse.badRequest() - if not (waitFor node.publish(pubSubTopic, message.toWakuMessage(version = 0)).withTimeout(futTimeout)): + if not (waitFor node.publish(pubSubTopic, resMessage.value).withTimeout(futTimeout)): error "Failed to publish message to topic", topic=pubSubTopic return RestApiResponse.internalServerError() diff --git a/waku/v2/node/rest/relay/relay_api.yaml b/waku/v2/node/rest/relay/relay_api.yaml index f84b4e7d4..3b356bb31 100644 --- a/waku/v2/node/rest/relay/relay_api.yaml +++ b/waku/v2/node/rest/relay/relay_api.yaml @@ -118,6 +118,7 @@ components: properties: payload: type: string + format: byte contentTopic: $ref: '#/components/schemas/ContentTopic' version: diff --git a/waku/v2/node/rest/serdes.nim b/waku/v2/node/rest/serdes.nim index e87b9b21b..ae0fa774a 100644 --- a/waku/v2/node/rest/serdes.nim +++ b/waku/v2/node/rest/serdes.nim @@ -1,7 +1,7 @@ {.push raises: [Defect].} -import std/typetraits import + std/typetraits, stew/results, chronicles, serialization,