RLN-Relay static group construction (#708)

* tests rln instance as pointer

* test insertion

* tests deletion

* tests proof generation and verification

* updates rln instance type and the rln api

* deletes old API

* removes temporary tests

* deletes unused codes

* Delete settings.json

* reverts the changes in tests v2

* removes an old comment

* adds member insertion and deletion

* adds getMerkleRoot and unit tests

* makes insertMember argument  a value type

* adds static group creation

* adds rln field to WakuRLNRelay type, and enables static group construction inside mountRlnRelay

* renames self to membershipKeyPair and removes key generation from mountRlnRelay

* renames sk,pk tp idKey and idCommitment

* updates mountRlnRelay arguments name

* logs created keys

* uncomments the key generation and adds explainer about it

* enables all the  tests

* adds comments to the rln relay types

* logs error message for the arguments that are not set
This commit is contained in:
Sanaz Taheri Boshrooyeh 2021-08-26 16:14:51 -07:00 committed by GitHub
parent 9a45aa7055
commit b7998de09d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 236 additions and 32 deletions

View File

@ -192,6 +192,7 @@ procSuite "Waku rln relay":
# initialize the WakuRLNRelay # initialize the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(), var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
membershipIndex: uint(0),
ethClientAddress: EthClient, ethClientAddress: EthClient,
ethAccountAddress: ethAccountAddress, ethAccountAddress: ethAccountAddress,
membershipContractAddress: contractAddress) membershipContractAddress: contractAddress)
@ -218,8 +219,43 @@ procSuite "Waku rln relay":
ethAccountAddress = accounts[9] ethAccountAddress = accounts[9]
await web3.close() await web3.close()
# create current peer's pk
var rlnInstance = createRLNInstance(32)
check rlnInstance.isOk == true
var rln = rlnInstance.value
# generate a key pair
var keypair = rln.membershipKeyGen()
doAssert(keypair.isSome())
# current peer index in the Merkle tree
let index = uint(5)
# Create a group of 10 members
var group = newSeq[IDCommitment]()
for i in 0..10:
var member_is_added: bool = false
if (uint(i) == index):
# insert the current peer's pk
group.add(keypair.get().idCommitment)
member_is_added = rln.insertMember(keypair.get().idCommitment)
doAssert(member_is_added)
debug "member key", key=keypair.get().idCommitment.toHex
else:
var memberKeypair = rln.membershipKeyGen()
doAssert(memberKeypair.isSome())
group.add(memberKeypair.get().idCommitment)
member_is_added = rln.insertMember(memberKeypair.get().idCommitment)
doAssert(member_is_added)
debug "member key", key=memberKeypair.get().idCommitment.toHex
let expectedRoot = rln.getMerkleRoot().value().toHex
debug "expected root ", expectedRoot
# start rln-relay # start rln-relay
await node.mountRlnRelay(ethClientAddress = some(EthClient), ethAccountAddress = some(ethAccountAddress), membershipContractAddress = some(membershipContractAddress)) await node.mountRlnRelay(ethClientAddrOpt = some(EthClient), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index))
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
debug "calculated root ", calculatedRoot
check expectedRoot == calculatedRoot
await node.stop() await node.stop()
@ -274,10 +310,10 @@ suite "Waku rln relay":
var empty : array[32,byte] var empty : array[32,byte]
check: check:
key.isSome key.isSome
key.get().secretKey.len == 32 key.get().idKey.len == 32
key.get().publicKey.len == 32 key.get().idCommitment.len == 32
key.get().secretKey != empty key.get().idKey != empty
key.get().publicKey != empty key.get().idCommitment != empty
debug "the generated membership key pair: ", key debug "the generated membership key pair: ", key
@ -309,6 +345,24 @@ suite "Waku rln relay":
var rootValue2 = cast[ptr array[32,byte]] (root2.`ptr`) var rootValue2 = cast[ptr array[32,byte]] (root2.`ptr`)
let rootHex2 = rootValue2[].toHex let rootHex2 = rootValue2[].toHex
# the two roots must be identical
doAssert(rootHex1 == rootHex2)
test "getMerkleRoot utils":
# create an RLN instance which also includes an empty Merkle tree
var rlnInstance = createRLNInstance(32)
check:
rlnInstance.isOk == true
# read the Merkle Tree root
var root1 = getMerkleRoot(rlnInstance.value())
doAssert(root1.isOk)
let rootHex1 = root1.value().toHex
# read the Merkle Tree root
var root2 = getMerkleRoot(rlnInstance.value())
doAssert(root2.isOk)
let rootHex2 = root2.value().toHex
# the two roots must be identical # the two roots must be identical
doAssert(rootHex1 == rootHex2) doAssert(rootHex1 == rootHex2)
@ -321,7 +375,7 @@ suite "Waku rln relay":
# generate a key pair # generate a key pair
var keypair = membershipKeyGen(rlnInstance.value) var keypair = membershipKeyGen(rlnInstance.value)
doAssert(keypair.isSome()) doAssert(keypair.isSome())
var pkBuffer = Buffer(`ptr`: addr(keypair.get().publicKey[0]), len: 32) var pkBuffer = Buffer(`ptr`: addr(keypair.get().idCommitment[0]), len: 32)
let pkBufferPtr = addr pkBuffer let pkBufferPtr = addr pkBuffer
# add the member to the tree # add the member to the tree
@ -340,6 +394,27 @@ suite "Waku rln relay":
let deletion_success = delete_member(rlnInstance.value, deleted_member_index) let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
doAssert(deletion_success) doAssert(deletion_success)
test "insertMember rln utils":
# create an RLN instance which also includes an empty Merkle tree
var rlnInstance = createRLNInstance(32)
check:
rlnInstance.isOk == true
var rln = rlnInstance.value
# generate a key pair
var keypair = rln.membershipKeyGen()
doAssert(keypair.isSome())
check:
rln.insertMember(keypair.get().idCommitment)
test "removeMember rln utils":
# create an RLN instance which also includes an empty Merkle tree
var rlnInstance = createRLNInstance(32)
check:
rlnInstance.isOk == true
var rln = rlnInstance.value
check:
rln.removeMember(uint(0))
test "Merkle tree consistency check between deletion and insertion": test "Merkle tree consistency check between deletion and insertion":
# create an RLN instance # create an RLN instance
var rlnInstance = createRLNInstance(32) var rlnInstance = createRLNInstance(32)
@ -357,7 +432,7 @@ suite "Waku rln relay":
# generate a key pair # generate a key pair
var keypair = membershipKeyGen(rlnInstance.value) var keypair = membershipKeyGen(rlnInstance.value)
doAssert(keypair.isSome()) doAssert(keypair.isSome())
var pkBuffer = Buffer(`ptr`: addr(keypair.get().publicKey[0]), len: 32) var pkBuffer = Buffer(`ptr`: addr(keypair.get().idCommitment[0]), len: 32)
let pkBufferPtr = addr pkBuffer let pkBufferPtr = addr pkBuffer
# add the member to the tree # add the member to the tree
@ -403,6 +478,52 @@ suite "Waku rln relay":
## The initial root of the tree (empty tree) must be identical to ## The initial root of the tree (empty tree) must be identical to
## the root of the tree after one insertion followed by a deletion ## the root of the tree after one insertion followed by a deletion
doAssert(rootHex1 == rootHex3) doAssert(rootHex1 == rootHex3)
test "Merkle tree consistency check between deletion and insertion using rln utils":
# create an RLN instance
var rlnInstance = createRLNInstance(32)
check:
rlnInstance.isOk == true
var rln = rlnInstance.value()
# read the Merkle Tree root
var root1 = rln.getMerkleRoot()
doAssert(root1.isOk)
let rootHex1 = root1.value().toHex()
# generate a key pair
var keypair = rln.membershipKeyGen()
doAssert(keypair.isSome())
let member_inserted = rln.insertMember(keypair.get().idCommitment)
check member_inserted
# read the Merkle Tree root after insertion
var root2 = rln.getMerkleRoot()
doAssert(root2.isOk)
let rootHex2 = root2.value().toHex()
# delete the first member
var deleted_member_index = uint(0)
let deletion_success = rln.removeMember(deleted_member_index)
doAssert(deletion_success)
# read the Merkle Tree root after the deletion
var root3 = rln.getMerkleRoot()
doAssert(root3.isOk)
let rootHex3 = root3.value().toHex()
debug "The initial root", rootHex1
debug "The root after insertion", rootHex2
debug "The root after deletion", rootHex3
# the root must change after the insertion
doAssert(not(rootHex1 == rootHex2))
## The initial root of the tree (empty tree) must be identical to
## the root of the tree after one insertion followed by a deletion
doAssert(rootHex1 == rootHex3)
test "hash Nim Wrappers": test "hash Nim Wrappers":
# create an RLN instance # create an RLN instance
var rlnInstance = createRLNInstance(32) var rlnInstance = createRLNInstance(32)
@ -446,7 +567,7 @@ suite "Waku rln relay":
# create the membership key # create the membership key
var auth = membershipKeyGen(rlnInstance.value) var auth = membershipKeyGen(rlnInstance.value)
var skBuffer = Buffer(`ptr`: addr(auth.get().secretKey[0]), len: 32) var skBuffer = Buffer(`ptr`: addr(auth.get().idKey[0]), len: 32)
# peer's index in the Merkle Tree # peer's index in the Merkle Tree
var index = 5 var index = 5
@ -459,11 +580,11 @@ suite "Waku rln relay":
var member_is_added: bool = false var member_is_added: bool = false
if (i == index): if (i == index):
# insert the current peer's pk # insert the current peer's pk
var pkBuffer = Buffer(`ptr`: addr(auth.get().publicKey[0]), len: 32) var pkBuffer = Buffer(`ptr`: addr(auth.get().idCommitment[0]), len: 32)
member_is_added = update_next_member(rlnInstance.value, addr pkBuffer) member_is_added = update_next_member(rlnInstance.value, addr pkBuffer)
else: else:
var memberKeys = membershipKeyGen(rlnInstance.value) var memberKeys = membershipKeyGen(rlnInstance.value)
var pkBuffer = Buffer(`ptr`: addr(memberKeys.get().publicKey[0]), len: 32) var pkBuffer = Buffer(`ptr`: addr(memberKeys.get().idCommitment[0]), len: 32)
member_is_added = update_next_member(rlnInstance.value, addr pkBuffer) member_is_added = update_next_member(rlnInstance.value, addr pkBuffer)
# check the member is added # check the member is added
doAssert(member_is_added) doAssert(member_is_added)

