mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-05 23:43:07 +00:00
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
e66d589397
commit
aa9b64c74c
@ -2,7 +2,7 @@
|
|||||||
{.used.}
|
{.used.}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/options,
|
std/options, sequtils,
|
||||||
testutils/unittests, chronos, chronicles, stint, web3,
|
testutils/unittests, chronos, chronicles, stint, web3,
|
||||||
stew/byteutils, stew/shims/net as stewNet,
|
stew/byteutils, stew/shims/net as stewNet,
|
||||||
libp2p/crypto/crypto,
|
libp2p/crypto/crypto,
|
||||||
@ -11,19 +11,14 @@ import
|
|||||||
../test_helpers,
|
../test_helpers,
|
||||||
./test_utils
|
./test_utils
|
||||||
|
|
||||||
|
# POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract:
|
||||||
# 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:
|
|
||||||
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol
|
# 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
|
# the solidity contract is compiled separately and the resultant bytecode is copied here
|
||||||
const poseidonHasherCode = readFile("tests/v2/poseidonHasher.txt")
|
const POSEIDON_HASHER_CODE = readFile("tests/v2/poseidonHasher.txt")
|
||||||
# membershipContractCode contains the bytecode of the membership solidity smart contract:
|
# MEMBERSHIP_CONTRACT_CODE contains the bytecode of the membership solidity smart contract:
|
||||||
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/RLN.sol
|
# 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
|
# 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
|
# the membership contract code in solidity
|
||||||
# uint256 public immutable MEMBERSHIP_DEPOSIT;
|
# uint256 public immutable MEMBERSHIP_DEPOSIT;
|
||||||
@ -105,7 +100,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||||||
|
|
||||||
# deploy the poseidon hash first
|
# deploy the poseidon hash first
|
||||||
let
|
let
|
||||||
hasherReceipt = await web3.deployContract(poseidonHasherCode)
|
hasherReceipt = await web3.deployContract(POSEIDON_HASHER_CODE)
|
||||||
hasherAddress = hasherReceipt.contractAddress.get
|
hasherAddress = hasherReceipt.contractAddress.get
|
||||||
debug "hasher address: ", hasherAddress
|
debug "hasher address: ", hasherAddress
|
||||||
|
|
||||||
@ -113,7 +108,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||||||
# encode membership contract inputs to 32 bytes zero-padded
|
# encode membership contract inputs to 32 bytes zero-padded
|
||||||
let
|
let
|
||||||
membershipFeeEncoded = encode(MembershipFee).data
|
membershipFeeEncoded = encode(MembershipFee).data
|
||||||
depthEncoded = encode(Depth).data
|
depthEncoded = encode(MERKLE_TREE_DEPTH.u256).data
|
||||||
hasherAddressEncoded = encode(hasherAddress).data
|
hasherAddressEncoded = encode(hasherAddress).data
|
||||||
# this is the contract constructor input
|
# this is the contract constructor input
|
||||||
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
|
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
|
||||||
@ -125,7 +120,7 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|||||||
debug "encoded contract input:" , contractInput
|
debug "encoded contract input:" , contractInput
|
||||||
|
|
||||||
# deploy membership contract with its constructor inputs
|
# 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
|
var contractAddress = receipt.contractAddress.get
|
||||||
debug "Address of the deployed membership contract: ", contractAddress
|
debug "Address of the deployed membership contract: ", contractAddress
|
||||||
|
|
||||||
@ -180,15 +175,13 @@ procSuite "Waku rln relay":
|
|||||||
await web3.close()
|
await web3.close()
|
||||||
|
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check: rlnInstance.isOk == true
|
||||||
rlnInstance.isOk == true
|
|
||||||
|
|
||||||
# generate the membership keys
|
# generate the membership keys
|
||||||
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
||||||
|
|
||||||
check:
|
check: membershipKeyPair.isSome
|
||||||
membershipKeyPair.isSome
|
|
||||||
|
|
||||||
# initialize the WakuRLNRelay
|
# initialize the WakuRLNRelay
|
||||||
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
||||||
@ -201,7 +194,7 @@ procSuite "Waku rln relay":
|
|||||||
let is_successful = await rlnPeer.register()
|
let is_successful = await rlnPeer.register()
|
||||||
check:
|
check:
|
||||||
is_successful
|
is_successful
|
||||||
asyncTest "mounting waku rln relay":
|
asyncTest "mounting waku rln-relay":
|
||||||
let
|
let
|
||||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||||
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
||||||
@ -220,7 +213,7 @@ procSuite "Waku rln relay":
|
|||||||
await web3.close()
|
await web3.close()
|
||||||
|
|
||||||
# create current peer's pk
|
# create current peer's pk
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check rlnInstance.isOk == true
|
check rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
@ -259,6 +252,43 @@ procSuite "Waku rln relay":
|
|||||||
|
|
||||||
await node.stop()
|
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":
|
suite "Waku rln relay":
|
||||||
test "key_gen Nim Wrappers":
|
test "key_gen Nim Wrappers":
|
||||||
@ -302,7 +332,7 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "membership Key Gen":
|
test "membership Key Gen":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -319,7 +349,7 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "get_root Nim binding":
|
test "get_root Nim binding":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -349,7 +379,7 @@ suite "Waku rln relay":
|
|||||||
doAssert(rootHex1 == rootHex2)
|
doAssert(rootHex1 == rootHex2)
|
||||||
test "getMerkleRoot utils":
|
test "getMerkleRoot utils":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -368,7 +398,7 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "update_next_member Nim Wrapper":
|
test "update_next_member Nim Wrapper":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -385,18 +415,18 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "delete_member Nim wrapper":
|
test "delete_member Nim wrapper":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
# delete the first member
|
# 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)
|
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||||
doAssert(deletion_success)
|
doAssert(deletion_success)
|
||||||
|
|
||||||
test "insertMember rln utils":
|
test "insertMember rln utils":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
@ -408,16 +438,16 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "removeMember rln utils":
|
test "removeMember rln utils":
|
||||||
# create an RLN instance which also includes an empty Merkle tree
|
# create an RLN instance which also includes an empty Merkle tree
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
check:
|
check:
|
||||||
rln.removeMember(uint(0))
|
rln.removeMember(MembeshipIndex(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()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -448,7 +478,7 @@ suite "Waku rln relay":
|
|||||||
doAssert(root2.len == 32)
|
doAssert(root2.len == 32)
|
||||||
|
|
||||||
# delete the first member
|
# 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)
|
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||||
doAssert(deletion_success)
|
doAssert(deletion_success)
|
||||||
|
|
||||||
@ -480,7 +510,7 @@ suite "Waku rln relay":
|
|||||||
doAssert(rootHex1 == rootHex3)
|
doAssert(rootHex1 == rootHex3)
|
||||||
test "Merkle tree consistency check between deletion and insertion using rln utils":
|
test "Merkle tree consistency check between deletion and insertion using rln utils":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value()
|
var rln = rlnInstance.value()
|
||||||
@ -503,7 +533,7 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
|
|
||||||
# delete the first member
|
# delete the first member
|
||||||
var deleted_member_index = uint(0)
|
var deleted_member_index = MembeshipIndex(0)
|
||||||
let deletion_success = rln.removeMember(deleted_member_index)
|
let deletion_success = rln.removeMember(deleted_member_index)
|
||||||
doAssert(deletion_success)
|
doAssert(deletion_success)
|
||||||
|
|
||||||
@ -526,7 +556,7 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "hash Nim Wrappers":
|
test "hash Nim Wrappers":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -560,7 +590,7 @@ suite "Waku rln relay":
|
|||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
|
|
||||||
# check if the rln instance is created successfully
|
# check if the rln instance is created successfully
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
@ -573,7 +603,7 @@ suite "Waku rln relay":
|
|||||||
var index = 5
|
var index = 5
|
||||||
|
|
||||||
# prepare the authentication object with peer's index and sk
|
# 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
|
# Create a Merkle tree with random members
|
||||||
for i in 0..10:
|
for i in 0..10:
|
||||||
@ -655,7 +685,7 @@ suite "Waku rln relay":
|
|||||||
# create and test a bad proof
|
# create and test a bad proof
|
||||||
# prepare a bad authentication object with a wrong peer's index
|
# prepare a bad authentication object with a wrong peer's index
|
||||||
var badIndex = 8
|
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
|
var badProof: Buffer
|
||||||
let badProofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr badAuthObj, addr badProof)
|
let badProofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr badAuthObj, addr badProof)
|
||||||
# check whether the generate_proof call is done successfully
|
# check whether the generate_proof call is done successfully
|
||||||
@ -666,4 +696,36 @@ suite "Waku rln relay":
|
|||||||
doAssert(badVerifyIsSuccessful)
|
doAssert(badVerifyIsSuccessful)
|
||||||
# badF=1 means the proof is not verified
|
# badF=1 means the proof is not verified
|
||||||
# verification of the bad proof should fail
|
# verification of the bad proof should fail
|
||||||
doAssert(badF == 1)
|
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
|
defaultValue: false
|
||||||
name: "rln-relay" }: bool
|
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* {.
|
staticnodes* {.
|
||||||
desc: "Peer multiaddr to directly connect with. Argument may be repeated."
|
desc: "Peer multiaddr to directly connect with. Argument may be repeated."
|
||||||
name: "staticnode" }: seq[string]
|
name: "staticnode" }: seq[string]
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import
|
|||||||
std/[options, tables, strutils, sequtils, os],
|
std/[options, tables, strutils, sequtils, os],
|
||||||
chronos, chronicles, metrics,
|
chronos, chronicles, metrics,
|
||||||
stew/shims/net as stewNet,
|
stew/shims/net as stewNet,
|
||||||
|
stew/byteutils,
|
||||||
eth/keys,
|
eth/keys,
|
||||||
eth/p2p/discoveryv5/enr,
|
eth/p2p/discoveryv5/enr,
|
||||||
libp2p/crypto/crypto,
|
libp2p/crypto/crypto,
|
||||||
@ -16,7 +17,7 @@ import
|
|||||||
../protocol/waku_swap/waku_swap,
|
../protocol/waku_swap/waku_swap,
|
||||||
../protocol/waku_filter/waku_filter,
|
../protocol/waku_filter/waku_filter,
|
||||||
../protocol/waku_lightpush/waku_lightpush,
|
../protocol/waku_lightpush/waku_lightpush,
|
||||||
../protocol/waku_rln_relay/waku_rln_relay_types,
|
../protocol/waku_rln_relay/[waku_rln_relay_types],
|
||||||
../utils/peers,
|
../utils/peers,
|
||||||
../utils/requests,
|
../utils/requests,
|
||||||
./storage/migration/migration_types,
|
./storage/migration/migration_types,
|
||||||
@ -36,7 +37,7 @@ when defined(rln):
|
|||||||
import
|
import
|
||||||
libp2p/protocols/pubsub/rpc/messages,
|
libp2p/protocols/pubsub/rpc/messages,
|
||||||
web3,
|
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"]
|
declarePublicCounter waku_node_messages, "number of messages received", ["type"]
|
||||||
declarePublicGauge waku_node_filters, "number of content filter subscriptions"
|
declarePublicGauge waku_node_filters, "number of content filter subscriptions"
|
||||||
@ -415,48 +416,56 @@ when defined(rln):
|
|||||||
memContractAddOpt: Option[Address] = none(Address),
|
memContractAddOpt: Option[Address] = none(Address),
|
||||||
groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]),
|
groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]),
|
||||||
memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair),
|
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
|
# TODO return a bool value to indicate the success of the call
|
||||||
# check whether inputs are provided
|
# check whether inputs are provided
|
||||||
if ethClientAddrOpt.isNone():
|
if onchainMode:
|
||||||
info "failed to mount rln relay: Ethereum client address is not provided"
|
if memContractAddOpt.isNone():
|
||||||
return
|
error "failed to mount rln relay: membership contract address is not provided"
|
||||||
if ethAccAddrOpt.isNone():
|
return
|
||||||
info "failed to mount rln relay: Ethereum account address is not provided"
|
if ethClientAddrOpt.isNone():
|
||||||
return
|
error "failed to mount rln relay: Ethereum client address is not provided"
|
||||||
if memContractAddOpt.isNone():
|
return
|
||||||
info "failed to mount rln relay: membership contract address is not provided"
|
if ethAccAddrOpt.isNone():
|
||||||
return
|
error "failed to mount rln relay: Ethereum account address is not provided"
|
||||||
if groupOpt.isNone():
|
return
|
||||||
# TODO this check is not necessary for a dynamic group
|
else:
|
||||||
info "failed to mount rln relay: group information is not provided"
|
if groupOpt.isNone():
|
||||||
return
|
error "failed to mount rln relay: group information is not provided"
|
||||||
|
return
|
||||||
|
|
||||||
if memKeyPairOpt.isNone():
|
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
|
return
|
||||||
if memIndexOpt.isNone():
|
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
|
return
|
||||||
|
|
||||||
let
|
var
|
||||||
|
ethClientAddr: string
|
||||||
|
ethAccAddr: Address
|
||||||
|
memContractAdd: Address
|
||||||
|
if onchainMode:
|
||||||
ethClientAddr = ethClientAddrOpt.get()
|
ethClientAddr = ethClientAddrOpt.get()
|
||||||
ethAccAddr = ethAccAddrOpt.get()
|
ethAccAddr = ethAccAddrOpt.get()
|
||||||
memContractAdd = memContractAddOpt.get()
|
memContractAdd = memContractAddOpt.get()
|
||||||
|
|
||||||
|
let
|
||||||
group = groupOpt.get()
|
group = groupOpt.get()
|
||||||
memKeyPair = memKeyPairOpt.get()
|
memKeyPair = memKeyPairOpt.get()
|
||||||
memIndex = memIndexOpt.get()
|
memIndex = memIndexOpt.get()
|
||||||
|
|
||||||
|
|
||||||
# check the peer's index and the inclusion of user's identity commitment in the group
|
# check the peer's index and the inclusion of user's identity commitment in the group
|
||||||
doAssert((memKeyPair.idCommitment) == group[int(memIndex)])
|
doAssert((memKeyPair.idCommitment) == group[int(memIndex)])
|
||||||
|
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance(32)
|
var rlnInstance = createRLNInstance()
|
||||||
doAssert(rlnInstance.isOk)
|
doAssert(rlnInstance.isOk)
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
|
|
||||||
# generate the membership keys if none is provided
|
# 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
|
# the node should pass its keys i.e., memKeyPairOpt to the function
|
||||||
if not memKeyPairOpt.isSome:
|
if not memKeyPairOpt.isSome:
|
||||||
let membershipKeyPair = rln.membershipKeyGen()
|
let membershipKeyPair = rln.membershipKeyGen()
|
||||||
@ -479,11 +488,12 @@ when defined(rln):
|
|||||||
ethAccountAddress: ethAccAddr,
|
ethAccountAddress: ethAccAddr,
|
||||||
rlnInstance: rln)
|
rlnInstance: rln)
|
||||||
|
|
||||||
# register the rln-relay peer to the membership contract
|
if onchainMode:
|
||||||
let is_successful = await rlnPeer.register()
|
# register the rln-relay peer to the membership contract
|
||||||
# check whether registration is done
|
let is_successful = await rlnPeer.register()
|
||||||
doAssert(is_successful)
|
# check whether registration is done
|
||||||
debug "peer is successfully registered into the membership contract"
|
doAssert(is_successful)
|
||||||
|
debug "peer is successfully registered into the membership contract"
|
||||||
|
|
||||||
node.wakuRlnRelay = rlnPeer
|
node.wakuRlnRelay = rlnPeer
|
||||||
|
|
||||||
@ -539,6 +549,7 @@ proc startRelay*(node: WakuNode) {.async.} =
|
|||||||
proc mountRelay*(node: WakuNode,
|
proc mountRelay*(node: WakuNode,
|
||||||
topics: seq[string] = newSeq[string](),
|
topics: seq[string] = newSeq[string](),
|
||||||
rlnRelayEnabled = false,
|
rlnRelayEnabled = false,
|
||||||
|
rlnRelayMemIndex = uint(0),
|
||||||
relayMessages = true,
|
relayMessages = true,
|
||||||
triggerSelf = true)
|
triggerSelf = true)
|
||||||
# @TODO: Better error handling: CatchableError is raised by `waitFor`
|
# @TODO: Better error handling: CatchableError is raised by `waitFor`
|
||||||
@ -573,11 +584,42 @@ proc mountRelay*(node: WakuNode,
|
|||||||
|
|
||||||
when defined(rln):
|
when defined(rln):
|
||||||
if rlnRelayEnabled:
|
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"
|
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"
|
info "relay mounted successfully"
|
||||||
|
|
||||||
if node.started:
|
if node.started:
|
||||||
@ -844,7 +886,8 @@ when isMainModule:
|
|||||||
mountRelay(node,
|
mountRelay(node,
|
||||||
conf.topics.split(" "),
|
conf.topics.split(" "),
|
||||||
rlnRelayEnabled = conf.rlnRelay,
|
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
|
# Keepalive mounted on all nodes
|
||||||
mountLibp2pPing(node)
|
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
|
# TODO define a return type of bool for register method to signify a successful registration
|
||||||
proc register(pubkey: Uint256) # external payable
|
proc register(pubkey: Uint256) # external payable
|
||||||
|
|
||||||
proc createRLNInstance*(d: int): RLNResult
|
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||||
{.raises: [Defect, IOError].} =
|
{.raises: [Defect, IOError].} =
|
||||||
|
|
||||||
## generates an instance of RLN
|
## 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)
|
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
|
||||||
return member_is_added
|
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)
|
let deletion_success = delete_member(rlnInstance, index)
|
||||||
return deletion_success
|
return deletion_success
|
||||||
|
|
||||||
@ -130,3 +130,63 @@ proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
|||||||
var rootValue = cast[ptr array[32,byte]] (root.`ptr`)
|
var rootValue = cast[ptr array[32,byte]] (root.`ptr`)
|
||||||
let merkleNode = rootValue[]
|
let merkleNode = rootValue[]
|
||||||
return ok(merkleNode)
|
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…
x
Reference in New Issue
Block a user