Restructuring Rln-Relay mounting process (#722)

* replaces uint with MembeshipIndex type

* fixes rln relay mem index config message

* adds rln relay setup proc

* decouples relay and rln-relay

* uses MemIndexType instead of uint

* brings back the rlnRelayEnabled flag to mountRlnRelay

* deletes commented codes

* adds rln relay topic validator inside updates rln relay mounting procedure

* adds rln-relay-pubsub-topic cli option

* adds a static rln-relay topic

* deletes rlnrelayEnabled argument

* adds pubsub topic for rln-relay

* deletes static pubsub topic

* mounts relay before rlnrelay in the tests

* logs rln relay pubsub topic

* cleans up the code

* edits rlnrelay setup

* uninitializes the input parameter of rlnrelay setup

* adds comments

* removes unused comments

* compiles addRLNRelayValidtor when RLN compilation flag is set

* adds comment about topic validator

* minor

* mode modifications on the description of add validator

* Checks whether rln relay pubsub topic is within the supported topics of relay protocol

* minor

* addresses comments

membeshipindex to membershipindex,
adds default value for rln topic
adds missing return
This commit is contained in:
Sanaz Taheri Boshrooyeh 2021-09-24 10:32:45 -07:00 committed by GitHub
parent 4895be61ec
commit 645832e9fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 116 additions and 89 deletions

View File

@ -23,7 +23,7 @@ proc runBackground() {.async.} =
Port(uint16(conf.tcpPort) + conf.portsShift), extIp, extTcpPort)
await node.start()
node.mountRelay(rlnRelayEnabled = conf.rlnrelay)
node.mountRelay()
# Subscribe to a topic
let topic = cast[Topic]("foobar")

View File

@ -326,7 +326,6 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} =
await node.start()
node.mountRelay(conf.topics.split(" "),
rlnRelayEnabled = conf.rlnRelay,
relayMessages = conf.relay) # Indicates if node is capable to relay messages
node.mountLibp2pPing()

View File

@ -11,6 +11,8 @@ import
../test_helpers,
./test_utils
const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto"
# POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract:
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol
# the solidity contract is compiled separately and the resultant bytecode is copied here
@ -244,7 +246,8 @@ procSuite "Waku rln relay":
debug "expected root ", expectedRoot
# start rln-relay
await node.mountRlnRelay(ethClientAddrOpt = some(EthClient), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index))
node.mountRelay()
await node.mountRlnRelay(ethClientAddrOpt = some(EthClient), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index), pubsubTopic = RLNRELAY_PUBSUB_TOPIC)
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
debug "calculated root ", calculatedRoot
@ -274,10 +277,11 @@ procSuite "Waku rln relay":
# index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs
# the corresponding key pair will be used to mount rlnRelay on the current node
# index also represents the index of the leaf in the Merkle tree that contains node's commitment key
let index = MembeshipIndex(5)
let index = MembershipIndex(5)
# -------- mount rln-relay in the off-chain mode
await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false)
node.mountRelay()
await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false, pubsubTopic = RLNRELAY_PUBSUB_TOPIC)
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
@ -420,7 +424,7 @@ suite "Waku rln relay":
rlnInstance.isOk == true
# delete the first member
var deleted_member_index = MembeshipIndex(0)
var deleted_member_index = MembershipIndex(0)
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
doAssert(deletion_success)
@ -443,7 +447,7 @@ suite "Waku rln relay":
rlnInstance.isOk == true
var rln = rlnInstance.value
check:
rln.removeMember(MembeshipIndex(0))
rln.removeMember(MembershipIndex(0))
test "Merkle tree consistency check between deletion and insertion":
# create an RLN instance
@ -478,7 +482,7 @@ suite "Waku rln relay":
doAssert(root2.len == 32)
# delete the first member
var deleted_member_index = MembeshipIndex(0)
var deleted_member_index = MembershipIndex(0)
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
doAssert(deletion_success)
@ -533,7 +537,7 @@ suite "Waku rln relay":
# delete the first member
var deleted_member_index = MembeshipIndex(0)
var deleted_member_index = MembershipIndex(0)
let deletion_success = rln.removeMember(deleted_member_index)
doAssert(deletion_success)
@ -603,7 +607,7 @@ suite "Waku rln relay":
var index = 5
# prepare the authentication object with peer's index and sk
var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(index))
var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(index))
# Create a Merkle tree with random members
for i in 0..10:
@ -685,7 +689,7 @@ suite "Waku rln relay":
# create and test a bad proof
# prepare a bad authentication object with a wrong peer's index
var badIndex = 8
var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(badIndex))
var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(badIndex))
var badProof: Buffer
let badProofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr badAuthObj, addr badProof)
# check whether the generate_proof call is done successfully

View File