View File

@ -410,30 +410,74 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
when defined(rln): when defined(rln):
proc mountRlnRelay*(node: WakuNode, proc mountRlnRelay*(node: WakuNode,
ethClientAddress: Option[string] = none(string), ethClientAddrOpt: Option[string] = none(string),
ethAccountAddress: Option[Address] = none(Address), ethAccAddrOpt: Option[Address] = none(Address),
membershipContractAddress: Option[Address] = none(Address)) {.async.} = memContractAddOpt: Option[Address] = none(Address),
groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]),
memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair),
memIndexOpt: Option[uint] = none(uint)) {.async.} =
# TODO return a bool value to indicate the success of the call # TODO return a bool value to indicate the success of the call
# check whether inputs are provided # check whether inputs are provided
doAssert(ethClientAddress.isSome()) if ethClientAddrOpt.isNone():
doAssert(ethAccountAddress.isSome()) info "failed to mount rln relay: Ethereum client address is not provided"
doAssert(membershipContractAddress.isSome()) return
if ethAccAddrOpt.isNone():
info "failed to mount rln relay: Ethereum account address is not provided"
return
if memContractAddOpt.isNone():
info "failed to mount rln relay: membership contract address is not provided"
return
if groupOpt.isNone():
# TODO this check is not necessary for a dynamic group
info "failed to mount rln relay: group information is not provided"
return
if memKeyPairOpt.isNone():
info "failed to mount rln relay: membership key of the node is not provided"
return
if memIndexOpt.isNone():
info "failed to mount rln relay: membership index is not provided"
return
let
ethClientAddr = ethClientAddrOpt.get()
ethAccAddr = ethAccAddrOpt.get()
memContractAdd = memContractAddOpt.get()
group = groupOpt.get()
memKeyPair = memKeyPairOpt.get()
memIndex = memIndexOpt.get()
# check the peer's index and the inclusion of user's identity commitment in the group
doAssert((memKeyPair.idCommitment) == group[int(memIndex)])
# create an RLN instance # create an RLN instance
var rlnInstance = createRLNInstance(32) var rlnInstance = createRLNInstance(32)
doAssert(rlnInstance.isOk) doAssert(rlnInstance.isOk)
var rln = rlnInstance.value
# generate the membership keys # generate the membership keys if none is provided
let membershipKeyPair = membershipKeyGen(rlnInstance.value) # this if condition never gets through for a static group of users
# check whether keys are generated # the node should pass its keys i.e., memKeyPairOpt to the function
doAssert(membershipKeyPair.isSome()) if not memKeyPairOpt.isSome:
debug "the membership key for the rln relay is generated" let membershipKeyPair = rln.membershipKeyGen()
# check whether keys are generated
doAssert(membershipKeyPair.isSome())
debug "the membership key for the rln relay is generated", idKey=membershipKeyPair.get().idKey.toHex, idCommitment=membershipKeyPair.get().idCommitment.toHex
# initialize the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(), # add members to the Merkle tree
ethClientAddress: ethClientAddress.get(), for index in 0..group.len-1:
ethAccountAddress: ethAccountAddress.get(), let member = group[index]
membershipContractAddress: membershipContractAddress.get()) let member_is_added = rln.insertMember(member)
doAssert(member_is_added)
# create the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: memKeyPair,
membershipIndex: memIndex,
membershipContractAddress: memContractAdd,
ethClientAddress: ethClientAddr,
ethAccountAddress: ethAccAddr,
rlnInstance: rln)
# register the rln-relay peer to the membership contract # register the rln-relay peer to the membership contract
let is_successful = await rlnPeer.register() let is_successful = await rlnPeer.register()

