mirror of
https://github.com/waku-org/nwaku.git
synced 2025-01-28 07:35:56 +00:00
deploy: d851d4842489b1cf0cfeb65a9bd03689ce09d991
This commit is contained in:
parent
58b03b8c84
commit
5a2680dba8
@ -129,6 +129,32 @@ proc showChatPrompt(c: Chat) =
|
||||
except IOError:
|
||||
discard
|
||||
|
||||
proc getChatLine(c: Chat, msg:WakuMessage): Result[string, string]=
|
||||
when PayloadV1:
|
||||
# Use Waku v1 payload encoding/encryption
|
||||
let
|
||||
keyInfo = KeyInfo(kind: Symmetric, symKey: c.symKey)
|
||||
decodedPayload = decodePayload(decoded.get(), keyInfo)
|
||||
|
||||
if decodedPayload.isOK():
|
||||
let
|
||||
pb = Chat2Message.init(decodedPayload.get().payload)
|
||||
chatLine = if pb.isOk: pb[].toString()
|
||||
else: string.fromBytes(decodedPayload.get().payload)
|
||||
return ok(chatLine)
|
||||
else:
|
||||
debug "Invalid encoded WakuMessage payload",
|
||||
error = decodedPayload.error
|
||||
return err("Invalid encoded WakuMessage payload")
|
||||
else:
|
||||
# No payload encoding/encryption from Waku
|
||||
let
|
||||
pb = Chat2Message.init(msg.payload)
|
||||
chatLine = if pb.isOk: pb[].toString()
|
||||
else: string.fromBytes(msg.payload)
|
||||
return ok(chatline)
|
||||
|
||||
|
||||
proc printReceivedMessage(c: Chat, msg: WakuMessage) =
|
||||
when PayloadV1:
|
||||
# Use Waku v1 payload encoding/encryption
|
||||
@ -225,6 +251,8 @@ proc publish(c: Chat, line: string) =
|
||||
debug "could not append rate limit proof to the message", success=success
|
||||
else:
|
||||
debug "rate limit proof is appended to the message", success=success
|
||||
# TODO move it to log after doogfooding
|
||||
echo "--rln epoch: ", fromEpoch(message.proof.epoch)
|
||||
if not c.node.wakuLightPush.isNil():
|
||||
# Attempt lightpush
|
||||
asyncSpawn c.node.lightpush(DefaultTopic, message, handler)
|
||||
@ -245,6 +273,7 @@ proc publish(c: Chat, line: string) =
|
||||
debug "could not append rate limit proof to the message", success=success
|
||||
else:
|
||||
debug "rate limit proof is appended to the message", success=success
|
||||
echo "--rln epoch: ", fromEpoch(message.proof.epoch)
|
||||
|
||||
if not c.node.wakuLightPush.isNil():
|
||||
# Attempt lightpush
|
||||
@ -372,7 +401,6 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
|
||||
prompt: false,
|
||||
contentTopic: conf.contentTopic,
|
||||
symKey: generateSymKey(conf.contentTopic))
|
||||
|
||||
if conf.staticnodes.len > 0:
|
||||
await connectToNodes(chat, conf.staticnodes)
|
||||
elif conf.dnsDiscovery and conf.dnsDiscoveryUrl != "":
|
||||
@ -495,16 +523,24 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
|
||||
if conf.rlnRelay:
|
||||
info "WakuRLNRelay is enabled"
|
||||
|
||||
proc spamHandler(wakuMessage: WakuMessage) {.gcsafe, closure.} =
|
||||
debug "spam handler is called"
|
||||
let chatLineResult = chat.getChatLine(wakuMessage)
|
||||
if chatLineResult.isOk():
|
||||
echo "A spam message is found and discarded : ", chatLineResult.value
|
||||
else:
|
||||
echo "A spam message is found and discarded"
|
||||
|
||||
# set up rln relay inputs
|
||||
let (groupOpt, memKeyPairOpt, memIndexOpt) = rlnRelaySetUp(conf.rlnRelayMemIndex)
|
||||
if memIndexOpt.isNone:
|
||||
error "failed to mount WakuRLNRelay"
|
||||
else:
|
||||
# mount rlnrelay in offline mode (for now)
|
||||
waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic)
|
||||
waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = some(spamHandler))
|
||||
|
||||
trace "membership id key", idkey=memKeyPairOpt.get().idKey.toHex
|
||||
trace "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex
|
||||
debug "membership id key", idkey=memKeyPairOpt.get().idKey.toHex
|
||||
debug "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex
|
||||
|
||||
# check the correct construction of the tree by comparing the calculated root against the expected root
|
||||
# no error should happen as it is already captured in the unit tests
|
||||
@ -514,8 +550,8 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
|
||||
expectedRoot = STATIC_GROUP_MERKLE_ROOT
|
||||
if root != expectedRoot:
|
||||
error "root mismatch: something went wrong not in Merkle tree construction"
|
||||
trace "the calculated root", root
|
||||
trace "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic
|
||||
debug "the calculated root", root
|
||||
debug "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic
|
||||
|
||||
|
||||
await chat.readWriteLoop()
|
||||
|
@ -241,7 +241,7 @@ type
|
||||
|
||||
rlnRelayContentTopic* {.
|
||||
desc: "the pubsub topic for which rln-relay gets enabled",
|
||||
defaultValue: "waku/2/rln-relay/proto"
|
||||
defaultValue: "/toy-chat/2/luzhou/proto"
|
||||
name: "rln-relay-content-topic" }: ContentTopic
|
||||
|
||||
rlnRelayPubsubTopic* {.
|
||||
|
@ -911,12 +911,12 @@ procSuite "WakuNode":
|
||||
wm1 = WakuMessage(payload: "message 1".toBytes(), contentTopic: contentTopic)
|
||||
proofAdded1 = node3.wakuRlnRelay.appendRLNProof(wm1, time)
|
||||
# another message in the same epoch as wm1, it will break the messaging rate limit
|
||||
wm2 = WakuMessage(payload: "message2".toBytes(), contentTopic: contentTopic)
|
||||
wm2 = WakuMessage(payload: "message 2".toBytes(), contentTopic: contentTopic)
|
||||
proofAdded2 = node3.wakuRlnRelay.appendRLNProof(wm2, time)
|
||||
# wm3 points to the next epoch
|
||||
wm3 = WakuMessage(payload: "message 3".toBytes(), contentTopic: contentTopic)
|
||||
proofAdded3 = node3.wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
||||
wm4 = WakuMessage(payload: "message4".toBytes(), contentTopic: contentTopic)
|
||||
wm4 = WakuMessage(payload: "message 4".toBytes(), contentTopic: contentTopic)
|
||||
|
||||
# check proofs are added correctly
|
||||
check:
|
||||
@ -966,9 +966,7 @@ procSuite "WakuNode":
|
||||
res2 = await completionFut2.withTimeout(10.seconds)
|
||||
|
||||
check:
|
||||
res1 or res2 == true # either of the wm1 and wm2 is relayed
|
||||
(res1 and res2) == false # either of the wm1 and wm2 is found as spam hence not relayed
|
||||
(await completionFut2.withTimeout(10.seconds)) == true
|
||||
(await completionFut3.withTimeout(10.seconds)) == true
|
||||
(await completionFut4.withTimeout(10.seconds)) == false
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# libtool - Provide generalized library-building support services.
|
||||
# Generated automatically by config.status (libbacktrace) version-unused
|
||||
# Libtool was configured on host fv-az190-599:
|
||||
# Libtool was configured on host fv-az272-316:
|
||||
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
|
||||
#
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
|
||||
|
@ -470,30 +470,34 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
|
||||
node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec))
|
||||
|
||||
when defined(rln):
|
||||
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic) =
|
||||
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) =
|
||||
## this procedure is a thin wrapper for the pubsub addValidator method
|
||||
## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic
|
||||
## if contentTopic is empty, then validation takes place for All the messages published on the given pubsubTopic
|
||||
## the message validation logic is according to https://rfc.vac.dev/spec/17/
|
||||
proc validator(topic: string, message: messages.Message): Future[pubsub.ValidationResult] {.async.} =
|
||||
debug "rln-relay topic validator is called"
|
||||
let msg = WakuMessage.init(message.data)
|
||||
if msg.isOk():
|
||||
let wakumessage = msg.value()
|
||||
# check the contentTopic
|
||||
if (wakumessage.contentTopic != "") and (contentTopic != "") and (wakumessage.contentTopic != contentTopic):
|
||||
trace "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=string.fromBytes(wakumessage.payload)
|
||||
debug "content topic did not match:", contentTopic=wakumessage.contentTopic, payload=string.fromBytes(wakumessage.payload)
|
||||
return pubsub.ValidationResult.Accept
|
||||
# validate the message
|
||||
let validationRes = node.wakuRlnRelay.validateMessage(wakumessage)
|
||||
case validationRes:
|
||||
of Valid:
|
||||
trace "message validity is verified, relaying:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
debug "message validity is verified, relaying:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
return pubsub.ValidationResult.Accept
|
||||
of Invalid:
|
||||
trace "message validity could not be verified, discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
debug "message validity could not be verified, discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
return pubsub.ValidationResult.Reject
|
||||
of Spam:
|
||||
trace "A spam message is found! yay! discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
debug "A spam message is found! yay! discarding:", wakumessage=wakumessage, payload=string.fromBytes(wakumessage.payload)
|
||||
if spamHandler.isSome:
|
||||
let handler = spamHandler.get
|
||||
handler(wakumessage)
|
||||
return pubsub.ValidationResult.Reject
|
||||
# set a validator for the supplied pubsubTopic
|
||||
let pb = PubSub(node.wakuRelay)
|
||||
@ -508,7 +512,8 @@ when defined(rln):
|
||||
memIndexOpt: Option[MembershipIndex] = none(MembershipIndex),
|
||||
onchainMode: bool = true,
|
||||
pubsubTopic: string,
|
||||
contentTopic: ContentTopic) {.async.} =
|
||||
contentTopic: ContentTopic,
|
||||
spamHandler: Option[SpamHandler] = none(SpamHandler)) {.async.} =
|
||||
# TODO return a bool value to indicate the success of the call
|
||||
# check whether inputs are provided
|
||||
|
||||
@ -601,8 +606,8 @@ when defined(rln):
|
||||
# adds a topic validator for the supplied pubsub topic at the relay protocol
|
||||
# messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped
|
||||
# the topic validator checks for the correct non-spamming proof of the message
|
||||
addRLNRelayValidator(node, pubsubTopic, contentTopic)
|
||||
debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic
|
||||
addRLNRelayValidator(node, pubsubTopic, contentTopic, spamHandler)
|
||||
debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic, contentTopic=contentTopic
|
||||
|
||||
node.wakuRlnRelay = rlnPeer
|
||||
|
||||
|
@ -116,8 +116,8 @@ const
|
||||
# the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
|
||||
STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d"
|
||||
|
||||
const EPOCH_UNIT_SECONDS* = float64(2)
|
||||
const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers
|
||||
const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds
|
||||
const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers in seconds
|
||||
# maximum allowed gap between the epochs of messages' RateLimitProofs
|
||||
const MAX_EPOCH_GAP* = int64(MAX_CLOCK_GAP_SECONDS/EPOCH_UNIT_SECONDS)
|
||||
|
||||
|
@ -16,6 +16,8 @@ logScope:
|
||||
type RLNResult* = Result[RLN[Bn256], string]
|
||||
type MerkleNodeResult* = Result[MerkleNode, string]
|
||||
type RateLimitProofResult* = Result[RateLimitProof, string]
|
||||
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure, raises: [Defect].}
|
||||
|
||||
# membership contract interface
|
||||
contract(MembershipContract):
|
||||
# TODO define a return type of bool for register method to signify a successful registration
|
||||
@ -157,6 +159,8 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh
|
||||
msg = data)
|
||||
var inputBuffer = toBuffer(serializedInputs)
|
||||
|
||||
debug "input buffer ", inputBuffer
|
||||
|
||||
# generate the proof
|
||||
var proof: Buffer
|
||||
let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof)
|
||||
@ -445,16 +449,19 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
|
||||
# get current rln epoch
|
||||
epoch = getCurrentEpoch()
|
||||
|
||||
debug "current epoch", currentEpoch=fromEpoch(epoch)
|
||||
let
|
||||
msgEpoch = msg.proof.epoch
|
||||
# calculate the gaps
|
||||
gap = compare(epoch, msgEpoch)
|
||||
|
||||
|
||||
debug "message epoch", msgEpoch=fromEpoch(msgEpoch)
|
||||
|
||||
# validate the epoch
|
||||
if abs(gap) >= MAX_EPOCH_GAP:
|
||||
# message's epoch is too old or too ahead
|
||||
# accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch
|
||||
debug "invalid message: epoch gap exceeds a threshold",gap=gap
|
||||
debug "invalid message: epoch gap exceeds a threshold",gap=gap, payload=string.fromBytes(msg.payload)
|
||||
return MessageValidationResult.Invalid
|
||||
|
||||
# verify the proof
|
||||
@ -463,31 +470,39 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
|
||||
input = concat(msg.payload, contentTopicBytes)
|
||||
if not rlnPeer.rlnInstance.proofVerify(input, msg.proof):
|
||||
# invalid proof
|
||||
debug "invalid message: invalid proof"
|
||||
debug "invalid message: invalid proof", payload=string.fromBytes(msg.payload)
|
||||
return MessageValidationResult.Invalid
|
||||
|
||||
# check if double messaging has happened
|
||||
let hasDup = rlnPeer.hasDuplicate(msg)
|
||||
if hasDup.isOk and hasDup.value == true:
|
||||
debug "invalid message: message is a spam"
|
||||
debug "invalid message: message is a spam", payload=string.fromBytes(msg.payload)
|
||||
return MessageValidationResult.Spam
|
||||
|
||||
# insert the message to the log
|
||||
# the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e.,
|
||||
# it will never error out
|
||||
discard rlnPeer.updateLog(msg)
|
||||
debug "message is valid", payload=string.fromBytes(msg.payload)
|
||||
return MessageValidationResult.Valid
|
||||
|
||||
|
||||
proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =
|
||||
## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module
|
||||
## it extracts the `contentTopic` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence
|
||||
let
|
||||
contentTopicBytes = wakumessage.contentTopic.toBytes
|
||||
output = concat(wakumessage.payload, contentTopicBytes)
|
||||
return output
|
||||
|
||||
|
||||
proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage, senderEpochTime: float64): bool =
|
||||
## returns true if it can create and append a `RateLimitProof` to the supplied `msg`
|
||||
## returns false otherwise
|
||||
## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds.
|
||||
## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`)
|
||||
|
||||
let
|
||||
contentTopicBytes = msg.contentTopic.toBytes
|
||||
input = concat(msg.payload, contentTopicBytes)
|
||||
let input = msg.toRLNSignal()
|
||||
|
||||
var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input,
|
||||
memKeys = rlnPeer.membershipKeyPair,
|
||||
|
Loading…
x
Reference in New Issue
Block a user