@ -5,7 +5,8 @@ import
libp2p/crypto/crypto,
libp2p/crypto/secp,
nimcrypto/utils,
eth/keys
eth/keys,
../protocol/waku_rln_relay/[waku_rln_relay_types]
type
WakuNodeConf* = object
@ -76,10 +77,14 @@ type
name: "rln-relay" }: bool
rlnRelayMemIndex* {.
desc: "(experimental) the index of node in the rln-relay group: a value between 0-49 inclusive",
defaultValue: 0
name: "rln-relay-membership-index" }: uint32
desc: "(experimental) the index of node in the rln-relay group: a value between 0-99 inclusive",
defaultValue: MembershipIndex(0)
name: "rln-relay-membership-index" }: MembershipIndex
rlnRelayPubsubTopic* {.
desc: "the pubsub topic for which rln-relay gets enabled",
defaultValue: "waku/2/rlnrelay/proto"
name: "rln-relay-pubsub-topic" }: string
staticnodes* {.
desc: "Peer multiaddr to directly connect with. Argument may be repeated."
name: "staticnode" }: seq[string]

View File

@ -408,18 +408,42 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
node.wakuStore = WakuStore.init(node.peerManager, node.rng, store, node.wakuSwap, persistMessages=persistMessages)
node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec))
when defined(rln):
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string) =
## this procedure is a thin wrapper for the pubsub addValidator method
## it sets message validator on the given pubsubTopic, the validator will check that
## all the messages published in the pubsubTopic have a valid zero-knowledge proof
proc validator(topic: string, message: messages.Message): Future[ValidationResult] {.async.} =
let msg = WakuMessage.init(message.data)
if msg.isOk():
# check the proof
if proofVrfy(msg.value().payload, msg.value().proof):
return ValidationResult.Accept
# set a validator for the pubsubTopic
let pb = PubSub(node.wakuRelay)
pb.addValidator(pubsubTopic, validator)
proc mountRlnRelay*(node: WakuNode,
ethClientAddrOpt: Option[string] = none(string),
ethAccAddrOpt: Option[Address] = none(Address),
memContractAddOpt: Option[Address] = none(Address),
groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]),
memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair),
memIndexOpt: Option[uint] = none(uint),
onchainMode: bool = true) {.async.} =
memIndexOpt: Option[MembershipIndex] = none(MembershipIndex),
onchainMode: bool = true,
pubsubTopic: string) {.async.} =
# TODO return a bool value to indicate the success of the call
# check whether inputs are provided
# relay protocol is the prerequisite of rln-relay
if node.wakuRelay.isNil:
error "Failed to mount WakuRLNRelay. Relay protocol is not mounted."
return
# check whether the pubsub topic is supported at the relay level
if pubsubTopic notin node.wakuRelay.defaultTopics:
error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic
return
if onchainMode:
if memContractAddOpt.isNone():
error "failed to mount rln relay: membership contract address is not provided"
@ -495,23 +519,15 @@ when defined(rln):
doAssert(is_successful)
debug "peer is successfully registered into the membership contract"
# 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)
debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic
node.wakuRlnRelay = rlnPeer
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string) =
## this procedure is a thin wrapper for the pubsub addValidator method
## it sets message validator on the given pubsubTopic, the validator will check that
## all the messages published in the pubsubTopic have a valid zero-knowledge proof
proc validator(topic: string, message: messages.Message): Future[ValidationResult] {.async.} =
let msg = WakuMessage.init(message.data)
if msg.isOk():
# check the proof
if proofVrfy(msg.value().payload, msg.value().proof):
return ValidationResult.Accept
# set a validator for the pubsubTopic
let pb = PubSub(node.wakuRelay)
pb.addValidator(pubsubTopic, validator)
proc startRelay*(node: WakuNode) {.async.} =
if node.wakuRelay.isNil:
trace "Failed to start relay. Not mounted."
@ -534,12 +550,6 @@ proc startRelay*(node: WakuNode) {.async.} =
await node.peerManager.reconnectPeers(WakuRelayCodec,
protocolMatcher(WakuRelayCodec),
backoffPeriod)
when defined(rln):
if node.wakuRelay.rlnRelayEnabled:
# TODO currently the message validator is set for the defaultTopic, this can be configurable to accept other pubsub topics as well
addRLNRelayValidator(node, defaultTopic)
info "WakuRLNRelay is mounted successfully"
# Start the WakuRelay protocol
await node.wakuRelay.start()
@ -548,8 +558,6 @@ proc startRelay*(node: WakuNode) {.async.} =
proc mountRelay*(node: WakuNode,
topics: seq[string] = newSeq[string](),
rlnRelayEnabled = false,
rlnRelayMemIndex = uint(0),
relayMessages = true,
triggerSelf = true)
# @TODO: Better error handling: CatchableError is raised by `waitFor`
@ -564,12 +572,11 @@ proc mountRelay*(node: WakuNode,
verifySignature = false
)
info "mounting relay", rlnRelayEnabled=rlnRelayEnabled, relayMessages=relayMessages
info "mounting relay", relayMessages=relayMessages
## The default relay topics is the union of
## all configured topics plus the hard-coded defaultTopic(s)
wakuRelay.defaultTopics = concat(@[defaultTopic], topics)
wakuRelay.rlnRelayEnabled = rlnRelayEnabled
node.switch.mount(wakuRelay, protocolMatcher(WakuRelayCodec))
@ -581,44 +588,6 @@ proc mountRelay*(node: WakuNode,
## topics. We also never start the relay protocol. node.wakuRelay remains nil.
## @TODO: in future, this WakuRelay dependency will be removed completely
node.wakuRelay = wakuRelay
when defined(rln):
if rlnRelayEnabled:
# TODO get user inputs via cli options
info "WakuRLNRelay is enabled"
# a static list of 50 membership keys in hexadecimal format
let
groupKeys = STATIC_GROUP_KEYS
groupSize = int(groupKeys.len/2)
debug "rln-relay membership index", rlnRelayMemIndex
# validate the user-supplied membership index
if rlnRelayMemIndex < uint(0) or rlnRelayMemIndex >= uint(groupSize):
error "wrong membership index, failed to mount WakuRLNRelay"
else:
# prepare group related inputs from the hardcoded keys
let
groupKeyPairs = groupKeys.toMembershipKeyPairs()
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
# mount rlnrelay in offline mode
waitFor node.mountRlnRelay(groupOpt= some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex]), memIndexOpt= some(rlnRelayMemIndex), onchainMode = false)
info "membership id key", idkey=groupKeyPairs[rlnRelayMemIndex].idKey.toHex
info "membership id commitment key", idCommitmentkey=groupIDCommitments[rlnRelayMemIndex].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
# TODO have added this check to account for unseen corner cases, will remove it later
let
root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex()
expectedRoot = STATIC_GROUP_MERKLE_ROOT
if root != expectedRoot:
error "root mismatch: something went wrong not in Merkle tree construction"
debug "the calculated root", root
info "WakuRLNRelay is mounted successfully"
info "relay mounted successfully"
@ -885,13 +854,38 @@ when isMainModule:
# Mount relay on all nodes
mountRelay(node,
conf.topics.split(" "),
rlnRelayEnabled = conf.rlnRelay,
relayMessages = conf.relay, # Indicates if node is capable to relay messages
rlnRelayMemIndex = conf.rlnRelayMemIndex)
)
# Keepalive mounted on all nodes
mountLibp2pPing(node)
when defined(rln):
if conf.rlnRelay:
info "WakuRLNRelay is enabled"
# 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)
info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex
info "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
# TODO have added this check to account for unseen corner cases, will remove it later
let
root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex()
expectedRoot = STATIC_GROUP_MERKLE_ROOT
if root != expectedRoot:
error "root mismatch: something went wrong not in Merkle tree construction"
debug "the calculated root", root
info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic
if conf.swap:
mountSwap(node)
# TODO Set swap peer, for now should be same as store peer

