mirror of https://github.com/waku-org/nwaku.git
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:
parent
9a45aa7055
commit
b7998de09d
|
@ -192,6 +192,7 @@ procSuite "Waku rln relay":
|
|||
|
||||
# initialize the WakuRLNRelay
|
||||
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
||||
membershipIndex: uint(0),
|
||||
ethClientAddress: EthClient,
|
||||
ethAccountAddress: ethAccountAddress,
|
||||
membershipContractAddress: contractAddress)
|
||||
|
@ -218,8 +219,43 @@ procSuite "Waku rln relay":
|
|||
ethAccountAddress = accounts[9]
|
||||
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
|
||||
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()
|
||||
|
||||
|
@ -274,10 +310,10 @@ suite "Waku rln relay":
|
|||
var empty : array[32,byte]
|
||||
check:
|
||||
key.isSome
|
||||
key.get().secretKey.len == 32
|
||||
key.get().publicKey.len == 32
|
||||
key.get().secretKey != empty
|
||||
key.get().publicKey != empty
|
||||
key.get().idKey.len == 32
|
||||
key.get().idCommitment.len == 32
|
||||
key.get().idKey != empty
|
||||
key.get().idCommitment != empty
|
||||
|
||||
debug "the generated membership key pair: ", key
|
||||
|
||||
|
@ -309,6 +345,24 @@ suite "Waku rln relay":
|
|||
var rootValue2 = cast[ptr array[32,byte]] (root2.`ptr`)
|
||||
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
|
||||
doAssert(rootHex1 == rootHex2)
|
||||
|
||||
|
@ -321,7 +375,7 @@ suite "Waku rln relay":
|
|||
# generate a key pair
|
||||
var keypair = membershipKeyGen(rlnInstance.value)
|
||||
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
|
||||
|
||||
# add the member to the tree
|
||||
|
@ -340,6 +394,27 @@ suite "Waku rln relay":
|
|||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
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":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
|
@ -357,7 +432,7 @@ suite "Waku rln relay":
|
|||
# generate a key pair
|
||||
var keypair = membershipKeyGen(rlnInstance.value)
|
||||
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
|
||||
|
||||
# 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 root of the tree after one insertion followed by a deletion
|
||||
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":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
|
@ -446,7 +567,7 @@ suite "Waku rln relay":
|
|||
|
||||
# create the membership key
|
||||
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
|
||||
var index = 5
|
||||
|
@ -459,11 +580,11 @@ suite "Waku rln relay":
|
|||
var member_is_added: bool = false
|
||||
if (i == index):
|
||||
# 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)
|
||||
else:
|
||||
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)
|
||||
# check the member is added
|
||||
doAssert(member_is_added)
|
||||
|
|
|
@ -410,30 +410,74 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
|
|||
|
||||
when defined(rln):
|
||||
proc mountRlnRelay*(node: WakuNode,
|
||||
ethClientAddress: Option[string] = none(string),
|
||||
ethAccountAddress: Option[Address] = none(Address),
|
||||
membershipContractAddress: Option[Address] = none(Address)) {.async.} =
|
||||
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)) {.async.} =
|
||||
# TODO return a bool value to indicate the success of the call
|
||||
# check whether inputs are provided
|
||||
doAssert(ethClientAddress.isSome())
|
||||
doAssert(ethAccountAddress.isSome())
|
||||
doAssert(membershipContractAddress.isSome())
|
||||
if ethClientAddrOpt.isNone():
|
||||
info "failed to mount rln relay: Ethereum client address is not provided"
|
||||
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
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
doAssert(rlnInstance.isOk)
|
||||
var rln = rlnInstance.value
|
||||
|
||||
# generate the membership keys
|
||||
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
||||
# generate the membership keys if none is provided
|
||||
# this if condition never gets through for a static group of users
|
||||
# the node should pass its keys i.e., memKeyPairOpt to the function
|
||||
if not memKeyPairOpt.isSome:
|
||||
let membershipKeyPair = rln.membershipKeyGen()
|
||||
# check whether keys are generated
|
||||
doAssert(membershipKeyPair.isSome())
|
||||
debug "the membership key for the rln relay is generated"
|
||||
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(),
|
||||
ethClientAddress: ethClientAddress.get(),
|
||||
ethAccountAddress: ethAccountAddress.get(),
|
||||
membershipContractAddress: membershipContractAddress.get())
|
||||
|
||||
# add members to the Merkle tree
|
||||
for index in 0..group.len-1:
|
||||
let member = group[index]
|
||||
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
|
||||
let is_successful = await rlnPeer.register()
|
||||
|
|
|
@ -11,20 +11,32 @@ type Bn256* = 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 -------------------------
|
||||
type MembershipKeyPair* = object
|
||||
secretKey*: array[32, byte]
|
||||
publicKey*: array[32, byte]
|
||||
# node's identity key (a secret key) which is selected randomly
|
||||
idKey*: IDKey
|
||||
# hash of node's identity key generated by
|
||||
# Poseidon hash function implemented in rln lib
|
||||
idCommitment*: IDCommitment
|
||||
|
||||
type WakuRLNRelay* = object
|
||||
membershipKeyPair*: MembershipKeyPair
|
||||
membershipIndex*: uint # index of peers in the Merkle tree
|
||||
membershipContractAddress*: Address
|
||||
ethClientAddress*: string
|
||||
ethAccountAddress*: Address
|
||||
# this field is required for signing transactions
|
||||
# TODO may need to erase this ethAccountPrivateKey when is not used
|
||||
# TODO may need to make ethAccountPrivateKey mandatory
|
||||
ethAccountPrivateKey*: Option[PrivateKey]
|
||||
membershipContractAddress*: Address
|
||||
rlnInstance*: RLN[Bn256]
|
||||
|
||||
|
||||
# inputs of the membership contract constructor
|
||||
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
|
||||
|
|
|
@ -12,6 +12,7 @@ logScope:
|
|||
topics = "wakurlnrelayutils"
|
||||
|
||||
type RLNResult* = Result[RLN[Bn256], string]
|
||||
type MerkleNodeResult* = Result[MerkleNode, string]
|
||||
# membership contract interface
|
||||
contract(MembershipContract):
|
||||
# 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]
|
||||
|
||||
var
|
||||
keypair = MembershipKeyPair(secretKey: secret, publicKey: public)
|
||||
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
||||
|
||||
|
||||
return some(keypair)
|
||||
|
@ -90,7 +91,7 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
|
|||
# does the signing using the provided key
|
||||
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
|
||||
let pk = cast[UInt256](rlnPeer.membershipKeyPair.publicKey)
|
||||
let pk = cast[UInt256](rlnPeer.membershipKeyPair.idCommitment)
|
||||
discard await sender.register(pk).send(MembershipFee)
|
||||
# TODO check the receipt and then return true/false
|
||||
await web3.close()
|
||||
|
@ -103,3 +104,29 @@ proc proofGen*(data: seq[byte]): seq[byte] =
|
|||
proc proofVrfy*(data, proof: seq[byte]): bool =
|
||||
# TODO to implement the actual proof verification logic
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue