2023-02-10 10:43:16 +01:00
|
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
else:
|
|
|
|
|
{.push raises: [].}
|
|
|
|
|
|
|
|
|
|
import
|
|
|
|
|
std/sequtils,
|
|
|
|
|
chronicles,
|
|
|
|
|
json_rpc/rpcserver,
|
|
|
|
|
eth/keys,
|
|
|
|
|
nimcrypto/sysrand
|
|
|
|
|
import
|
2023-08-09 18:11:50 +01:00
|
|
|
|
../../../common/base64,
|
2023-04-19 13:29:23 +02:00
|
|
|
|
../../../waku_core,
|
2023-04-18 15:22:10 +02:00
|
|
|
|
../../../waku_relay,
|
2023-09-11 12:02:31 +05:30
|
|
|
|
../../../waku_rln_relay,
|
|
|
|
|
../../../waku_rln_relay/rln/wrappers,
|
2023-09-22 09:36:46 -04:00
|
|
|
|
../../../waku_node,
|
2023-03-06 17:19:06 +01:00
|
|
|
|
../../message_cache,
|
2023-10-27 15:43:54 -04:00
|
|
|
|
../../handlers,
|
2023-08-07 15:11:46 +01:00
|
|
|
|
../message
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-08-16 14:02:22 +05:30
|
|
|
|
from std/times import getTime
|
|
|
|
|
from std/times import toUnix
|
|
|
|
|
|
|
|
|
|
|
2023-02-10 10:43:16 +01:00
|
|
|
|
logScope:
|
|
|
|
|
topics = "waku node jsonrpc relay_api"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const futTimeout* = 5.seconds # Max time to wait for futures
|
|
|
|
|
|
|
|
|
|
## Waku Relay JSON-RPC API
|
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
proc installRelayApiHandlers*(node: WakuNode, server: RpcServer, cache: MessageCache[string]) =
|
|
|
|
|
server.rpc("post_waku_v2_relay_v1_subscriptions") do (pubsubTopics: seq[PubsubTopic]) -> bool:
|
|
|
|
|
if pubsubTopics.len == 0:
|
|
|
|
|
raise newException(ValueError, "No pubsub topic provided")
|
|
|
|
|
|
2023-02-10 10:43:16 +01:00
|
|
|
|
## Subscribes a node to a list of PubSub topics
|
|
|
|
|
debug "post_waku_v2_relay_v1_subscriptions"
|
|
|
|
|
|
|
|
|
|
# Subscribe to all requested topics
|
2023-09-26 07:33:52 -04:00
|
|
|
|
let newTopics = pubsubTopics.filterIt(not cache.isSubscribed(it))
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
for pubsubTopic in newTopics:
|
|
|
|
|
if pubsubTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty pubsub topic")
|
|
|
|
|
|
|
|
|
|
cache.subscribe(pubsubTopic)
|
|
|
|
|
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(messageCacheHandler(cache)))
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
server.rpc("delete_waku_v2_relay_v1_subscriptions") do (pubsubTopics: seq[PubsubTopic]) -> bool:
|
|
|
|
|
if pubsubTopics.len == 0:
|
|
|
|
|
raise newException(ValueError, "No pubsub topic provided")
|
|
|
|
|
|
2023-02-10 10:43:16 +01:00
|
|
|
|
## Unsubscribes a node from a list of PubSub topics
|
|
|
|
|
debug "delete_waku_v2_relay_v1_subscriptions"
|
|
|
|
|
|
|
|
|
|
# Unsubscribe all handlers from requested topics
|
2023-09-26 07:33:52 -04:00
|
|
|
|
let subscribedTopics = pubsubTopics.filterIt(cache.isSubscribed(it))
|
|
|
|
|
|
|
|
|
|
for pubsubTopic in subscribedTopics:
|
|
|
|
|
if pubsubTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty pubsub topic")
|
2023-08-23 09:53:17 -04:00
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
cache.unsubscribe(pubsubTopic)
|
|
|
|
|
node.unsubscribe((kind: PubsubUnsub, topic: pubsubTopic))
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
2023-09-01 15:03:59 +02:00
|
|
|
|
server.rpc("post_waku_v2_relay_v1_message") do (pubsubTopic: PubsubTopic, msg: WakuMessageRPC) -> bool:
|
2023-09-26 07:33:52 -04:00
|
|
|
|
if pubsubTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty pubsub topic")
|
|
|
|
|
|
2023-02-10 10:43:16 +01:00
|
|
|
|
## Publishes a WakuMessage to a PubSub topic
|
2023-09-01 15:03:59 +02:00
|
|
|
|
debug "post_waku_v2_relay_v1_message", pubsubTopic=pubsubTopic
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-02-14 09:19:06 +01:00
|
|
|
|
let payloadRes = base64.decode(msg.payload)
|
|
|
|
|
if payloadRes.isErr():
|
|
|
|
|
raise newException(ValueError, "invalid payload format: " & payloadRes.error)
|
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
if msg.contentTopic.isNone():
|
|
|
|
|
raise newException(ValueError, "message has no content topic")
|
|
|
|
|
|
2023-08-16 14:02:22 +05:30
|
|
|
|
var message = WakuMessage(
|
2023-02-14 09:19:06 +01:00
|
|
|
|
payload: payloadRes.value,
|
2023-09-26 07:33:52 -04:00
|
|
|
|
contentTopic: msg.contentTopic.get(),
|
2023-02-14 09:19:06 +01:00
|
|
|
|
version: msg.version.get(0'u32),
|
|
|
|
|
timestamp: msg.timestamp.get(Timestamp(0)),
|
|
|
|
|
ephemeral: msg.ephemeral.get(false)
|
|
|
|
|
)
|
2023-09-01 15:03:59 +02:00
|
|
|
|
|
|
|
|
|
# ensure the node is subscribed to the pubsubTopic. otherwise it risks publishing
|
|
|
|
|
# to a topic with no connected peers
|
|
|
|
|
if pubsubTopic notin node.wakuRelay.subscribedTopics():
|
2023-09-26 07:33:52 -04:00
|
|
|
|
raise newException(
|
|
|
|
|
ValueError, "Failed to publish: Node not subscribed to pubsubTopic: " & pubsubTopic)
|
2023-09-01 15:03:59 +02:00
|
|
|
|
|
|
|
|
|
# if RLN is mounted, append the proof to the message
|
2023-09-11 12:02:31 +05:30
|
|
|
|
if not node.wakuRlnRelay.isNil():
|
|
|
|
|
# append the proof to the message
|
|
|
|
|
let success = node.wakuRlnRelay.appendRLNProof(message,
|
|
|
|
|
float64(getTime().toUnix()))
|
|
|
|
|
if not success:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: error appending RLN proof to message")
|
|
|
|
|
# validate the message before sending it
|
|
|
|
|
let result = node.wakuRlnRelay.validateMessage(message)
|
|
|
|
|
if result == MessageValidationResult.Invalid:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: invalid RLN proof")
|
|
|
|
|
elif result == MessageValidationResult.Spam:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: limit exceeded, try again later")
|
|
|
|
|
elif result == MessageValidationResult.Valid:
|
2023-09-26 07:33:52 -04:00
|
|
|
|
debug "RLN proof validated successfully", pubSubTopic=pubsubTopic
|
2023-09-01 15:03:59 +02:00
|
|
|
|
else:
|
2023-09-11 12:02:31 +05:30
|
|
|
|
raise newException(ValueError, "Failed to publish: unknown RLN proof validation result")
|
2023-09-01 15:03:59 +02:00
|
|
|
|
|
|
|
|
|
# if we reach here its either a non-RLN message or a RLN message with a valid proof
|
2023-11-10 15:25:07 +01:00
|
|
|
|
debug "Publishing message", pubSubTopic=pubsubTopic, rln=not node.wakuRlnRelay.isNil()
|
2023-09-26 07:33:52 -04:00
|
|
|
|
let publishFut = node.publish(some(pubsubTopic), message)
|
2023-02-10 10:43:16 +01:00
|
|
|
|
if not await publishFut.withTimeout(futTimeout):
|
2023-09-01 15:03:59 +02:00
|
|
|
|
raise newException(ValueError, "Failed to publish: timed out")
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
server.rpc("get_waku_v2_relay_v1_messages") do (pubsubTopic: PubsubTopic) -> seq[WakuMessageRPC]:
|
|
|
|
|
if pubsubTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty pubsub topic")
|
|
|
|
|
|
2023-02-10 10:43:16 +01:00
|
|
|
|
## Returns all WakuMessages received on a PubSub topic since the
|
|
|
|
|
## last time this method was called
|
2023-09-26 07:33:52 -04:00
|
|
|
|
debug "get_waku_v2_relay_v1_messages", topic=pubsubTopic
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
let msgRes = cache.getMessages(pubsubTopic, clear=true)
|
2023-02-10 10:43:16 +01:00
|
|
|
|
if msgRes.isErr():
|
2023-09-26 07:33:52 -04:00
|
|
|
|
raise newException(ValueError, "Not subscribed to pubsub topic: " & pubsubTopic)
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-02-14 09:19:06 +01:00
|
|
|
|
return msgRes.value.map(toWakuMessageRPC)
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
# Autosharding API
|
|
|
|
|
|
|
|
|
|
server.rpc("post_waku_v2_relay_v1_auto_subscriptions") do (contentTopics: seq[ContentTopic]) -> bool:
|
|
|
|
|
if contentTopics.len == 0:
|
|
|
|
|
raise newException(ValueError, "No content topic provided")
|
|
|
|
|
|
|
|
|
|
## Subscribes a node to a list of Content topics
|
|
|
|
|
debug "post_waku_v2_relay_v1_auto_subscriptions"
|
|
|
|
|
|
|
|
|
|
let newTopics = contentTopics.filterIt(not cache.isSubscribed(it))
|
|
|
|
|
|
|
|
|
|
# Subscribe to all requested topics
|
|
|
|
|
for contentTopic in newTopics:
|
|
|
|
|
if contentTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty content topic")
|
|
|
|
|
|
|
|
|
|
cache.subscribe(contentTopic)
|
|
|
|
|
node.subscribe((kind: ContentSub, topic: contentTopic), some(autoMessageCacheHandler(cache)))
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
server.rpc("delete_waku_v2_relay_v1_auto_subscriptions") do (contentTopics: seq[ContentTopic]) -> bool:
|
|
|
|
|
if contentTopics.len == 0:
|
|
|
|
|
raise newException(ValueError, "No content topic provided")
|
|
|
|
|
|
|
|
|
|
## Unsubscribes a node from a list of Content topics
|
|
|
|
|
debug "delete_waku_v2_relay_v1_auto_subscriptions"
|
|
|
|
|
|
|
|
|
|
let subscribedTopics = contentTopics.filterIt(cache.isSubscribed(it))
|
|
|
|
|
|
|
|
|
|
# Unsubscribe all handlers from requested topics
|
|
|
|
|
for contentTopic in subscribedTopics:
|
|
|
|
|
if contentTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty content topic")
|
|
|
|
|
|
|
|
|
|
cache.unsubscribe(contentTopic)
|
|
|
|
|
node.unsubscribe((kind: ContentUnsub, topic: contentTopic))
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
server.rpc("post_waku_v2_relay_v1_auto_message") do (msg: WakuMessageRPC) -> bool:
|
|
|
|
|
## Publishes a WakuMessage to a Content topic
|
|
|
|
|
debug "post_waku_v2_relay_v1_auto_message"
|
|
|
|
|
|
|
|
|
|
let payloadRes = base64.decode(msg.payload)
|
|
|
|
|
if payloadRes.isErr():
|
|
|
|
|
raise newException(ValueError, "invalid payload format: " & payloadRes.error)
|
|
|
|
|
|
|
|
|
|
if msg.contentTopic.isNone():
|
|
|
|
|
raise newException(ValueError, "message has no content topic")
|
|
|
|
|
|
|
|
|
|
var message = WakuMessage(
|
|
|
|
|
payload: payloadRes.value,
|
|
|
|
|
contentTopic: msg.contentTopic.get(),
|
|
|
|
|
version: msg.version.get(0'u32),
|
|
|
|
|
timestamp: msg.timestamp.get(Timestamp(0)),
|
|
|
|
|
ephemeral: msg.ephemeral.get(false)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# if RLN is mounted, append the proof to the message
|
|
|
|
|
if not node.wakuRlnRelay.isNil():
|
|
|
|
|
# append the proof to the message
|
|
|
|
|
let success = node.wakuRlnRelay.appendRLNProof(message,
|
|
|
|
|
float64(getTime().toUnix()))
|
|
|
|
|
if not success:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: error appending RLN proof to message")
|
|
|
|
|
# validate the message before sending it
|
|
|
|
|
let result = node.wakuRlnRelay.validateMessage(message)
|
|
|
|
|
if result == MessageValidationResult.Invalid:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: invalid RLN proof")
|
|
|
|
|
elif result == MessageValidationResult.Spam:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: limit exceeded, try again later")
|
|
|
|
|
elif result == MessageValidationResult.Valid:
|
|
|
|
|
debug "RLN proof validated successfully", contentTopic=message.contentTopic
|
|
|
|
|
else:
|
|
|
|
|
raise newException(ValueError, "Failed to publish: unknown RLN proof validation result")
|
|
|
|
|
|
|
|
|
|
# if we reach here its either a non-RLN message or a RLN message with a valid proof
|
2023-11-10 15:25:07 +01:00
|
|
|
|
debug "Publishing message", contentTopic=message.contentTopic, rln=not node.wakuRlnRelay.isNil()
|
2023-09-26 07:33:52 -04:00
|
|
|
|
let publishFut = node.publish(none(PubsubTopic), message)
|
|
|
|
|
if not await publishFut.withTimeout(futTimeout):
|
|
|
|
|
raise newException(ValueError, "Failed to publish: timed out")
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
server.rpc("get_waku_v2_relay_v1_auto_messages") do (contentTopic: ContentTopic) -> seq[WakuMessageRPC]:
|
|
|
|
|
if contentTopic == "":
|
|
|
|
|
raise newException(ValueError, "Empty content topic")
|
|
|
|
|
|
|
|
|
|
## Returns all WakuMessages received on a Content topic since the
|
|
|
|
|
## last time this method was called
|
|
|
|
|
debug "get_waku_v2_relay_v1_auto_messages", topic=contentTopic
|
|
|
|
|
|
|
|
|
|
let msgRes = cache.getMessages(contentTopic, clear=true)
|
|
|
|
|
if msgRes.isErr():
|
|
|
|
|
raise newException(ValueError, "Not subscribed to content topic: " & contentTopic)
|
2023-02-10 10:43:16 +01:00
|
|
|
|
|
2023-09-26 07:33:52 -04:00
|
|
|
|
return msgRes.value.map(toWakuMessageRPC)
|