mirror of https://github.com/waku-org/nwaku.git
Off-chain group construction and management (#718)
* WIP * WIP: fixes a bug * adds test for static group formation * adds static group creation when rln-relay is enabled * adds createStatic group * wip: adds group formation to mount rlnrelay * adds createMembershipList utility function * adds doc strings and todos * cleans up the code and add comments * defaults createRLNInstance depth argument to 32 * renames Depth * distinguishes between onchain and offchain modes * updates index boundaries * updates log levels * updates docstring * updates log level of displayed membership keys * relocates a todo * activates all the tests * fixes some comments and todos * extracts some utils procs for better debugging * adds todo * moves calculateMerkleRoot and toMembersipKeyPairs to the rln utils * makes calls to the utils functions * adds unit test for createMembershipList * adds unittest for toMembershipKeyPairs and calcMerkleRoot * cleans up the code and fixes tree root value * reverts an unwanted change * minor * adds comments and cleans up the code * updates config message * adds more comments * fixes a minor value mismatch * edits the size of group * minor rewording * defines a const var for the group keys * replaces the sequence literal with the StaticGroupKeys const * converts var to let when applicable * replaces hardcoded value with well-defined constants * moves createMembershipList to the rln relay utils module * renames HashSize to HashHexSize * minor updates on the comments * reorganizes the consts * indicates that rlnRelayMemIndex is an experimental option * fixes a type conversion bug * revises the unittest of "mount waku rln-relay off-chain" * clarifies the use of index * updates a docstring * removes redundant constants and capitalize all of them * deletes the ETH_CLIENT const from the test file * renames a few vars for the sake of clarity * reorganizes unittest into blocks of execution, debug messages, and checks * adds more comments * more comments and clarifications * cleans up the tests * minor * adds a minor fix * replaces a var usage with let * fixes a bug
This commit is contained in:
parent
a335a40a21
commit
4895be61ec
|
@ -2,7 +2,7 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
std/options,
|
||||
std/options, sequtils,
|
||||
testutils/unittests, chronos, chronicles, stint, web3,
|
||||
stew/byteutils, stew/shims/net as stewNet,
|
||||
libp2p/crypto/crypto,
|
||||
|
@ -11,19 +11,14 @@ import
|
|||
../test_helpers,
|
||||
./test_utils
|
||||
|
||||
|
||||
# the address of Ethereum client (ganache-cli for now)
|
||||
# TODO this address in hardcoded in the code, we may need to take it as input from the user
|
||||
const EthClient = "ws://localhost:8540/"
|
||||
|
||||
# poseidonHasherCode holds the bytecode of Poseidon hasher solidity smart contract:
|
||||
# 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
|
||||
const poseidonHasherCode = readFile("tests/v2/poseidonHasher.txt")
|
||||
# membershipContractCode contains the bytecode of the membership solidity smart contract:
|
||||
const POSEIDON_HASHER_CODE = readFile("tests/v2/poseidonHasher.txt")
|
||||
# MEMBERSHIP_CONTRACT_CODE contains the bytecode of the membership solidity smart contract:
|
||||
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/RLN.sol
|
||||
# the solidity contract is compiled separately and the resultant bytecode is copied here
|
||||
const membershipContractCode = readFile("tests/v2/membershipContract.txt")
|
||||
const MEMBERSHIP_CONTRACT_CODE = readFile("tests/v2/membershipContract.txt")
|
||||
|
||||
# the membership contract code in solidity
|
||||
# uint256 public immutable MEMBERSHIP_DEPOSIT;
|
||||
|
@ -105,7 +100,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||
|
||||
# deploy the poseidon hash first
|
||||
let
|
||||
hasherReceipt = await web3.deployContract(poseidonHasherCode)
|
||||
hasherReceipt = await web3.deployContract(POSEIDON_HASHER_CODE)
|
||||
hasherAddress = hasherReceipt.contractAddress.get
|
||||
debug "hasher address: ", hasherAddress
|
||||
|
||||
|
@ -113,7 +108,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||
# encode membership contract inputs to 32 bytes zero-padded
|
||||
let
|
||||
membershipFeeEncoded = encode(MembershipFee).data
|
||||
depthEncoded = encode(Depth).data
|
||||
depthEncoded = encode(MERKLE_TREE_DEPTH.u256).data
|
||||
hasherAddressEncoded = encode(hasherAddress).data
|
||||
# this is the contract constructor input
|
||||
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
|
||||
|
@ -125,7 +120,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||
debug "encoded contract input:" , contractInput
|
||||
|
||||
# deploy membership contract with its constructor inputs
|
||||
let receipt = await web3.deployContract(membershipContractCode, contractInput = contractInput)
|
||||
let receipt = await web3.deployContract(MEMBERSHIP_CONTRACT_CODE, contractInput = contractInput)
|
||||
var contractAddress = receipt.contractAddress.get
|
||||
debug "Address of the deployed membership contract: ", contractAddress
|
||||
|
||||
|
@ -180,15 +175,13 @@ procSuite "Waku rln relay":
|
|||
await web3.close()
|
||||
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
var rlnInstance = createRLNInstance()
|
||||
check: rlnInstance.isOk == true
|
||||
|
||||
# generate the membership keys
|
||||
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
||||
|
||||
check:
|
||||
membershipKeyPair.isSome
|
||||
check: membershipKeyPair.isSome
|
||||
|
||||
# initialize the WakuRLNRelay
|
||||
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
||||
|
@ -201,7 +194,7 @@ procSuite "Waku rln relay":
|
|||
let is_successful = await rlnPeer.register()
|
||||
check:
|
||||
is_successful
|
||||
asyncTest "mounting waku rln relay":
|
||||
asyncTest "mounting waku rln-relay":
|
||||
let
|
||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
||||
|
@ -220,7 +213,7 @@ procSuite "Waku rln relay":
|
|||
await web3.close()
|
||||
|
||||
# create current peer's pk
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check rlnInstance.isOk == true
|
||||
var rln = rlnInstance.value
|
||||
# generate a key pair
|
||||
|
@ -259,6 +252,43 @@ procSuite "Waku rln relay":
|
|||
|
||||
await node.stop()
|
||||
|
||||
asyncTest "mount waku-rln-relay in the off-chain mode":
|
||||
let
|
||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
||||
Port(60000))
|
||||
await node.start()
|
||||
|
||||
# preparing inputs to mount rln-relay
|
||||
|
||||
# create a group of 100 membership keys
|
||||
let
|
||||
(groupKeys, root) = createMembershipList(100)
|
||||
# convert the keys to MembershipKeyPair structs
|
||||
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
||||
# extract the id commitments
|
||||
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
||||
debug "groupKeyPairs", groupKeyPairs
|
||||
debug "groupIDCommitments", groupIDCommitments
|
||||
|
||||
# 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)
|
||||
|
||||
# -------- mount rln-relay in the off-chain mode
|
||||
await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false)
|
||||
|
||||
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc
|
||||
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
|
||||
debug "calculated root by mountRlnRelay", calculatedRoot
|
||||
|
||||
# this part checks whether the Merkle tree is constructed correctly inside the mountRlnRelay proc
|
||||
# this check is done by comparing the tree root resulted from mountRlnRelay i.e., calculatedRoot
|
||||
# against the root which is the expected root
|
||||
check calculatedRoot == root
|
||||
|
||||
await node.stop()
|
||||
|
||||
suite "Waku rln relay":
|
||||
test "key_gen Nim Wrappers":
|
||||
|
@ -302,7 +332,7 @@ suite "Waku rln relay":
|
|||
|
||||
test "membership Key Gen":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -319,7 +349,7 @@ suite "Waku rln relay":
|
|||
|
||||
test "get_root Nim binding":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -349,7 +379,7 @@ suite "Waku rln relay":
|
|||
doAssert(rootHex1 == rootHex2)
|
||||
test "getMerkleRoot utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -368,7 +398,7 @@ suite "Waku rln relay":
|
|||
|
||||
test "update_next_member Nim Wrapper":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -385,18 +415,18 @@ suite "Waku rln relay":
|
|||
|
||||
test "delete_member Nim wrapper":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = uint(0)
|
||||
var deleted_member_index = MembeshipIndex(0)
|
||||
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)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
var rln = rlnInstance.value
|
||||
|
@ -408,16 +438,16 @@ suite "Waku rln relay":
|
|||
|
||||
test "removeMember rln utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
var rln = rlnInstance.value
|
||||
check:
|
||||
rln.removeMember(uint(0))
|
||||
rln.removeMember(MembeshipIndex(0))
|
||||
|
||||
test "Merkle tree consistency check between deletion and insertion":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -448,7 +478,7 @@ suite "Waku rln relay":
|
|||
doAssert(root2.len == 32)
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = uint(0)
|
||||
var deleted_member_index = MembeshipIndex(0)
|
||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
doAssert(deletion_success)
|
||||
|
||||
|
@ -480,7 +510,7 @@ suite "Waku rln relay":
|
|||
doAssert(rootHex1 == rootHex3)
|
||||
test "Merkle tree consistency check between deletion and insertion using rln utils":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
var rln = rlnInstance.value()
|
||||
|
@ -503,7 +533,7 @@ suite "Waku rln relay":
|
|||
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = uint(0)
|
||||
var deleted_member_index = MembeshipIndex(0)
|
||||
let deletion_success = rln.removeMember(deleted_member_index)
|
||||
doAssert(deletion_success)
|
||||
|
||||
|
@ -526,7 +556,7 @@ suite "Waku rln relay":
|
|||
|
||||
test "hash Nim Wrappers":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -560,7 +590,7 @@ suite "Waku rln relay":
|
|||
# create an RLN instance
|
||||
|
||||
# check if the rln instance is created successfully
|
||||
var rlnInstance = createRLNInstance(32)
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
@ -573,7 +603,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: uint(index))
|
||||
var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(index))
|
||||
|
||||
# Create a Merkle tree with random members
|
||||
for i in 0..10:
|
||||
|
@ -655,7 +685,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: uint(badIndex))
|
||||
var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(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
|
||||
|
@ -667,3 +697,35 @@ suite "Waku rln relay":
|
|||
# badF=1 means the proof is not verified
|
||||
# verification of the bad proof should fail
|
||||
doAssert(badF == 1)
|
||||
|
||||
test "create a list of membership keys and construct a Merkle tree based on the list":
|
||||
let
|
||||
groupSize = 100
|
||||
(list, root) = createMembershipList(groupSize)
|
||||
|
||||
debug "created membership key list", list
|
||||
debug "the Merkle tree root", root
|
||||
|
||||
check:
|
||||
list.len == groupSize # check the number of keys
|
||||
root.len == HASH_HEX_SIZE # check the size of the calculated tree root
|
||||
|
||||
test "check correctness of toMembershipKeyPairs and calcMerkleRoot":
|
||||
let groupKeys = STATIC_GROUP_KEYS
|
||||
|
||||
# create a set of MembershipKeyPair objects from groupKeys
|
||||
let groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
||||
# extract the id commitments
|
||||
let groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
||||
# calculate the Merkle tree root out of the extracted id commitments
|
||||
let root = calcMerkleRoot(groupIDCommitments)
|
||||
|
||||
debug "groupKeyPairs", groupKeyPairs
|
||||
debug "groupIDCommitments", groupIDCommitments
|
||||
debug "root", root
|
||||
|
||||
check:
|
||||
# check that the correct number of key pairs is created
|
||||
groupKeyPairs.len == StaticGroupSize
|
||||
# compare the calculated root against the correct root
|
||||
root == STATIC_GROUP_MERKLE_ROOT
|
|
@ -75,6 +75,11 @@ type
|
|||
defaultValue: false
|
||||
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
|
||||
|
||||
staticnodes* {.
|
||||
desc: "Peer multiaddr to directly connect with. Argument may be repeated."
|
||||
name: "staticnode" }: seq[string]
|
||||
|
|
|
@ -4,6 +4,7 @@ import
|
|||
std/[options, tables, strutils, sequtils, os],
|
||||
chronos, chronicles, metrics,
|
||||
stew/shims/net as stewNet,
|
||||
stew/byteutils,
|
||||
eth/keys,
|
||||
eth/p2p/discoveryv5/enr,
|
||||
libp2p/crypto/crypto,
|
||||
|
@ -16,7 +17,7 @@ import
|
|||
../protocol/waku_swap/waku_swap,
|
||||
../protocol/waku_filter/waku_filter,
|
||||
../protocol/waku_lightpush/waku_lightpush,
|
||||
../protocol/waku_rln_relay/waku_rln_relay_types,
|
||||
../protocol/waku_rln_relay/[waku_rln_relay_types],
|
||||
../utils/peers,
|
||||
../utils/requests,
|
||||
./storage/migration/migration_types,
|
||||
|
@ -36,7 +37,7 @@ when defined(rln):
|
|||
import
|
||||
libp2p/protocols/pubsub/rpc/messages,
|
||||
web3,
|
||||
../protocol/waku_rln_relay/[rln, waku_rln_relay_utils]
|
||||
../protocol/waku_rln_relay/[rln, waku_rln_relay_utils, waku_rln_relay_utils]
|
||||
|
||||
declarePublicCounter waku_node_messages, "number of messages received", ["type"]
|
||||
declarePublicGauge waku_node_filters, "number of content filter subscriptions"
|
||||
|
@ -415,48 +416,56 @@ when defined(rln):
|
|||
memContractAddOpt: Option[Address] = none(Address),
|
||||
groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]),
|
||||
memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair),
|
||||
memIndexOpt: Option[uint] = none(uint)) {.async.} =
|
||||
memIndexOpt: Option[uint] = none(uint),
|
||||
onchainMode: bool = true) {.async.} =
|
||||
# TODO return a bool value to indicate the success of the call
|
||||
# check whether inputs are provided
|
||||
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 onchainMode:
|
||||
if memContractAddOpt.isNone():
|
||||
error "failed to mount rln relay: membership contract address is not provided"
|
||||
return
|
||||
if ethClientAddrOpt.isNone():
|
||||
error "failed to mount rln relay: Ethereum client address is not provided"
|
||||
return
|
||||
if ethAccAddrOpt.isNone():
|
||||
error "failed to mount rln relay: Ethereum account address is not provided"
|
||||
return
|
||||
else:
|
||||
if groupOpt.isNone():
|
||||
error "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"
|
||||
error "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"
|
||||
error "failed to mount rln relay: membership index is not provided"
|
||||
return
|
||||
|
||||
let
|
||||
var
|
||||
ethClientAddr: string
|
||||
ethAccAddr: Address
|
||||
memContractAdd: Address
|
||||
if onchainMode:
|
||||
ethClientAddr = ethClientAddrOpt.get()
|
||||
ethAccAddr = ethAccAddrOpt.get()
|
||||
memContractAdd = memContractAddOpt.get()
|
||||
|
||||
let
|
||||
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)
|
||||
var rlnInstance = createRLNInstance()
|
||||
doAssert(rlnInstance.isOk)
|
||||
var rln = rlnInstance.value
|
||||
|
||||
# generate the membership keys if none is provided
|
||||
# this if condition never gets through for a static group of users
|
||||
# in a happy path, this 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()
|
||||
|
@ -479,11 +488,12 @@ when defined(rln):
|
|||
ethAccountAddress: ethAccAddr,
|
||||
rlnInstance: rln)
|
||||
|
||||
# register the rln-relay peer to the membership contract
|
||||
let is_successful = await rlnPeer.register()
|
||||
# check whether registration is done
|
||||
doAssert(is_successful)
|
||||
debug "peer is successfully registered into the membership contract"
|
||||
if onchainMode:
|
||||
# register the rln-relay peer to the membership contract
|
||||
let is_successful = await rlnPeer.register()
|
||||
# check whether registration is done
|
||||
doAssert(is_successful)
|
||||
debug "peer is successfully registered into the membership contract"
|
||||
|
||||
node.wakuRlnRelay = rlnPeer
|
||||
|
||||
|
@ -539,6 +549,7 @@ 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`
|
||||
|
@ -573,10 +584,41 @@ proc mountRelay*(node: WakuNode,
|
|||
|
||||
when defined(rln):
|
||||
if rlnRelayEnabled:
|
||||
# TODO pass rln relay inputs to this proc, right now it uses default values that are set in the mountRlnRelay proc
|
||||
# TODO get user inputs via cli options
|
||||
info "WakuRLNRelay is enabled"
|
||||
waitFor mountRlnRelay(node)
|
||||
info "WakuRLNRelay is mounted successfully"
|
||||
|
||||
# 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"
|
||||
|
||||
|
@ -844,7 +886,8 @@ when isMainModule:
|
|||
mountRelay(node,
|
||||
conf.topics.split(" "),
|
||||
rlnRelayEnabled = conf.rlnRelay,
|
||||
relayMessages = conf.relay) # Indicates if node is capable to relay messages
|
||||
relayMessages = conf.relay, # Indicates if node is capable to relay messages
|
||||
rlnRelayMemIndex = conf.rlnRelayMemIndex)
|
||||
|
||||
# Keepalive mounted on all nodes
|
||||
mountLibp2pPing(node)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -18,7 +18,7 @@ contract(MembershipContract):
|
|||
# TODO define a return type of bool for register method to signify a successful registration
|
||||
proc register(pubkey: Uint256) # external payable
|
||||
|
||||
proc createRLNInstance*(d: int): RLNResult
|
||||
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||
{.raises: [Defect, IOError].} =
|
||||
|
||||
## generates an instance of RLN
|
||||
|
@ -114,7 +114,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: uint): bool =
|
||||
proc removeMember*(rlnInstance: RLN[Bn256], index: MembeshipIndex): bool =
|
||||
let deletion_success = delete_member(rlnInstance, index)
|
||||
return deletion_success
|
||||
|
||||
|
@ -130,3 +130,63 @@ proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
|||
var rootValue = cast[ptr array[32,byte]] (root.`ptr`)
|
||||
let merkleNode = rootValue[]
|
||||
return ok(merkleNode)
|
||||
|
||||
proc toMembershipKeyPairs*(groupKeys: seq[(string, string)]): seq[MembershipKeyPair] {.raises: [Defect, ValueError]} =
|
||||
## groupKeys is sequence of membership key tuples in the form of (identity key, identity commitment) all in the hexadecimal format
|
||||
## the toMembershipKeyPairs proc populates a sequence of MembershipKeyPairs using the supplied groupKeys
|
||||
|
||||
var groupKeyPairs = newSeq[MembershipKeyPair]()
|
||||
|
||||
for i in 0..groupKeys.len-1:
|
||||
let
|
||||
idKey = groupKeys[i][0].hexToByteArray(32)
|
||||
idCommitment = groupKeys[i][1].hexToByteArray(32)
|
||||
groupKeyPairs.add(MembershipKeyPair(idKey: idKey, idCommitment: idCommitment))
|
||||
return groupKeyPairs
|
||||
|
||||
proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError].} =
|
||||
## returns the root of the Merkle tree that is computed from the supplied list
|
||||
## the root is in hexadecimal format
|
||||
|
||||
var rlnInstance = createRLNInstance()
|
||||
doAssert(rlnInstance.isOk)
|
||||
var rln = rlnInstance.value
|
||||
|
||||
# create a Merkle tree
|
||||
for i in 0..list.len-1:
|
||||
var member_is_added = false
|
||||
member_is_added = rln.insertMember(list[i])
|
||||
doAssert(member_is_added)
|
||||
|
||||
let root = rln.getMerkleRoot().value().toHex
|
||||
return root
|
||||
|
||||
proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [Defect, IOError].} =
|
||||
## createMembershipList produces a sequence of membership key pairs in the form of (identity key, id commitment keys) in the hexadecimal format
|
||||
## this proc also returns the root of a Merkle tree constructed out of the identity commitment keys of the generated list
|
||||
## the output of this proc is used to initialize a static group keys (to test waku-rln-relay in the off-chain mode)
|
||||
|
||||
# initialize a Merkle tree
|
||||
var rlnInstance = createRLNInstance()
|
||||
if not rlnInstance.isOk:
|
||||
return (@[], "")
|
||||
var rln = rlnInstance.value
|
||||
|
||||
var output = newSeq[(string,string)]()
|
||||
for i in 0..n-1:
|
||||
|
||||
# generate a key pair
|
||||
let keypair = rln.membershipKeyGen()
|
||||
doAssert(keypair.isSome())
|
||||
|
||||
let keyTuple = (keypair.get().idKey.toHex, keypair.get().idCommitment.toHex)
|
||||
output.add(keyTuple)
|
||||
|
||||
# insert the key to the Merkle tree
|
||||
let inserted = rln.insertMember(keypair.get().idCommitment)
|
||||
if not inserted:
|
||||
return (@[], "")
|
||||
|
||||
|
||||
let root = rln.getMerkleRoot().value.toHex
|
||||
return (output, root)
|
Loading…
Reference in New Issue