View File

@ -20,7 +20,6 @@ const
type
WakuRelay* = ref object of GossipSub
defaultTopics*: seq[string] # Default configured PubSub topics
rlnRelayEnabled*: bool # Flag indicating if RLN relay is enabled
method init*(w: WakuRelay) =
debug "init WakuRelay"

View File

@ -37,7 +37,7 @@ type WakuRLNRelay* = object
ethAccountPrivateKey*: Option[PrivateKey]
rlnInstance*: RLN[Bn256]
type MembeshipIndex* = uint
type MembershipIndex* = uint
# inputs of the membership contract constructor
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
@ -67,6 +67,5 @@ const
# STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above
# only identity commitments are used for the Merkle tree construction
# 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* = "25e03b7d551c3016cb21c057a113be5bf27eaa9b2529e806715bc0a1ef221e07"
STATIC_GROUP_MERKLE_ROOT* = "25e03b7d551c3016cb21c057a113be5bf27eaa9b2529e806715bc0a1ef221e07"

View File

@ -1,6 +1,7 @@
{.push raises: [Defect].}
import
std/sequtils,
chronicles, options, chronos, stint,
web3,
stew/results,
@ -114,7 +115,7 @@ proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
return member_is_added
proc removeMember*(rlnInstance: RLN[Bn256], index: MembeshipIndex): bool =
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
let deletion_success = delete_member(rlnInstance, index)
return deletion_success
@ -189,4 +190,30 @@ proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [De
let root = rln.getMerkleRoot().value.toHex
return (output, root)
return (output, root)
proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[IDCommitment]],Option[MembershipKeyPair], Option[MembershipIndex]) {.raises:[Defect, ValueError].} =
let
# static group
groupKeys = STATIC_GROUP_KEYS
groupSize = STATIC_GROUP_SIZE
debug "rln-relay membership index", rlnRelayMemIndex
# validate the user-supplied membership index
if rlnRelayMemIndex < MembershipIndex(0) or rlnRelayMemIndex >= MembershipIndex(groupSize):
error "wrong membership index"
return(none(seq[IDCommitment]), none(MembershipKeyPair), none(MembershipIndex))
# prepare the outputs from the static group keys
let
# create a sequence of MembershipKeyPairs from the group keys (group keys are in string format)
groupKeyPairs = groupKeys.toMembershipKeyPairs()
# extract id commitment keys
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
groupOpt= some(groupIDCommitments)
# user selected membership key pair
memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex])
memIndexOpt= some(rlnRelayMemIndex)
return (groupOpt, memKeyPairOpt, memIndexOpt)