View File

@ -11,20 +11,32 @@ type Bn256* = pointer
type RLN*[E] = pointer type RLN*[E] = pointer
type IDKey* = array[32, byte]
type IDCommitment* = array[32, byte]
# represents a Merkle tree node which is the output of
# Poseidon hash function implemented by rln lib
type MerkleNode* = array[32,byte]
# Custom data types defined for waku rln relay ------------------------- # Custom data types defined for waku rln relay -------------------------
type MembershipKeyPair* = object type MembershipKeyPair* = object
secretKey*: array[32, byte] # node's identity key (a secret key) which is selected randomly
publicKey*: array[32, byte] idKey*: IDKey
# hash of node's identity key generated by
# Poseidon hash function implemented in rln lib
idCommitment*: IDCommitment
type WakuRLNRelay* = object type WakuRLNRelay* = object
membershipKeyPair*: MembershipKeyPair membershipKeyPair*: MembershipKeyPair
membershipIndex*: uint # index of peers in the Merkle tree
membershipContractAddress*: Address
ethClientAddress*: string ethClientAddress*: string
ethAccountAddress*: Address ethAccountAddress*: Address
# this field is required for signing transactions # this field is required for signing transactions
# TODO may need to erase this ethAccountPrivateKey when is not used # TODO may need to erase this ethAccountPrivateKey when is not used
# TODO may need to make ethAccountPrivateKey mandatory # TODO may need to make ethAccountPrivateKey mandatory
ethAccountPrivateKey*: Option[PrivateKey] ethAccountPrivateKey*: Option[PrivateKey]
membershipContractAddress*: Address rlnInstance*: RLN[Bn256]
# inputs of the membership contract constructor # inputs of the membership contract constructor
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils # TODO may be able to make these constants private and put them inside the waku_rln_relay_utils

View File

@ -12,6 +12,7 @@ logScope:
topics = "wakurlnrelayutils" topics = "wakurlnrelayutils"
type RLNResult* = Result[RLN[Bn256], string] type RLNResult* = Result[RLN[Bn256], string]
type MerkleNodeResult* = Result[MerkleNode, string]
# membership contract interface # membership contract interface
contract(MembershipContract): contract(MembershipContract):
# TODO define a return type of bool for register method to signify a successful registration # TODO define a return type of bool for register method to signify a successful registration
@ -77,7 +78,7 @@ proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
for (i,x) in public.mpairs: x = generatedKeys[i+32] for (i,x) in public.mpairs: x = generatedKeys[i+32]
var var
keypair = MembershipKeyPair(secretKey: secret, publicKey: public) keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
return some(keypair) return some(keypair)
@ -90,7 +91,7 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
# does the signing using the provided key # does the signing using the provided key
web3.privateKey = rlnPeer.ethAccountPrivateKey web3.privateKey = rlnPeer.ethAccountPrivateKey
var sender = web3.contractSender(MembershipContract, rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address var sender = web3.contractSender(MembershipContract, rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address
let pk = cast[UInt256](rlnPeer.membershipKeyPair.publicKey) let pk = cast[UInt256](rlnPeer.membershipKeyPair.idCommitment)
discard await sender.register(pk).send(MembershipFee) discard await sender.register(pk).send(MembershipFee)
# TODO check the receipt and then return true/false # TODO check the receipt and then return true/false
await web3.close() await web3.close()
@ -102,4 +103,30 @@ proc proofGen*(data: seq[byte]): seq[byte] =
proc proofVrfy*(data, proof: seq[byte]): bool = proc proofVrfy*(data, proof: seq[byte]): bool =
# TODO to implement the actual proof verification logic # TODO to implement the actual proof verification logic
return true return true
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
var temp = idComm
var pkBuffer = Buffer(`ptr`: addr(temp[0]), len: 32)
let pkBufferPtr = addr pkBuffer
# add the member to the tree
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
return member_is_added
proc removeMember*(rlnInstance: RLN[Bn256], index: uint): bool =
let deletion_success = delete_member(rlnInstance, index)
return deletion_success
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
# read the Merkle Tree root after insertion
var
root {.noinit.} : Buffer = Buffer()
rootPtr = addr(root)
get_root_successful = get_root(rlnInstance, rootPtr)
if (not get_root_successful): return err("could not get the root")
if (not (root.len == 32)): return err("wrong output size")
var rootValue = cast[ptr array[32,byte]] (root.`ptr`)
let merkleNode = rootValue[]
return ok(merkleNode)