mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-08 00:43:06 +00:00
chore|feat (waku-rln-relay): modules reorganization|Initial test for capturing events using nim-web3 (#941)
* first edition * adds the full test scenario * fixes typos * fixes a bug in the supplied command * further edits the description * displays the chat prompt after spam detection * updates changelog * minor wording fix * adds a new test file for onchain rln relay * adds the Event proc * adds one working example of event subscription * defines a new unitt test for event subscription * adds the new test file * cleans up the code * adds a working event subscription for faucet contract * wip * makes faucet test conditional * updates contract byte codes * adds a working test for event subscription and cleans up the tests * fixes case * adss toUInt256 unit function * enables the tests * fixes a bug * undo commented tests * cleans up the test * logs the pk * removes excess entry in the changelog * fixes spacing * comments * removes unused test codes * adds the conditional compilation for onchain tests * uncomments offchain tests * removes onchain tests * reorganizes the code and moves the rln contract data into a separate module * deletes txt files * beautifies the code * beautifies the code * removes an excess line * more formatting fixes * minor fix * updates the case of membership fee const * renames compare to diff * renames time to e * edits the number of arguments of the send proc * fixes a comment alignment * fixes indentation * fixed id style * splits check from condition * fixes a naming mismatch
This commit is contained in:
parent
e635fda9ae
commit
63a9d9e63b
@ -27,6 +27,8 @@ import
|
|||||||
|
|
||||||
when defined(rln):
|
when defined(rln):
|
||||||
import ./v2/test_waku_rln_relay
|
import ./v2/test_waku_rln_relay
|
||||||
|
when defined(onchain_rln):
|
||||||
|
import ./v2/test_waku_rln_relay_onchain
|
||||||
|
|
||||||
|
|
||||||
# TODO Only enable this once swap module is integrated more nicely as a dependency, i.e. as submodule with CI etc
|
# TODO Only enable this once swap module is integrated more nicely as a dependency, i.e. as submodule with CI etc
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -6,7 +6,8 @@ import
|
|||||||
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,
|
||||||
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils, waku_rln_relay_types],
|
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils,
|
||||||
|
waku_rln_relay_types],
|
||||||
../../waku/v2/node/wakunode2,
|
../../waku/v2/node/wakunode2,
|
||||||
../test_helpers,
|
../test_helpers,
|
||||||
./test_utils
|
./test_utils
|
||||||
@ -14,258 +15,7 @@ import
|
|||||||
const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto"
|
const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto"
|
||||||
const RLNRELAY_CONTENT_TOPIC = "waku/2/rlnrelay/proto"
|
const RLNRELAY_CONTENT_TOPIC = "waku/2/rlnrelay/proto"
|
||||||
|
|
||||||
# POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract:
|
|
||||||
# https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol
|
|
||||||
# the solidity contract is compiled separately and the resultant bytecode is copied here
|
|
||||||
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 MEMBERSHIP_CONTRACT_CODE = readFile("tests/v2/membershipContract.txt")
|
|
||||||
|
|
||||||
# the membership contract code in solidity
|
|
||||||
# uint256 public immutable MEMBERSHIP_DEPOSIT;
|
|
||||||
# uint256 public immutable DEPTH;
|
|
||||||
# uint256 public immutable SET_SIZE;
|
|
||||||
# uint256 public pubkeyIndex = 0;
|
|
||||||
# mapping(uint256 => uint256) public members;
|
|
||||||
# IPoseidonHasher public poseidonHasher;
|
|
||||||
|
|
||||||
# event MemberRegistered(uint256 indexed pubkey, uint256 indexed index);
|
|
||||||
# event MemberWithdrawn(uint256 indexed pubkey, uint256 indexed index);
|
|
||||||
|
|
||||||
# constructor(
|
|
||||||
# uint256 membershipDeposit,
|
|
||||||
# uint256 depth,
|
|
||||||
# address _poseidonHasher
|
|
||||||
# ) public {
|
|
||||||
# MEMBERSHIP_DEPOSIT = membershipDeposit;
|
|
||||||
# DEPTH = depth;
|
|
||||||
# SET_SIZE = 1 << depth;
|
|
||||||
# poseidonHasher = IPoseidonHasher(_poseidonHasher);
|
|
||||||
# }
|
|
||||||
|
|
||||||
# function register(uint256 pubkey) external payable {
|
|
||||||
# require(pubkeyIndex < SET_SIZE, "RLN, register: set is full");
|
|
||||||
# require(msg.value == MEMBERSHIP_DEPOSIT, "RLN, register: membership deposit is not satisfied");
|
|
||||||
# _register(pubkey);
|
|
||||||
# }
|
|
||||||
|
|
||||||
# function registerBatch(uint256[] calldata pubkeys) external payable {
|
|
||||||
# require(pubkeyIndex + pubkeys.length <= SET_SIZE, "RLN, registerBatch: set is full");
|
|
||||||
# require(msg.value == MEMBERSHIP_DEPOSIT * pubkeys.length, "RLN, registerBatch: membership deposit is not satisfied");
|
|
||||||
# for (uint256 i = 0; i < pubkeys.length; i++) {
|
|
||||||
# _register(pubkeys[i]);
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# function withdrawBatch(
|
|
||||||
# uint256[] calldata secrets,
|
|
||||||
# uint256[] calldata pubkeyIndexes,
|
|
||||||
# address payable[] calldata receivers
|
|
||||||
# ) external {
|
|
||||||
# uint256 batchSize = secrets.length;
|
|
||||||
# require(batchSize != 0, "RLN, withdrawBatch: batch size zero");
|
|
||||||
# require(batchSize == pubkeyIndexes.length, "RLN, withdrawBatch: batch size mismatch pubkey indexes");
|
|
||||||
# require(batchSize == receivers.length, "RLN, withdrawBatch: batch size mismatch receivers");
|
|
||||||
# for (uint256 i = 0; i < batchSize; i++) {
|
|
||||||
# _withdraw(secrets[i], pubkeyIndexes[i], receivers[i]);
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# function withdraw(
|
|
||||||
# uint256 secret,
|
|
||||||
# uint256 _pubkeyIndex,
|
|
||||||
# address payable receiver
|
|
||||||
# ) external {
|
|
||||||
# _withdraw(secret, _pubkeyIndex, receiver);
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
contract(MembershipContract):
|
|
||||||
proc register(pubkey: Uint256) # external payable
|
|
||||||
# proc registerBatch(pubkeys: seq[Uint256]) # external payable
|
|
||||||
# TODO will add withdraw function after integrating the keyGeneration function (required to compute public keys from secret keys)
|
|
||||||
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
|
|
||||||
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
|
|
||||||
|
|
||||||
proc uploadContract(ethClientAddress: string): Future[Address] {.async.} =
|
|
||||||
let web3 = await newWeb3(ethClientAddress)
|
|
||||||
debug "web3 connected to", ethClientAddress
|
|
||||||
|
|
||||||
# fetch the list of registered accounts
|
|
||||||
let accounts = await web3.provider.eth_accounts()
|
|
||||||
web3.defaultAccount = accounts[1]
|
|
||||||
let add =web3.defaultAccount
|
|
||||||
debug "contract deployer account address ", add
|
|
||||||
|
|
||||||
var balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest")
|
|
||||||
debug "Initial account balance: ", balance
|
|
||||||
|
|
||||||
# deploy the poseidon hash first
|
|
||||||
let
|
|
||||||
hasherReceipt = await web3.deployContract(POSEIDON_HASHER_CODE)
|
|
||||||
hasherAddress = hasherReceipt.contractAddress.get
|
|
||||||
debug "hasher address: ", hasherAddress
|
|
||||||
|
|
||||||
|
|
||||||
# encode membership contract inputs to 32 bytes zero-padded
|
|
||||||
let
|
|
||||||
membershipFeeEncoded = encode(MembershipFee).data
|
|
||||||
depthEncoded = encode(MERKLE_TREE_DEPTH.u256).data
|
|
||||||
hasherAddressEncoded = encode(hasherAddress).data
|
|
||||||
# this is the contract constructor input
|
|
||||||
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
|
|
||||||
|
|
||||||
|
|
||||||
debug "encoded membership fee: ", membershipFeeEncoded
|
|
||||||
debug "encoded depth: ", depthEncoded
|
|
||||||
debug "encoded hasher address: ", hasherAddressEncoded
|
|
||||||
debug "encoded contract input:" , contractInput
|
|
||||||
|
|
||||||
# deploy membership contract with its constructor inputs
|
|
||||||
let receipt = await web3.deployContract(MEMBERSHIP_CONTRACT_CODE, contractInput = contractInput)
|
|
||||||
var contractAddress = receipt.contractAddress.get
|
|
||||||
debug "Address of the deployed membership contract: ", contractAddress
|
|
||||||
|
|
||||||
# balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest")
|
|
||||||
# debug "Account balance after the contract deployment: ", balance
|
|
||||||
|
|
||||||
await web3.close()
|
|
||||||
debug "disconnected from ", ethClientAddress
|
|
||||||
|
|
||||||
return contractAddress
|
|
||||||
|
|
||||||
procSuite "Waku rln relay":
|
procSuite "Waku rln relay":
|
||||||
when defined(onchain_rln):
|
|
||||||
asyncTest "contract membership":
|
|
||||||
debug "ethereum client address", ETH_CLIENT
|
|
||||||
let contractAddress = await uploadContract(ETH_CLIENT)
|
|
||||||
# connect to the eth client
|
|
||||||
let web3 = await newWeb3(ETH_CLIENT)
|
|
||||||
debug "web3 connected to", ETH_CLIENT
|
|
||||||
|
|
||||||
# fetch the list of registered accounts
|
|
||||||
let accounts = await web3.provider.eth_accounts()
|
|
||||||
web3.defaultAccount = accounts[1]
|
|
||||||
let add = web3.defaultAccount
|
|
||||||
debug "contract deployer account address ", add
|
|
||||||
|
|
||||||
# prepare a contract sender to interact with it
|
|
||||||
var sender = web3.contractSender(MembershipContract, contractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
|
||||||
|
|
||||||
# send takes three parameters, c: ContractCallBase, value = 0.u256, gas = 3000000'u64 gasPrice = 0
|
|
||||||
# should use send proc for the contract functions that update the state of the contract
|
|
||||||
let tx = await sender.register(20.u256).send(value = MembershipFee)
|
|
||||||
debug "The hash of registration tx: ", tx # value is the membership fee
|
|
||||||
|
|
||||||
# var members: array[2, uint256] = [20.u256, 21.u256]
|
|
||||||
# debug "This is the batch registration result ", await sender.registerBatch(members).send(value = (members.len * membershipFee)) # value is the membership fee
|
|
||||||
|
|
||||||
# balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest")
|
|
||||||
# debug "Balance after registration: ", balance
|
|
||||||
|
|
||||||
await web3.close()
|
|
||||||
debug "disconnected from", ETH_CLIENT
|
|
||||||
|
|
||||||
asyncTest "registration procedure":
|
|
||||||
# deploy the contract
|
|
||||||
let contractAddress = await uploadContract(ETH_CLIENT)
|
|
||||||
|
|
||||||
# prepare rln-relay peer inputs
|
|
||||||
let
|
|
||||||
web3 = await newWeb3(ETH_CLIENT)
|
|
||||||
accounts = await web3.provider.eth_accounts()
|
|
||||||
# choose one of the existing accounts for the rln-relay peer
|
|
||||||
ethAccountAddress = accounts[9]
|
|
||||||
await web3.close()
|
|
||||||
|
|
||||||
# create an RLN instance
|
|
||||||
var rlnInstance = createRLNInstance()
|
|
||||||
check: rlnInstance.isOk == true
|
|
||||||
|
|
||||||
# generate the membership keys
|
|
||||||
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
|
||||||
|
|
||||||
check: membershipKeyPair.isSome
|
|
||||||
|
|
||||||
# initialize the WakuRLNRelay
|
|
||||||
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
|
||||||
membershipIndex: MembershipIndex(0),
|
|
||||||
ethClientAddress: ETH_CLIENT,
|
|
||||||
ethAccountAddress: ethAccountAddress,
|
|
||||||
membershipContractAddress: contractAddress)
|
|
||||||
|
|
||||||
# register the rln-relay peer to the membership contract
|
|
||||||
let is_successful = await rlnPeer.register()
|
|
||||||
check:
|
|
||||||
is_successful
|
|
||||||
asyncTest "mounting waku rln-relay":
|
|
||||||
let
|
|
||||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
|
||||||
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
|
||||||
Port(60000))
|
|
||||||
await node.start()
|
|
||||||
|
|
||||||
# deploy the contract
|
|
||||||
let membershipContractAddress = await uploadContract(ETH_CLIENT)
|
|
||||||
|
|
||||||
# prepare rln-relay inputs
|
|
||||||
let
|
|
||||||
web3 = await newWeb3(ETH_CLIENT)
|
|
||||||
accounts = await web3.provider.eth_accounts()
|
|
||||||
# choose one of the existing account for the rln-relay peer
|
|
||||||
ethAccountAddress = accounts[9]
|
|
||||||
await web3.close()
|
|
||||||
|
|
||||||
# create current peer's pk
|
|
||||||
var rlnInstance = createRLNInstance()
|
|
||||||
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
|
|
||||||
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
|
|
||||||
await node.mountRlnRelay(ethClientAddrOpt = some(EthClient),
|
|
||||||
ethAccAddrOpt = some(ethAccountAddress),
|
|
||||||
memContractAddOpt = some(membershipContractAddress),
|
|
||||||
groupOpt = some(group),
|
|
||||||
memKeyPairOpt = some(keypair.get()),
|
|
||||||
memIndexOpt = some(index),
|
|
||||||
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
|
|
||||||
contentTopic = RLNRELAY_CONTENT_TOPIC)
|
|
||||||
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
|
|
||||||
debug "calculated root ", calculatedRoot
|
|
||||||
|
|
||||||
check expectedRoot == calculatedRoot
|
|
||||||
|
|
||||||
await node.stop()
|
|
||||||
|
|
||||||
asyncTest "mount waku-rln-relay in the off-chain mode":
|
asyncTest "mount waku-rln-relay in the off-chain mode":
|
||||||
let
|
let
|
||||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||||
@ -278,69 +28,72 @@ procSuite "Waku rln relay":
|
|||||||
# create a group of 100 membership keys
|
# create a group of 100 membership keys
|
||||||
let
|
let
|
||||||
(groupKeys, root) = createMembershipList(100)
|
(groupKeys, root) = createMembershipList(100)
|
||||||
check groupKeys.len == 100
|
check:
|
||||||
let
|
groupKeys.len == 100
|
||||||
|
let
|
||||||
# convert the keys to MembershipKeyPair structs
|
# convert the keys to MembershipKeyPair structs
|
||||||
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
||||||
# extract the id commitments
|
# extract the id commitments
|
||||||
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
||||||
debug "groupKeyPairs", groupKeyPairs
|
debug "groupKeyPairs", groupKeyPairs
|
||||||
debug "groupIDCommitments", groupIDCommitments
|
debug "groupIDCommitments", groupIDCommitments
|
||||||
|
|
||||||
# index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs
|
# 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
|
# 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
|
# index also represents the index of the leaf in the Merkle tree that contains node's commitment key
|
||||||
let index = MembershipIndex(5)
|
let index = MembershipIndex(5)
|
||||||
|
|
||||||
# -------- mount rln-relay in the off-chain mode
|
# -------- mount rln-relay in the off-chain mode
|
||||||
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
|
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
|
||||||
await node.mountRlnRelay(groupOpt = some(groupIDCommitments),
|
await node.mountRlnRelay(groupOpt = some(groupIDCommitments),
|
||||||
memKeyPairOpt = some(groupKeyPairs[index]),
|
memKeyPairOpt = some(groupKeyPairs[index]),
|
||||||
memIndexOpt = some(index),
|
memIndexOpt = some(index),
|
||||||
onchainMode = false,
|
onchainMode = false,
|
||||||
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
|
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
|
||||||
contentTopic = RLNRELAY_CONTENT_TOPIC)
|
contentTopic = RLNRELAY_CONTENT_TOPIC)
|
||||||
|
|
||||||
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc
|
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc
|
||||||
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
|
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
|
||||||
debug "calculated root by mountRlnRelay", calculatedRoot
|
debug "calculated root by mountRlnRelay", calculatedRoot
|
||||||
|
|
||||||
# this part checks whether the Merkle tree is constructed correctly inside the mountRlnRelay proc
|
# 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
|
# this check is done by comparing the tree root resulted from mountRlnRelay i.e., calculatedRoot
|
||||||
# against the root which is the expected root
|
# against the root which is the expected root
|
||||||
check calculatedRoot == root
|
check:
|
||||||
|
calculatedRoot == root
|
||||||
|
|
||||||
await node.stop()
|
await node.stop()
|
||||||
|
|
||||||
suite "Waku rln relay":
|
suite "Waku rln relay":
|
||||||
test "key_gen Nim Wrappers":
|
test "key_gen Nim Wrappers":
|
||||||
var
|
var
|
||||||
merkleDepth: csize_t = 32
|
merkleDepth: csize_t = 32
|
||||||
# parameters.key contains the parameters related to the Poseidon hasher
|
# parameters.key contains the parameters related to the Poseidon hasher
|
||||||
# to generate this file, clone this repo https://github.com/kilic/rln
|
# to generate this file, clone this repo https://github.com/kilic/rln
|
||||||
# and run the following command in the root directory of the cloned project
|
# and run the following command in the root directory of the cloned project
|
||||||
# cargo run --example export_test_keys
|
# cargo run --example export_test_keys
|
||||||
# the file is generated separately and copied here
|
# the file is generated separately and copied here
|
||||||
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
||||||
pbytes = parameters.toBytes()
|
pbytes = parameters.toBytes()
|
||||||
len : csize_t = uint(pbytes.len)
|
len: csize_t = uint(pbytes.len)
|
||||||
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
||||||
check:
|
check:
|
||||||
# check the parameters.key is not empty
|
# check the parameters.key is not empty
|
||||||
pbytes.len != 0
|
pbytes.len != 0
|
||||||
|
|
||||||
var
|
var
|
||||||
rlnInstance: RLN[Bn256]
|
rlnInstance: RLN[Bn256]
|
||||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer, addr rlnInstance)
|
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||||
|
addr rlnInstance)
|
||||||
check:
|
check:
|
||||||
# check whether the circuit parameters are generated successfully
|
# check whether the circuit parameters are generated successfully
|
||||||
res == true
|
res == true
|
||||||
|
|
||||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
var
|
var
|
||||||
keysBuffer : Buffer
|
keysBuffer: Buffer
|
||||||
keysBufferPtr = addr(keysBuffer)
|
keysBufferPtr = addr(keysBuffer)
|
||||||
done = key_gen(rlnInstance, keysBufferPtr)
|
done = key_gen(rlnInstance, keysBufferPtr)
|
||||||
check:
|
check:
|
||||||
# check whether the keys are generated successfully
|
# check whether the keys are generated successfully
|
||||||
done == true
|
done == true
|
||||||
@ -350,8 +103,8 @@ suite "Waku rln relay":
|
|||||||
check:
|
check:
|
||||||
# the public and secret keys together are 64 bytes
|
# the public and secret keys together are 64 bytes
|
||||||
generatedKeys.len == 64
|
generatedKeys.len == 64
|
||||||
debug "generated keys: ", generatedKeys
|
debug "generated keys: ", generatedKeys
|
||||||
|
|
||||||
test "membership Key Gen":
|
test "membership Key Gen":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
@ -359,15 +112,15 @@ suite "Waku rln relay":
|
|||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
var key = membershipKeyGen(rlnInstance.value)
|
var key = membershipKeyGen(rlnInstance.value)
|
||||||
var empty : array[32,byte]
|
var empty: array[32, byte]
|
||||||
check:
|
check:
|
||||||
key.isSome
|
key.isSome
|
||||||
key.get().idKey.len == 32
|
key.get().idKey.len == 32
|
||||||
key.get().idCommitment.len == 32
|
key.get().idCommitment.len == 32
|
||||||
key.get().idKey != empty
|
key.get().idKey != empty
|
||||||
key.get().idCommitment != empty
|
key.get().idCommitment != empty
|
||||||
|
|
||||||
debug "the generated membership key pair: ", key
|
debug "the generated membership key pair: ", key
|
||||||
|
|
||||||
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
|
||||||
@ -376,8 +129,8 @@ suite "Waku rln relay":
|
|||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var
|
var
|
||||||
root1 {.noinit.} : Buffer = Buffer()
|
root1 {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr1 = addr(root1)
|
rootPtr1 = addr(root1)
|
||||||
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
||||||
check:
|
check:
|
||||||
@ -385,22 +138,23 @@ suite "Waku rln relay":
|
|||||||
root1.len == 32
|
root1.len == 32
|
||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var
|
var
|
||||||
root2 {.noinit.} : Buffer = Buffer()
|
root2 {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr2 = addr(root2)
|
rootPtr2 = addr(root2)
|
||||||
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
||||||
check:
|
check:
|
||||||
get_root_successful2
|
get_root_successful2
|
||||||
root2.len == 32
|
root2.len == 32
|
||||||
|
|
||||||
var rootValue1 = cast[ptr array[32,byte]] (root1.`ptr`)
|
var rootValue1 = cast[ptr array[32, byte]] (root1.`ptr`)
|
||||||
let rootHex1 = rootValue1[].toHex
|
let rootHex1 = rootValue1[].toHex
|
||||||
|
|
||||||
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
|
# the two roots must be identical
|
||||||
check rootHex1 == rootHex2
|
check:
|
||||||
|
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()
|
var rlnInstance = createRLNInstance()
|
||||||
@ -409,16 +163,19 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var root1 = getMerkleRoot(rlnInstance.value())
|
var root1 = getMerkleRoot(rlnInstance.value())
|
||||||
check root1.isOk
|
check:
|
||||||
|
root1.isOk
|
||||||
let rootHex1 = root1.value().toHex
|
let rootHex1 = root1.value().toHex
|
||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var root2 = getMerkleRoot(rlnInstance.value())
|
var root2 = getMerkleRoot(rlnInstance.value())
|
||||||
check root2.isOk
|
check:
|
||||||
|
root2.isOk
|
||||||
let rootHex2 = root2.value().toHex
|
let rootHex2 = root2.value().toHex
|
||||||
|
|
||||||
# the two roots must be identical
|
# the two roots must be identical
|
||||||
check rootHex1 == rootHex2
|
check:
|
||||||
|
rootHex1 == rootHex2
|
||||||
|
|
||||||
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
|
||||||
@ -428,7 +185,8 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
var keypair = membershipKeyGen(rlnInstance.value)
|
var keypair = membershipKeyGen(rlnInstance.value)
|
||||||
check keypair.isSome()
|
check:
|
||||||
|
keypair.isSome()
|
||||||
var pkBuffer = toBuffer(keypair.get().idCommitment)
|
var pkBuffer = toBuffer(keypair.get().idCommitment)
|
||||||
let pkBufferPtr = addr pkBuffer
|
let pkBufferPtr = addr pkBuffer
|
||||||
|
|
||||||
@ -436,17 +194,18 @@ suite "Waku rln relay":
|
|||||||
var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr)
|
var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr)
|
||||||
check:
|
check:
|
||||||
member_is_added == true
|
member_is_added == true
|
||||||
|
|
||||||
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()
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
# delete the first member
|
# delete the first member
|
||||||
var deleted_member_index = MembershipIndex(0)
|
var deleted_member_index = MembershipIndex(0)
|
||||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||||
check deletion_success
|
check:
|
||||||
|
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
|
||||||
@ -456,17 +215,18 @@ suite "Waku rln relay":
|
|||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
var keypair = rln.membershipKeyGen()
|
var keypair = rln.membershipKeyGen()
|
||||||
check keypair.isSome()
|
|
||||||
check:
|
check:
|
||||||
rln.insertMember(keypair.get().idCommitment)
|
keypair.isSome()
|
||||||
|
check:
|
||||||
|
rln.insertMember(keypair.get().idCommitment)
|
||||||
|
|
||||||
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()
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
check:
|
check:
|
||||||
rln.removeMember(MembershipIndex(0))
|
rln.removeMember(MembershipIndex(0))
|
||||||
|
|
||||||
test "Merkle tree consistency check between deletion and insertion":
|
test "Merkle tree consistency check between deletion and insertion":
|
||||||
@ -476,14 +236,14 @@ suite "Waku rln relay":
|
|||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var
|
var
|
||||||
root1 {.noinit.} : Buffer = Buffer()
|
root1 {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr1 = addr(root1)
|
rootPtr1 = addr(root1)
|
||||||
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
||||||
check:
|
check:
|
||||||
get_root_successful1
|
get_root_successful1
|
||||||
root1.len == 32
|
root1.len == 32
|
||||||
|
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
var keypair = membershipKeyGen(rlnInstance.value)
|
var keypair = membershipKeyGen(rlnInstance.value)
|
||||||
check: keypair.isSome()
|
check: keypair.isSome()
|
||||||
@ -492,49 +252,52 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
# add the member to the tree
|
# add the member to the tree
|
||||||
var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr)
|
var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr)
|
||||||
check member_is_added
|
check:
|
||||||
|
member_is_added
|
||||||
|
|
||||||
# read the Merkle Tree root after insertion
|
# read the Merkle Tree root after insertion
|
||||||
var
|
var
|
||||||
root2 {.noinit.} : Buffer = Buffer()
|
root2 {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr2 = addr(root2)
|
rootPtr2 = addr(root2)
|
||||||
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
||||||
check:
|
check:
|
||||||
get_root_successful2
|
get_root_successful2
|
||||||
root2.len == 32
|
root2.len == 32
|
||||||
|
|
||||||
# delete the first member
|
# delete the first member
|
||||||
var deleted_member_index = MembershipIndex(0)
|
var deleted_member_index = MembershipIndex(0)
|
||||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||||
check deletion_success
|
check:
|
||||||
|
deletion_success
|
||||||
|
|
||||||
# read the Merkle Tree root after the deletion
|
# read the Merkle Tree root after the deletion
|
||||||
var
|
var
|
||||||
root3 {.noinit.} : Buffer = Buffer()
|
root3 {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr3 = addr(root3)
|
rootPtr3 = addr(root3)
|
||||||
get_root_successful3 = get_root(rlnInstance.value, rootPtr3)
|
get_root_successful3 = get_root(rlnInstance.value, rootPtr3)
|
||||||
check:
|
check:
|
||||||
get_root_successful3
|
get_root_successful3
|
||||||
root3.len == 32
|
root3.len == 32
|
||||||
|
|
||||||
var rootValue1 = cast[ptr array[32,byte]] (root1.`ptr`)
|
var rootValue1 = cast[ptr array[32, byte]] (root1.`ptr`)
|
||||||
let rootHex1 = rootValue1[].toHex
|
let rootHex1 = rootValue1[].toHex
|
||||||
debug "The initial root", rootHex1
|
debug "The initial root", rootHex1
|
||||||
|
|
||||||
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
|
||||||
debug "The root after insertion", rootHex2
|
debug "The root after insertion", rootHex2
|
||||||
|
|
||||||
var rootValue3 = cast[ptr array[32,byte]] (root3.`ptr`)
|
var rootValue3 = cast[ptr array[32, byte]] (root3.`ptr`)
|
||||||
let rootHex3 = rootValue3[].toHex
|
let rootHex3 = rootValue3[].toHex
|
||||||
debug "The root after deletion", rootHex3
|
debug "The root after deletion", rootHex3
|
||||||
|
|
||||||
# the root must change after the insertion
|
# the root must change after the insertion
|
||||||
check: not(rootHex1 == rootHex2)
|
check: not(rootHex1 == rootHex2)
|
||||||
|
|
||||||
## 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
|
||||||
check rootHex1 == rootHex3
|
check:
|
||||||
|
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()
|
var rlnInstance = createRLNInstance()
|
||||||
@ -544,29 +307,35 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
# read the Merkle Tree root
|
# read the Merkle Tree root
|
||||||
var root1 = rln.getMerkleRoot()
|
var root1 = rln.getMerkleRoot()
|
||||||
check root1.isOk
|
check:
|
||||||
|
root1.isOk
|
||||||
let rootHex1 = root1.value().toHex()
|
let rootHex1 = root1.value().toHex()
|
||||||
|
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
var keypair = rln.membershipKeyGen()
|
var keypair = rln.membershipKeyGen()
|
||||||
check keypair.isSome()
|
check:
|
||||||
let member_inserted = rln.insertMember(keypair.get().idCommitment)
|
keypair.isSome()
|
||||||
check member_inserted
|
let member_inserted = rln.insertMember(keypair.get().idCommitment)
|
||||||
|
check:
|
||||||
|
member_inserted
|
||||||
|
|
||||||
# read the Merkle Tree root after insertion
|
# read the Merkle Tree root after insertion
|
||||||
var root2 = rln.getMerkleRoot()
|
var root2 = rln.getMerkleRoot()
|
||||||
check root2.isOk
|
check:
|
||||||
|
root2.isOk
|
||||||
let rootHex2 = root2.value().toHex()
|
let rootHex2 = root2.value().toHex()
|
||||||
|
|
||||||
|
|
||||||
# delete the first member
|
# delete the first member
|
||||||
var deleted_member_index = MembershipIndex(0)
|
var deleted_member_index = MembershipIndex(0)
|
||||||
let deletion_success = rln.removeMember(deleted_member_index)
|
let deletion_success = rln.removeMember(deleted_member_index)
|
||||||
check deletion_success
|
check:
|
||||||
|
deletion_success
|
||||||
|
|
||||||
# read the Merkle Tree root after the deletion
|
# read the Merkle Tree root after the deletion
|
||||||
var root3 = rln.getMerkleRoot()
|
var root3 = rln.getMerkleRoot()
|
||||||
check root3.isOk
|
check:
|
||||||
|
root3.isOk
|
||||||
let rootHex3 = root3.value().toHex()
|
let rootHex3 = root3.value().toHex()
|
||||||
|
|
||||||
|
|
||||||
@ -575,35 +344,40 @@ suite "Waku rln relay":
|
|||||||
debug "The root after deletion", rootHex3
|
debug "The root after deletion", rootHex3
|
||||||
|
|
||||||
# the root must change after the insertion
|
# the root must change after the insertion
|
||||||
check not(rootHex1 == rootHex2)
|
check:
|
||||||
|
not(rootHex1 == rootHex2)
|
||||||
|
|
||||||
## 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
|
||||||
check rootHex1 == rootHex3
|
check:
|
||||||
|
rootHex1 == rootHex3
|
||||||
|
|
||||||
test "hash Nim Wrappers":
|
test "hash Nim Wrappers":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
# prepare the input
|
# prepare the input
|
||||||
var
|
var
|
||||||
msg = "Hello".toBytes()
|
msg = "Hello".toBytes()
|
||||||
hashInput = appendLength(msg)
|
hashInput = appendLength(msg)
|
||||||
hashInputBuffer = toBuffer(hashInput)
|
hashInputBuffer = toBuffer(hashInput)
|
||||||
|
|
||||||
# prepare other inputs to the hash function
|
# prepare other inputs to the hash function
|
||||||
var outputBuffer: Buffer
|
var outputBuffer: Buffer
|
||||||
|
|
||||||
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer, addr outputBuffer)
|
|
||||||
check hashSuccess
|
|
||||||
let outputArr = cast[ptr array[32,byte]](outputBuffer.`ptr`)[]
|
|
||||||
check:
|
|
||||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == outputArr.toHex()
|
|
||||||
|
|
||||||
var
|
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer,
|
||||||
hashOutput = cast[ptr array[32,byte]] (outputBuffer.`ptr`)[]
|
addr outputBuffer)
|
||||||
|
check:
|
||||||
|
hashSuccess
|
||||||
|
let outputArr = cast[ptr array[32, byte]](outputBuffer.`ptr`)[]
|
||||||
|
check:
|
||||||
|
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||||
|
outputArr.toHex()
|
||||||
|
|
||||||
|
var
|
||||||
|
hashOutput = cast[ptr array[32, byte]] (outputBuffer.`ptr`)[]
|
||||||
hashOutputHex = hashOutput.toHex()
|
hashOutputHex = hashOutput.toHex()
|
||||||
|
|
||||||
debug "hash output", hashOutputHex
|
debug "hash output", hashOutputHex
|
||||||
@ -614,26 +388,27 @@ suite "Waku rln relay":
|
|||||||
check:
|
check:
|
||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
let rln = rlnInstance.value
|
let rln = rlnInstance.value
|
||||||
|
|
||||||
# prepare the input
|
# prepare the input
|
||||||
let msg = "Hello".toBytes()
|
let msg = "Hello".toBytes()
|
||||||
|
|
||||||
let hash = rln.hash(msg)
|
let hash = rln.hash(msg)
|
||||||
check:
|
check:
|
||||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == hash.toHex()
|
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||||
|
hash.toHex()
|
||||||
|
|
||||||
test "create a list of membership keys and construct a Merkle tree based on the list":
|
test "create a list of membership keys and construct a Merkle tree based on the list":
|
||||||
let
|
let
|
||||||
groupSize = 100
|
groupSize = 100
|
||||||
(list, root) = createMembershipList(groupSize)
|
(list, root) = createMembershipList(groupSize)
|
||||||
|
|
||||||
debug "created membership key list", list
|
debug "created membership key list", list
|
||||||
debug "the Merkle tree root", root
|
debug "the Merkle tree root", root
|
||||||
|
|
||||||
check:
|
check:
|
||||||
list.len == groupSize # check the number of keys
|
list.len == groupSize # check the number of keys
|
||||||
root.len == HASH_HEX_SIZE # check the size of the calculated tree root
|
root.len == HASH_HEX_SIZE # check the size of the calculated tree root
|
||||||
|
|
||||||
test "check correctness of toMembershipKeyPairs and calcMerkleRoot":
|
test "check correctness of toMembershipKeyPairs and calcMerkleRoot":
|
||||||
let groupKeys = STATIC_GROUP_KEYS
|
let groupKeys = STATIC_GROUP_KEYS
|
||||||
|
|
||||||
@ -648,14 +423,14 @@ suite "Waku rln relay":
|
|||||||
debug "groupIDCommitments", groupIDCommitments
|
debug "groupIDCommitments", groupIDCommitments
|
||||||
debug "root", root
|
debug "root", root
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# check that the correct number of key pairs is created
|
# check that the correct number of key pairs is created
|
||||||
groupKeyPairs.len == StaticGroupSize
|
groupKeyPairs.len == StaticGroupSize
|
||||||
# compare the calculated root against the correct root
|
# compare the calculated root against the correct root
|
||||||
root == STATIC_GROUP_MERKLE_ROOT
|
root == STATIC_GROUP_MERKLE_ROOT
|
||||||
|
|
||||||
test "RateLimitProof Protobuf encode/init test":
|
test "RateLimitProof Protobuf encode/init test":
|
||||||
var
|
var
|
||||||
proof: ZKSNARK
|
proof: ZKSNARK
|
||||||
merkleRoot: MerkleNode
|
merkleRoot: MerkleNode
|
||||||
epoch: Epoch
|
epoch: Epoch
|
||||||
@ -663,14 +438,14 @@ suite "Waku rln relay":
|
|||||||
shareY: MerkleNode
|
shareY: MerkleNode
|
||||||
nullifier: Nullifier
|
nullifier: Nullifier
|
||||||
# populate fields with dummy values
|
# populate fields with dummy values
|
||||||
for x in proof.mitems : x = 1
|
for x in proof.mitems: x = 1
|
||||||
for x in merkleRoot.mitems : x = 2
|
for x in merkleRoot.mitems: x = 2
|
||||||
for x in epoch.mitems : x = 3
|
for x in epoch.mitems: x = 3
|
||||||
for x in shareX.mitems : x = 4
|
for x in shareX.mitems: x = 4
|
||||||
for x in shareY.mitems : x = 5
|
for x in shareY.mitems: x = 5
|
||||||
for x in nullifier.mitems : x = 6
|
for x in nullifier.mitems: x = 6
|
||||||
|
|
||||||
let
|
let
|
||||||
rateLimitProof = RateLimitProof(proof: proof,
|
rateLimitProof = RateLimitProof(proof: proof,
|
||||||
merkleRoot: merkleRoot,
|
merkleRoot: merkleRoot,
|
||||||
epoch: epoch,
|
epoch: epoch,
|
||||||
@ -686,16 +461,17 @@ suite "Waku rln relay":
|
|||||||
|
|
||||||
test "test proofVerify and proofGen for a valid proof":
|
test "test proofVerify and proofGen for a valid proof":
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
check rlnInstance.isOk
|
check:
|
||||||
|
rlnInstance.isOk
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
|
|
||||||
let
|
let
|
||||||
# create a membership key pair
|
# create a membership key pair
|
||||||
memKeys = membershipKeyGen(rln).get()
|
memKeys = membershipKeyGen(rln).get()
|
||||||
# peer's index in the Merkle Tree
|
# peer's index in the Merkle Tree
|
||||||
index = 5
|
index = 5
|
||||||
|
|
||||||
# Create a Merkle tree with random members
|
# Create a Merkle tree with random members
|
||||||
for i in 0..10:
|
for i in 0..10:
|
||||||
var member_is_added: bool = false
|
var member_is_added: bool = false
|
||||||
if (i == index):
|
if (i == index):
|
||||||
@ -706,27 +482,30 @@ suite "Waku rln relay":
|
|||||||
let memberKeys = rln.membershipKeyGen()
|
let memberKeys = rln.membershipKeyGen()
|
||||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||||
# check the member is added
|
# check the member is added
|
||||||
check member_is_added
|
check:
|
||||||
|
member_is_added
|
||||||
|
|
||||||
# prepare the message
|
# prepare the message
|
||||||
let messageBytes = "Hello".toBytes()
|
let messageBytes = "Hello".toBytes()
|
||||||
|
|
||||||
# prepare the epoch
|
# prepare the epoch
|
||||||
var epoch : Epoch
|
var epoch: Epoch
|
||||||
debug "epoch", epochHex=epoch.toHex()
|
debug "epoch", epochHex = epoch.toHex()
|
||||||
|
|
||||||
# generate proof
|
# generate proof
|
||||||
let proofRes = rln.proofGen(data = messageBytes,
|
let proofRes = rln.proofGen(data = messageBytes,
|
||||||
memKeys = memKeys,
|
memKeys = memKeys,
|
||||||
memIndex = MembershipIndex(index),
|
memIndex = MembershipIndex(index),
|
||||||
epoch = epoch)
|
epoch = epoch)
|
||||||
check proofRes.isOk()
|
check:
|
||||||
|
proofRes.isOk()
|
||||||
let proof = proofRes.value
|
let proof = proofRes.value
|
||||||
|
|
||||||
# verify the proof
|
# verify the proof
|
||||||
let verified = rln.proofVerify(data = messageBytes,
|
let verified = rln.proofVerify(data = messageBytes,
|
||||||
proof = proof)
|
proof = proof)
|
||||||
check verified == true
|
check:
|
||||||
|
verified == true
|
||||||
|
|
||||||
test "test proofVerify and proofGen for an invalid proof":
|
test "test proofVerify and proofGen for an invalid proof":
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
@ -734,13 +513,13 @@ suite "Waku rln relay":
|
|||||||
rlnInstance.isOk == true
|
rlnInstance.isOk == true
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
|
|
||||||
let
|
let
|
||||||
# create a membership key pair
|
# create a membership key pair
|
||||||
memKeys = membershipKeyGen(rln).get()
|
memKeys = membershipKeyGen(rln).get()
|
||||||
# peer's index in the Merkle Tree
|
# peer's index in the Merkle Tree
|
||||||
index = 5
|
index = 5
|
||||||
|
|
||||||
# Create a Merkle tree with random members
|
# Create a Merkle tree with random members
|
||||||
for i in 0..10:
|
for i in 0..10:
|
||||||
var member_is_added: bool = false
|
var member_is_added: bool = false
|
||||||
if (i == index):
|
if (i == index):
|
||||||
@ -751,50 +530,56 @@ suite "Waku rln relay":
|
|||||||
let memberKeys = rln.membershipKeyGen()
|
let memberKeys = rln.membershipKeyGen()
|
||||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||||
# check the member is added
|
# check the member is added
|
||||||
check member_is_added
|
check:
|
||||||
|
member_is_added
|
||||||
|
|
||||||
# prepare the message
|
# prepare the message
|
||||||
let messageBytes = "Hello".toBytes()
|
let messageBytes = "Hello".toBytes()
|
||||||
|
|
||||||
# prepare the epoch
|
# prepare the epoch
|
||||||
var epoch : Epoch
|
var epoch: Epoch
|
||||||
debug "epoch in bytes", epochHex=epoch.toHex()
|
debug "epoch in bytes", epochHex = epoch.toHex()
|
||||||
|
|
||||||
|
|
||||||
let badIndex = 4
|
let badIndex = 4
|
||||||
# generate proof
|
# generate proof
|
||||||
let proofRes = rln.proofGen(data = messageBytes,
|
let proofRes = rln.proofGen(data = messageBytes,
|
||||||
memKeys = memKeys,
|
memKeys = memKeys,
|
||||||
memIndex = MembershipIndex(badIndex),
|
memIndex = MembershipIndex(badIndex),
|
||||||
epoch = epoch)
|
epoch = epoch)
|
||||||
check proofRes.isOk()
|
check:
|
||||||
|
proofRes.isOk()
|
||||||
let proof = proofRes.value
|
let proof = proofRes.value
|
||||||
|
|
||||||
# verify the proof (should not be verified)
|
# verify the proof (should not be verified)
|
||||||
let verified = rln.proofVerify(data = messageBytes,
|
let verified = rln.proofVerify(data = messageBytes,
|
||||||
proof = proof)
|
proof = proof)
|
||||||
check verified == false
|
check:
|
||||||
|
verified == false
|
||||||
test "toEpoch and fromEpoch consistency check":
|
test "toEpoch and fromEpoch consistency check":
|
||||||
# check edge cases
|
# check edge cases
|
||||||
let
|
let
|
||||||
time = uint64.high
|
epoch = uint64.high # rln epoch
|
||||||
epoch = time.toEpoch()
|
epochBytes = epoch.toEpoch()
|
||||||
decodedTime = epoch.fromEpoch()
|
decodedEpoch = epochBytes.fromEpoch()
|
||||||
check time == decodedTime
|
check:
|
||||||
debug "encoded and decode time", time=time, epoch=epoch, decodedTime=decodedTime
|
epoch == decodedEpoch
|
||||||
|
debug "encoded and decode time", epoch = epoch, epochBytes = epochBytes,
|
||||||
|
decodedEpoch = decodedEpoch
|
||||||
|
|
||||||
test "Epoch comparison":
|
test "Epoch comparison":
|
||||||
# check edge cases
|
# check edge cases
|
||||||
let
|
let
|
||||||
time1 = uint64.high
|
time1 = uint64.high
|
||||||
time2 = uint64.high - 1
|
time2 = uint64.high - 1
|
||||||
epoch1 = time1.toEpoch()
|
epoch1 = time1.toEpoch()
|
||||||
epoch2 = time2.toEpoch()
|
epoch2 = time2.toEpoch()
|
||||||
check compare(epoch1, epoch2) == int64(1)
|
check:
|
||||||
check compare(epoch2, epoch1) == int64(-1)
|
diff(epoch1, epoch2) == int64(1)
|
||||||
|
diff(epoch2, epoch1) == int64(-1)
|
||||||
|
|
||||||
test "updateLog and hasDuplicate tests":
|
test "updateLog and hasDuplicate tests":
|
||||||
let
|
let
|
||||||
wakurlnrelay = WakuRLNRelay()
|
wakurlnrelay = WakuRLNRelay()
|
||||||
epoch = getCurrentEpoch()
|
epoch = getCurrentEpoch()
|
||||||
|
|
||||||
@ -816,10 +601,13 @@ suite "Waku rln relay":
|
|||||||
for index, x in shareX3.mpairs: shareX3[index] = 3
|
for index, x in shareX3.mpairs: shareX3[index] = 3
|
||||||
let shareY3 = shareX3
|
let shareY3 = shareX3
|
||||||
|
|
||||||
let
|
let
|
||||||
wm1 = WakuMessage(proof: RateLimitProof(epoch: epoch, nullifier: nullifier1, shareX: shareX1, shareY: shareY1))
|
wm1 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
||||||
wm2 = WakuMessage(proof: RateLimitProof(epoch: epoch, nullifier: nullifier2, shareX: shareX2, shareY: shareY2))
|
nullifier: nullifier1, shareX: shareX1, shareY: shareY1))
|
||||||
wm3 = WakuMessage(proof: RateLimitProof(epoch: epoch, nullifier: nullifier3, shareX: shareX3, shareY: shareY3))
|
wm2 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
||||||
|
nullifier: nullifier2, shareX: shareX2, shareY: shareY2))
|
||||||
|
wm3 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
||||||
|
nullifier: nullifier3, shareX: shareX3, shareY: shareY3))
|
||||||
|
|
||||||
# check whether hasDuplicate correctly finds records with the same nullifiers but different secret shares
|
# check whether hasDuplicate correctly finds records with the same nullifiers but different secret shares
|
||||||
# no duplicate for wm1 should be found, since the log is empty
|
# no duplicate for wm1 should be found, since the log is empty
|
||||||
@ -843,7 +631,7 @@ suite "Waku rln relay":
|
|||||||
# wm3 has the same nullifier as wm1 but different secret shares, it should be detected as duplicate
|
# wm3 has the same nullifier as wm1 but different secret shares, it should be detected as duplicate
|
||||||
let result3 = wakurlnrelay.hasDuplicate(wm3)
|
let result3 = wakurlnrelay.hasDuplicate(wm3)
|
||||||
check:
|
check:
|
||||||
result3.isOk
|
result3.isOk
|
||||||
# it is a duplicate
|
# it is a duplicate
|
||||||
result3.value == true
|
result3.value == true
|
||||||
|
|
||||||
@ -859,10 +647,10 @@ suite "Waku rln relay":
|
|||||||
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
||||||
debug "groupKeyPairs", groupKeyPairs
|
debug "groupKeyPairs", groupKeyPairs
|
||||||
debug "groupIDCommitments", groupIDCommitments
|
debug "groupIDCommitments", groupIDCommitments
|
||||||
|
|
||||||
# index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs
|
# 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
|
# 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
|
# index also represents the index of the leaf in the Merkle tree that contains node's commitment key
|
||||||
let index = MembershipIndex(5)
|
let index = MembershipIndex(5)
|
||||||
|
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
@ -873,24 +661,25 @@ suite "Waku rln relay":
|
|||||||
# add members
|
# add members
|
||||||
discard rln.addAll(groupIDCommitments)
|
discard rln.addAll(groupIDCommitments)
|
||||||
|
|
||||||
let
|
let
|
||||||
wakuRlnRelay = WakuRLNRelay(membershipIndex: index, membershipKeyPair: groupKeyPairs[index], rlnInstance: rln)
|
wakuRlnRelay = WakuRLNRelay(membershipIndex: index,
|
||||||
|
membershipKeyPair: groupKeyPairs[index], rlnInstance: rln)
|
||||||
|
|
||||||
# get the current epoch time
|
# get the current epoch time
|
||||||
let time = epochTime()
|
let time = epochTime()
|
||||||
|
|
||||||
# create some messages from the same peer and append rln proof to them, except wm4
|
# create some messages from the same peer and append rln proof to them, except wm4
|
||||||
var
|
var
|
||||||
wm1 = WakuMessage(payload: "Valid message".toBytes())
|
wm1 = WakuMessage(payload: "Valid message".toBytes())
|
||||||
proofAdded1 = wakuRlnRelay.appendRLNProof(wm1, time)
|
proofAdded1 = wakuRlnRelay.appendRLNProof(wm1, time)
|
||||||
# another message in the same epoch as wm1, it will break the messaging rate limit
|
# another message in the same epoch as wm1, it will break the messaging rate limit
|
||||||
wm2 = WakuMessage(payload: "Spam".toBytes())
|
wm2 = WakuMessage(payload: "Spam".toBytes())
|
||||||
proofAdded2 = wakuRlnRelay.appendRLNProof(wm2, time)
|
proofAdded2 = wakuRlnRelay.appendRLNProof(wm2, time)
|
||||||
# wm3 points to the next epoch
|
# wm3 points to the next epoch
|
||||||
wm3 = WakuMessage(payload: "Valid message".toBytes())
|
wm3 = WakuMessage(payload: "Valid message".toBytes())
|
||||||
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
||||||
wm4 = WakuMessage(payload: "Invalid message".toBytes())
|
wm4 = WakuMessage(payload: "Invalid message".toBytes())
|
||||||
|
|
||||||
# checks proofs are added
|
# checks proofs are added
|
||||||
check:
|
check:
|
||||||
proofAdded1
|
proofAdded1
|
||||||
@ -903,15 +692,15 @@ suite "Waku rln relay":
|
|||||||
msgValidate1 = wakuRlnRelay.validateMessage(wm1, some(time))
|
msgValidate1 = wakuRlnRelay.validateMessage(wm1, some(time))
|
||||||
# wm2 is published within the same Epoch as wm1 and should be found as spam
|
# wm2 is published within the same Epoch as wm1 and should be found as spam
|
||||||
msgValidate2 = wakuRlnRelay.validateMessage(wm2, some(time))
|
msgValidate2 = wakuRlnRelay.validateMessage(wm2, some(time))
|
||||||
# a valid message should be validated successfully
|
# a valid message should be validated successfully
|
||||||
msgValidate3 = wakuRlnRelay.validateMessage(wm3, some(time))
|
msgValidate3 = wakuRlnRelay.validateMessage(wm3, some(time))
|
||||||
# wm4 has no rln proof and should not be validated
|
# wm4 has no rln proof and should not be validated
|
||||||
msgValidate4 = wakuRlnRelay.validateMessage(wm4, some(time))
|
msgValidate4 = wakuRlnRelay.validateMessage(wm4, some(time))
|
||||||
|
|
||||||
|
|
||||||
check:
|
check:
|
||||||
msgValidate1 == MessageValidationResult.Valid
|
msgValidate1 == MessageValidationResult.Valid
|
||||||
msgValidate2 == MessageValidationResult.Spam
|
msgValidate2 == MessageValidationResult.Spam
|
||||||
msgValidate3 == MessageValidationResult.Valid
|
msgValidate3 == MessageValidationResult.Valid
|
||||||
msgValidate4 == MessageValidationResult.Invalid
|
msgValidate4 == MessageValidationResult.Invalid
|
||||||
|
|
||||||
|
|||||||
271
tests/v2/test_waku_rln_relay_onchain.nim
Normal file
271
tests/v2/test_waku_rln_relay_onchain.nim
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
|
||||||
|
# contains rln-relay tests that require interaction with Ganache i.e., onchain tests
|
||||||
|
{.used.}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/options, sequtils, times,
|
||||||
|
testutils/unittests, chronos, chronicles, stint, web3, json,
|
||||||
|
stew/byteutils, stew/shims/net as stewNet,
|
||||||
|
libp2p/crypto/crypto,
|
||||||
|
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils,
|
||||||
|
waku_rln_relay_types, rln_relay_contract],
|
||||||
|
../../waku/v2/node/wakunode2,
|
||||||
|
../test_helpers,
|
||||||
|
./test_utils
|
||||||
|
|
||||||
|
const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto"
|
||||||
|
const RLNRELAY_CONTENT_TOPIC = "waku/2/rlnrelay/proto"
|
||||||
|
|
||||||
|
# contract ABI
|
||||||
|
contract(MembershipContract):
|
||||||
|
proc register(pubkey: Uint256) # external payable
|
||||||
|
proc MemberRegistered(pubkey: Uint256, index: Uint256) {.event.}
|
||||||
|
# proc registerBatch(pubkeys: seq[Uint256]) # external payable
|
||||||
|
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
|
||||||
|
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
|
||||||
|
|
||||||
|
# a util function used for testing purposes
|
||||||
|
# it deploys membership contract on Ganache (or any Eth client available on ETH_CLIENT address)
|
||||||
|
# must be edited if used for a different contract than membership contract
|
||||||
|
proc uploadRLNContract*(ethClientAddress: string): Future[Address] {.async.} =
|
||||||
|
let web3 = await newWeb3(ethClientAddress)
|
||||||
|
debug "web3 connected to", ethClientAddress
|
||||||
|
|
||||||
|
# fetch the list of registered accounts
|
||||||
|
let accounts = await web3.provider.eth_accounts()
|
||||||
|
web3.defaultAccount = accounts[1]
|
||||||
|
let add = web3.defaultAccount
|
||||||
|
debug "contract deployer account address ", add
|
||||||
|
|
||||||
|
var balance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
|
||||||
|
debug "Initial account balance: ", balance
|
||||||
|
|
||||||
|
# deploy the poseidon hash contract and gets its address
|
||||||
|
let
|
||||||
|
hasherReceipt = await web3.deployContract(POSEIDON_HASHER_CODE)
|
||||||
|
hasherAddress = hasherReceipt.contractAddress.get
|
||||||
|
debug "hasher address: ", hasherAddress
|
||||||
|
|
||||||
|
|
||||||
|
# encode membership contract inputs to 32 bytes zero-padded
|
||||||
|
let
|
||||||
|
membershipFeeEncoded = encode(MEMBERSHIP_FEE).data
|
||||||
|
depthEncoded = encode(MERKLE_TREE_DEPTH.u256).data
|
||||||
|
hasherAddressEncoded = encode(hasherAddress).data
|
||||||
|
# this is the contract constructor input
|
||||||
|
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
|
||||||
|
|
||||||
|
|
||||||
|
debug "encoded membership fee: ", membershipFeeEncoded
|
||||||
|
debug "encoded depth: ", depthEncoded
|
||||||
|
debug "encoded hasher address: ", hasherAddressEncoded
|
||||||
|
debug "encoded contract input:", contractInput
|
||||||
|
|
||||||
|
# deploy membership contract with its constructor inputs
|
||||||
|
let receipt = await web3.deployContract(MEMBERSHIP_CONTRACT_CODE,
|
||||||
|
contractInput = contractInput)
|
||||||
|
var contractAddress = receipt.contractAddress.get
|
||||||
|
debug "Address of the deployed membership contract: ", contractAddress
|
||||||
|
|
||||||
|
balance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
|
||||||
|
debug "Account balance after the contract deployment: ", balance
|
||||||
|
|
||||||
|
await web3.close()
|
||||||
|
debug "disconnected from ", ethClientAddress
|
||||||
|
|
||||||
|
return contractAddress
|
||||||
|
|
||||||
|
procSuite "Waku-rln-relay":
|
||||||
|
asyncTest "event subscription":
|
||||||
|
# preparation ------------------------------
|
||||||
|
debug "ethereum client address", ETH_CLIENT
|
||||||
|
let contractAddress = await uploadRLNContract(ETH_CLIENT)
|
||||||
|
# connect to the eth client
|
||||||
|
let web3 = await newWeb3(ETH_CLIENT)
|
||||||
|
debug "web3 connected to", ETH_CLIENT
|
||||||
|
|
||||||
|
# fetch the list of registered accounts
|
||||||
|
let accounts = await web3.provider.eth_accounts()
|
||||||
|
web3.defaultAccount = accounts[1]
|
||||||
|
debug "contract deployer account address ",
|
||||||
|
defaultAccount = web3.defaultAccount
|
||||||
|
|
||||||
|
# prepare a contract sender to interact with it
|
||||||
|
var contractObj = web3.contractSender(MembershipContract,
|
||||||
|
contractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
||||||
|
|
||||||
|
# create an RLN instance
|
||||||
|
var rlnInstance = createRLNInstance()
|
||||||
|
check:
|
||||||
|
rlnInstance.isOk == true
|
||||||
|
# generate the membership keys
|
||||||
|
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
||||||
|
check:
|
||||||
|
membershipKeyPair.isSome
|
||||||
|
let pk = membershipKeyPair.get().idCommitment.toUInt256()
|
||||||
|
debug "membership commitment key", pk = pk
|
||||||
|
|
||||||
|
# test ------------------------------
|
||||||
|
var fut = newFuture[void]()
|
||||||
|
let s = await contractObj.subscribe(MemberRegistered, %*{"fromBlock": "0x0",
|
||||||
|
"address": contractAddress}) do(
|
||||||
|
pubkey: Uint256, index: Uint256){.raises: [Defect], gcsafe.}:
|
||||||
|
try:
|
||||||
|
debug "onRegister", pubkey = pubkey, index = index
|
||||||
|
check:
|
||||||
|
pubkey == pk
|
||||||
|
fut.complete()
|
||||||
|
except Exception as err:
|
||||||
|
# chronos still raises exceptions which inherit directly from Exception
|
||||||
|
doAssert false, err.msg
|
||||||
|
do (err: CatchableError):
|
||||||
|
echo "Error from subscription: ", err.msg
|
||||||
|
|
||||||
|
# register a member
|
||||||
|
let tx = await contractObj.register(pk).send(value = MEMBERSHIP_FEE)
|
||||||
|
debug "a member is registered", tx = tx
|
||||||
|
|
||||||
|
# wait for the event to be received
|
||||||
|
await fut
|
||||||
|
|
||||||
|
# release resources -----------------------
|
||||||
|
await web3.close()
|
||||||
|
|
||||||
|
asyncTest "insert a key to the membership contract":
|
||||||
|
# preparation ------------------------------
|
||||||
|
debug "ethereum client address", ETH_CLIENT
|
||||||
|
let contractAddress = await uploadRLNContract(ETH_CLIENT)
|
||||||
|
# connect to the eth client
|
||||||
|
let web3 = await newWeb3(ETH_CLIENT)
|
||||||
|
debug "web3 connected to", ETH_CLIENT
|
||||||
|
|
||||||
|
# fetch the list of registered accounts
|
||||||
|
let accounts = await web3.provider.eth_accounts()
|
||||||
|
web3.defaultAccount = accounts[1]
|
||||||
|
let add = web3.defaultAccount
|
||||||
|
debug "contract deployer account address ", add
|
||||||
|
|
||||||
|
# prepare a contract sender to interact with it
|
||||||
|
var sender = web3.contractSender(MembershipContract,
|
||||||
|
contractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
||||||
|
|
||||||
|
# send takes the following parameters, c: ContractCallBase, value = 0.u256, gas = 3000000'u64 gasPrice = 0
|
||||||
|
# should use send proc for the contract functions that update the state of the contract
|
||||||
|
let tx = await sender.register(20.u256).send(value = MEMBERSHIP_FEE) # value is the membership fee
|
||||||
|
debug "The hash of registration tx: ", tx
|
||||||
|
|
||||||
|
# var members: array[2, uint256] = [20.u256, 21.u256]
|
||||||
|
# debug "This is the batch registration result ", await sender.registerBatch(members).send(value = (members.len * MEMBERSHIP_FEE)) # value is the membership fee
|
||||||
|
|
||||||
|
let balance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
|
||||||
|
debug "Balance after registration: ", balance
|
||||||
|
|
||||||
|
await web3.close()
|
||||||
|
debug "disconnected from", ETH_CLIENT
|
||||||
|
|
||||||
|
asyncTest "registration procedure":
|
||||||
|
# preparation ------------------------------
|
||||||
|
# deploy the contract
|
||||||
|
let contractAddress = await uploadRLNContract(ETH_CLIENT)
|
||||||
|
|
||||||
|
# prepare rln-relay peer inputs
|
||||||
|
let
|
||||||
|
web3 = await newWeb3(ETH_CLIENT)
|
||||||
|
accounts = await web3.provider.eth_accounts()
|
||||||
|
# choose one of the existing accounts for the rln-relay peer
|
||||||
|
ethAccountAddress = accounts[0]
|
||||||
|
await web3.close()
|
||||||
|
|
||||||
|
# create an RLN instance
|
||||||
|
var rlnInstance = createRLNInstance()
|
||||||
|
check:
|
||||||
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
|
# generate the membership keys
|
||||||
|
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
|
||||||
|
check:
|
||||||
|
membershipKeyPair.isSome
|
||||||
|
|
||||||
|
# test ------------------------------
|
||||||
|
# initialize the WakuRLNRelay
|
||||||
|
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
|
||||||
|
membershipIndex: MembershipIndex(0),
|
||||||
|
ethClientAddress: ETH_CLIENT,
|
||||||
|
ethAccountAddress: ethAccountAddress,
|
||||||
|
membershipContractAddress: contractAddress)
|
||||||
|
|
||||||
|
# register the rln-relay peer to the membership contract
|
||||||
|
let is_successful = await rlnPeer.register()
|
||||||
|
check:
|
||||||
|
is_successful
|
||||||
|
|
||||||
|
asyncTest "mounting waku rln-relay":
|
||||||
|
# preparation ------------------------------
|
||||||
|
let
|
||||||
|
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||||
|
node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"),
|
||||||
|
Port(60000))
|
||||||
|
await node.start()
|
||||||
|
|
||||||
|
# deploy the contract
|
||||||
|
let membershipContractAddress = await uploadRLNContract(ETH_CLIENT)
|
||||||
|
|
||||||
|
# prepare rln-relay inputs
|
||||||
|
let
|
||||||
|
web3 = await newWeb3(ETH_CLIENT)
|
||||||
|
accounts = await web3.provider.eth_accounts()
|
||||||
|
# choose one of the existing account for the rln-relay peer
|
||||||
|
ethAccountAddress = accounts[9]
|
||||||
|
await web3.close()
|
||||||
|
|
||||||
|
# create current peer's pk
|
||||||
|
var rlnInstance = createRLNInstance()
|
||||||
|
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
|
||||||
|
|
||||||
|
# test ------------------------------
|
||||||
|
# start rln-relay
|
||||||
|
node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC])
|
||||||
|
await node.mountRlnRelay(ethClientAddrOpt = some(EthClient),
|
||||||
|
ethAccAddrOpt = some(ethAccountAddress),
|
||||||
|
memContractAddOpt = some(membershipContractAddress),
|
||||||
|
groupOpt = some(group),
|
||||||
|
memKeyPairOpt = some(keypair.get()),
|
||||||
|
memIndexOpt = some(index),
|
||||||
|
pubsubTopic = RLNRELAY_PUBSUB_TOPIC,
|
||||||
|
contentTopic = RLNRELAY_CONTENT_TOPIC)
|
||||||
|
let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex
|
||||||
|
debug "calculated root ", calculatedRoot
|
||||||
|
|
||||||
|
check:
|
||||||
|
expectedRoot == calculatedRoot
|
||||||
|
|
||||||
|
await node.stop()
|
||||||
|
|
||||||
107
waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim
Normal file
107
waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,12 +1,12 @@
|
|||||||
{.push raises: [Defect].}
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/sequtils, tables, times,
|
std/sequtils, tables, times,
|
||||||
chronicles, options, chronos, stint,
|
chronicles, options, chronos, stint,
|
||||||
web3,
|
web3,
|
||||||
stew/results,
|
stew/results,
|
||||||
stew/[byteutils, arrayops, endians2],
|
stew/[byteutils, arrayops, endians2],
|
||||||
rln,
|
rln,
|
||||||
waku_rln_relay_types,
|
waku_rln_relay_types,
|
||||||
../waku_message
|
../waku_message
|
||||||
|
|
||||||
@ -16,7 +16,8 @@ logScope:
|
|||||||
type RLNResult* = Result[RLN[Bn256], string]
|
type RLNResult* = Result[RLN[Bn256], string]
|
||||||
type MerkleNodeResult* = Result[MerkleNode, string]
|
type MerkleNodeResult* = Result[MerkleNode, string]
|
||||||
type RateLimitProofResult* = Result[RateLimitProof, string]
|
type RateLimitProofResult* = Result[RateLimitProof, string]
|
||||||
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure, raises: [Defect].}
|
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure,
|
||||||
|
raises: [Defect].}
|
||||||
|
|
||||||
# membership contract interface
|
# membership contract interface
|
||||||
contract(MembershipContract):
|
contract(MembershipContract):
|
||||||
@ -26,14 +27,14 @@ contract(MembershipContract):
|
|||||||
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): 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
|
||||||
## An RLN instance supports both zkSNARKs logics and Merkle tree data structure and operations
|
## An RLN instance supports both zkSNARKs logics and Merkle tree data structure and operations
|
||||||
## d indicates the depth of Merkle tree
|
## d indicates the depth of Merkle tree
|
||||||
var
|
var
|
||||||
rlnInstance: RLN[Bn256]
|
rlnInstance: RLN[Bn256]
|
||||||
merkleDepth: csize_t = uint(d)
|
merkleDepth: csize_t = uint(d)
|
||||||
## parameters.key contains the prover and verifier keys
|
## parameters.key contains the prover and verifier keys
|
||||||
## to generate this file, clone this repo https://github.com/kilic/rln
|
## to generate this file, clone this repo https://github.com/kilic/rln
|
||||||
## and run the following command in the root directory of the cloned project
|
## and run the following command in the root directory of the cloned project
|
||||||
## cargo run --example export_test_keys
|
## cargo run --example export_test_keys
|
||||||
## the file is generated separately and copied here
|
## the file is generated separately and copied here
|
||||||
@ -43,76 +44,84 @@ proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
|||||||
## and then proceed as explained above
|
## and then proceed as explained above
|
||||||
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
||||||
pbytes = parameters.toBytes()
|
pbytes = parameters.toBytes()
|
||||||
len : csize_t = uint(pbytes.len)
|
len: csize_t = uint(pbytes.len)
|
||||||
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
||||||
|
|
||||||
# check the parameters.key is not empty
|
# check the parameters.key is not empty
|
||||||
if (pbytes.len == 0):
|
if (pbytes.len == 0):
|
||||||
debug "error in parameters.key"
|
debug "error in parameters.key"
|
||||||
return err("error in parameters.key")
|
return err("error in parameters.key")
|
||||||
|
|
||||||
# create an instance of RLN
|
# create an instance of RLN
|
||||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer, addr rlnInstance)
|
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||||
|
addr rlnInstance)
|
||||||
# check whether the circuit parameters are generated successfully
|
# check whether the circuit parameters are generated successfully
|
||||||
if (res == false):
|
if (res == false):
|
||||||
debug "error in parameters generation"
|
debug "error in parameters generation"
|
||||||
return err("error in parameters generation")
|
return err("error in parameters generation")
|
||||||
return ok(rlnInstance)
|
return ok(rlnInstance)
|
||||||
|
|
||||||
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
||||||
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
||||||
|
|
||||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
var
|
var
|
||||||
keysBuffer : Buffer
|
keysBuffer: Buffer
|
||||||
keysBufferPtr = addr(keysBuffer)
|
keysBufferPtr = addr(keysBuffer)
|
||||||
done = key_gen(ctxPtr, keysBufferPtr)
|
done = key_gen(ctxPtr, keysBufferPtr)
|
||||||
|
|
||||||
# check whether the keys are generated successfully
|
# check whether the keys are generated successfully
|
||||||
if(done == false):
|
if(done == false):
|
||||||
debug "error in key generation"
|
debug "error in key generation"
|
||||||
return none(MembershipKeyPair)
|
return none(MembershipKeyPair)
|
||||||
|
|
||||||
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
||||||
# the public and secret keys together are 64 bytes
|
# the public and secret keys together are 64 bytes
|
||||||
if (generatedKeys.len != 64):
|
if (generatedKeys.len != 64):
|
||||||
debug "the generated keys are invalid"
|
debug "the generated keys are invalid"
|
||||||
return none(MembershipKeyPair)
|
return none(MembershipKeyPair)
|
||||||
|
|
||||||
# TODO define a separate proc to decode the generated keys to the secret and public components
|
# TODO define a separate proc to decode the generated keys to the secret and public components
|
||||||
var
|
var
|
||||||
secret: array[32, byte]
|
secret: array[32, byte]
|
||||||
public: array[32, byte]
|
public: array[32, byte]
|
||||||
for (i,x) in secret.mpairs: x = generatedKeys[i]
|
for (i, x) in secret.mpairs: x = generatedKeys[i]
|
||||||
for (i,x) in public.mpairs: x = generatedKeys[i+32]
|
for (i, x) in public.mpairs: x = generatedKeys[i+32]
|
||||||
|
|
||||||
var
|
var
|
||||||
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
||||||
|
|
||||||
|
|
||||||
return some(keypair)
|
return some(keypair)
|
||||||
|
|
||||||
|
proc toUInt256*(idCommitment: IDCommitment): UInt256 =
|
||||||
|
let pk = cast[UInt256](idCommitment)
|
||||||
|
return pk
|
||||||
|
|
||||||
proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
|
proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
|
||||||
## registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
|
## registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
|
||||||
## into the membership contract whose address is in rlnPeer.membershipContractAddress
|
## into the membership contract whose address is in rlnPeer.membershipContractAddress
|
||||||
let web3 = await newWeb3(rlnPeer.ethClientAddress)
|
let web3 = await newWeb3(rlnPeer.ethClientAddress)
|
||||||
web3.defaultAccount = rlnPeer.ethAccountAddress
|
web3.defaultAccount = rlnPeer.ethAccountAddress
|
||||||
# when the private key is set in a web3 instance, the send proc (sender.register(pk).send(MembershipFee))
|
# when the private key is set in a web3 instance, the send proc (sender.register(pk).send(MEMBERSHIP_FEE))
|
||||||
# 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,
|
||||||
let pk = cast[UInt256](rlnPeer.membershipKeyPair.idCommitment)
|
rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
||||||
discard await sender.register(pk).send(MembershipFee)
|
let pk = toUInt256(rlnPeer.membershipKeyPair.idCommitment)
|
||||||
|
discard await sender.register(pk).send(MEMBERSHIP_FEE)
|
||||||
|
debug "pk", pk = pk
|
||||||
# TODO check the receipt and then return true/false
|
# TODO check the receipt and then return true/false
|
||||||
await web3.close()
|
await web3.close()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc appendLength*(input: openArray[byte]): seq[byte] =
|
proc appendLength*(input: openArray[byte]): seq[byte] =
|
||||||
## returns length prefixed version of the input
|
## returns length prefixed version of the input
|
||||||
## with the following format [len<8>|input<var>]
|
## with the following format [len<8>|input<var>]
|
||||||
## len: 8-byte value that represents the number of bytes in the `input`
|
## len: 8-byte value that represents the number of bytes in the `input`
|
||||||
## len is serialized in little-endian
|
## len is serialized in little-endian
|
||||||
## input: the supplied `input`
|
## input: the supplied `input`
|
||||||
let
|
let
|
||||||
# the length should be serialized in little-endian
|
# the length should be serialized in little-endian
|
||||||
len = toBytes(uint64(input.len), Endianness.littleEndian)
|
len = toBytes(uint64(input.len), Endianness.littleEndian)
|
||||||
output = concat(@len, @input)
|
output = concat(@len, @input)
|
||||||
@ -125,32 +134,35 @@ proc toBuffer*(x: openArray[byte]): Buffer =
|
|||||||
let output = Buffer(`ptr`: addr(temp[0]), len: uint(temp.len))
|
let output = Buffer(`ptr`: addr(temp[0]), len: uint(temp.len))
|
||||||
return output
|
return output
|
||||||
|
|
||||||
proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode =
|
proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode =
|
||||||
## a thin layer on top of the Nim wrapper of the Poseidon hasher
|
## a thin layer on top of the Nim wrapper of the Poseidon hasher
|
||||||
debug "hash input", hashhex=data.toHex()
|
debug "hash input", hashhex = data.toHex()
|
||||||
var lenPrefData = appendLength(data)
|
var lenPrefData = appendLength(data)
|
||||||
var
|
var
|
||||||
hashInputBuffer = lenPrefData.toBuffer()
|
hashInputBuffer = lenPrefData.toBuffer()
|
||||||
outputBuffer: Buffer # will holds the hash output
|
outputBuffer: Buffer # will holds the hash output
|
||||||
|
|
||||||
debug "hash input buffer length", bufflen=hashInputBuffer.len
|
debug "hash input buffer length", bufflen = hashInputBuffer.len
|
||||||
let
|
let
|
||||||
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
||||||
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch, msg: openArray[byte]): seq[byte] =
|
proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch,
|
||||||
|
msg: openArray[byte]): seq[byte] =
|
||||||
## a private proc to convert RateLimitProof and the data to a byte seq
|
## a private proc to convert RateLimitProof and the data to a byte seq
|
||||||
## this conversion is used in the proofGen proc
|
## this conversion is used in the proofGen proc
|
||||||
## the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146
|
## the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146
|
||||||
## [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
## [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||||
let memIndexBytes = toBytes(uint64(memIndex), Endianness.littleEndian)
|
let memIndexBytes = toBytes(uint64(memIndex), Endianness.littleEndian)
|
||||||
let lenPrefMsg = appendLength(msg)
|
let lenPrefMsg = appendLength(msg)
|
||||||
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: MembershipKeyPair, memIndex: MembershipIndex, epoch: Epoch): RateLimitProofResult =
|
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||||
|
memKeys: MembershipKeyPair, memIndex: MembershipIndex,
|
||||||
|
epoch: Epoch): RateLimitProofResult =
|
||||||
|
|
||||||
# serialize inputs
|
# serialize inputs
|
||||||
let serializedInputs = serialize(idKey = memKeys.idKey,
|
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||||
@ -168,12 +180,12 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh
|
|||||||
if not proofIsSuccessful:
|
if not proofIsSuccessful:
|
||||||
return err("could not generate the proof")
|
return err("could not generate the proof")
|
||||||
|
|
||||||
var proofValue = cast[ptr array[416,byte]] (proof.`ptr`)
|
var proofValue = cast[ptr array[416, byte]] (proof.`ptr`)
|
||||||
let proofBytes: array[416,byte] = proofValue[]
|
let proofBytes: array[416, byte] = proofValue[]
|
||||||
debug "proof content", proofHex=proofValue[].toHex
|
debug "proof content", proofHex = proofValue[].toHex
|
||||||
|
|
||||||
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
||||||
let
|
let
|
||||||
proofOffset = 256
|
proofOffset = 256
|
||||||
rootOffset = proofOffset + 32
|
rootOffset = proofOffset + 32
|
||||||
epochOffset = rootOffset + 32
|
epochOffset = rootOffset + 32
|
||||||
@ -181,8 +193,8 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh
|
|||||||
shareYOffset = shareXOffset + 32
|
shareYOffset = shareXOffset + 32
|
||||||
nullifierOffset = shareYOffset + 32
|
nullifierOffset = shareYOffset + 32
|
||||||
|
|
||||||
var
|
var
|
||||||
zkproof: ZKSNARK
|
zkproof: ZKSNARK
|
||||||
proofRoot, shareX, shareY: MerkleNode
|
proofRoot, shareX, shareY: MerkleNode
|
||||||
epoch: Epoch
|
epoch: Epoch
|
||||||
nullifier: Nullifier
|
nullifier: Nullifier
|
||||||
@ -209,7 +221,7 @@ proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
|||||||
## the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
|
## the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
|
||||||
## [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
## [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
||||||
let lenPrefMsg = appendLength(@data)
|
let lenPrefMsg = appendLength(@data)
|
||||||
var proofBytes = concat(@(proof.proof),
|
var proofBytes = concat(@(proof.proof),
|
||||||
@(proof.merkleRoot),
|
@(proof.merkleRoot),
|
||||||
@(proof.epoch),
|
@(proof.epoch),
|
||||||
@(proof.shareX),
|
@(proof.shareX),
|
||||||
@ -219,23 +231,24 @@ proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
|||||||
|
|
||||||
return proofBytes
|
return proofBytes
|
||||||
|
|
||||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte], proof: RateLimitProof): bool =
|
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||||
var
|
proof: RateLimitProof): bool =
|
||||||
proofBytes= serialize(proof, data)
|
var
|
||||||
|
proofBytes = serialize(proof, data)
|
||||||
proofBuffer = proofBytes.toBuffer()
|
proofBuffer = proofBytes.toBuffer()
|
||||||
f = 0.uint32
|
f = 0.uint32
|
||||||
trace "serialized proof", proof=proofBytes.toHex()
|
trace "serialized proof", proof = proofBytes.toHex()
|
||||||
|
|
||||||
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
||||||
if not verifyIsSuccessful:
|
if not verifyIsSuccessful:
|
||||||
# something went wrong in verification
|
# something went wrong in verification
|
||||||
return false
|
return false
|
||||||
# f = 0 means the proof is verified
|
# f = 0 means the proof is verified
|
||||||
if f == 0:
|
if f == 0:
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
|
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
|
||||||
var pkBuffer = toBuffer(idComm)
|
var pkBuffer = toBuffer(idComm)
|
||||||
let pkBufferPtr = addr pkBuffer
|
let pkBufferPtr = addr pkBuffer
|
||||||
|
|
||||||
@ -243,14 +256,14 @@ 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: MembershipIndex): bool =
|
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
|
||||||
let deletion_success = delete_member(rlnInstance, index)
|
let deletion_success = delete_member(rlnInstance, index)
|
||||||
return deletion_success
|
return deletion_success
|
||||||
|
|
||||||
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
||||||
# read the Merkle Tree root after insertion
|
# read the Merkle Tree root after insertion
|
||||||
var
|
var
|
||||||
root {.noinit.} : Buffer = Buffer()
|
root {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr = addr(root)
|
rootPtr = addr(root)
|
||||||
get_root_successful = get_root(rlnInstance, rootPtr)
|
get_root_successful = get_root(rlnInstance, rootPtr)
|
||||||
if (not get_root_successful): return err("could not get the root")
|
if (not get_root_successful): return err("could not get the root")
|
||||||
@ -259,54 +272,57 @@ proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
|||||||
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
||||||
return ok(rootValue)
|
return ok(rootValue)
|
||||||
|
|
||||||
proc toMembershipKeyPairs*(groupKeys: seq[(string, string)]): seq[MembershipKeyPair] {.raises: [Defect, ValueError]} =
|
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
|
## 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
|
## the toMembershipKeyPairs proc populates a sequence of MembershipKeyPairs using the supplied groupKeys
|
||||||
|
|
||||||
var groupKeyPairs = newSeq[MembershipKeyPair]()
|
var groupKeyPairs = newSeq[MembershipKeyPair]()
|
||||||
|
|
||||||
for i in 0..groupKeys.len-1:
|
for i in 0..groupKeys.len-1:
|
||||||
let
|
let
|
||||||
idKey = groupKeys[i][0].hexToByteArray(32)
|
idKey = groupKeys[i][0].hexToByteArray(32)
|
||||||
idCommitment = groupKeys[i][1].hexToByteArray(32)
|
idCommitment = groupKeys[i][1].hexToByteArray(32)
|
||||||
groupKeyPairs.add(MembershipKeyPair(idKey: idKey, idCommitment: idCommitment))
|
groupKeyPairs.add(MembershipKeyPair(idKey: idKey,
|
||||||
|
idCommitment: idCommitment))
|
||||||
return groupKeyPairs
|
return groupKeyPairs
|
||||||
|
|
||||||
proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError].} =
|
proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError].} =
|
||||||
## returns the root of the Merkle tree that is computed from the supplied list
|
## returns the root of the Merkle tree that is computed from the supplied list
|
||||||
## the root is in hexadecimal format
|
## the root is in hexadecimal format
|
||||||
|
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
doAssert(rlnInstance.isOk)
|
doAssert(rlnInstance.isOk)
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
|
|
||||||
# create a Merkle tree
|
# create a Merkle tree
|
||||||
for i in 0..list.len-1:
|
for i in 0..list.len-1:
|
||||||
var member_is_added = false
|
var member_is_added = false
|
||||||
member_is_added = rln.insertMember(list[i])
|
member_is_added = rln.insertMember(list[i])
|
||||||
doAssert(member_is_added)
|
doAssert(member_is_added)
|
||||||
|
|
||||||
let root = rln.getMerkleRoot().value().toHex
|
let root = rln.getMerkleRoot().value().toHex
|
||||||
return root
|
return root
|
||||||
|
|
||||||
proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [Defect, IOError].} =
|
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
|
## 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
|
## 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)
|
## 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
|
# initialize a Merkle tree
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
if not rlnInstance.isOk:
|
if not rlnInstance.isOk:
|
||||||
return (@[], "")
|
return (@[], "")
|
||||||
var rln = rlnInstance.value
|
var rln = rlnInstance.value
|
||||||
|
|
||||||
var output = newSeq[(string,string)]()
|
var output = newSeq[(string, string)]()
|
||||||
for i in 0..n-1:
|
for i in 0..n-1:
|
||||||
|
|
||||||
# generate a key pair
|
# generate a key pair
|
||||||
let keypair = rln.membershipKeyGen()
|
let keypair = rln.membershipKeyGen()
|
||||||
doAssert(keypair.isSome())
|
doAssert(keypair.isSome())
|
||||||
|
|
||||||
let keyTuple = (keypair.get().idKey.toHex, keypair.get().idCommitment.toHex)
|
let keyTuple = (keypair.get().idKey.toHex, keypair.get().idCommitment.toHex)
|
||||||
output.add(keyTuple)
|
output.add(keyTuple)
|
||||||
|
|
||||||
@ -314,12 +330,14 @@ proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [De
|
|||||||
let inserted = rln.insertMember(keypair.get().idCommitment)
|
let inserted = rln.insertMember(keypair.get().idCommitment)
|
||||||
if not inserted:
|
if not inserted:
|
||||||
return (@[], "")
|
return (@[], "")
|
||||||
|
|
||||||
|
|
||||||
let root = rln.getMerkleRoot().value.toHex
|
let root = rln.getMerkleRoot().value.toHex
|
||||||
return (output, root)
|
return (output, root)
|
||||||
|
|
||||||
proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[IDCommitment]],Option[MembershipKeyPair], Option[MembershipIndex]) {.raises:[Defect, ValueError].} =
|
proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[
|
||||||
|
IDCommitment]], Option[MembershipKeyPair], Option[
|
||||||
|
MembershipIndex]) {.raises: [Defect, ValueError].} =
|
||||||
let
|
let
|
||||||
# static group
|
# static group
|
||||||
groupKeys = STATIC_GROUP_KEYS
|
groupKeys = STATIC_GROUP_KEYS
|
||||||
@ -328,31 +346,33 @@ proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[IDCommitment
|
|||||||
debug "rln-relay membership index", rlnRelayMemIndex
|
debug "rln-relay membership index", rlnRelayMemIndex
|
||||||
|
|
||||||
# validate the user-supplied membership index
|
# validate the user-supplied membership index
|
||||||
if rlnRelayMemIndex < MembershipIndex(0) or rlnRelayMemIndex >= MembershipIndex(groupSize):
|
if rlnRelayMemIndex < MembershipIndex(0) or rlnRelayMemIndex >=
|
||||||
|
MembershipIndex(groupSize):
|
||||||
error "wrong membership index"
|
error "wrong membership index"
|
||||||
return(none(seq[IDCommitment]), none(MembershipKeyPair), none(MembershipIndex))
|
return(none(seq[IDCommitment]), none(MembershipKeyPair), none(MembershipIndex))
|
||||||
|
|
||||||
# prepare the outputs from the static group keys
|
# prepare the outputs from the static group keys
|
||||||
let
|
let
|
||||||
# create a sequence of MembershipKeyPairs from the group keys (group keys are in string format)
|
# create a sequence of MembershipKeyPairs from the group keys (group keys are in string format)
|
||||||
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
||||||
# extract id commitment keys
|
# extract id commitment keys
|
||||||
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment)
|
||||||
groupOpt= some(groupIDCommitments)
|
groupOpt = some(groupIDCommitments)
|
||||||
# user selected membership key pair
|
# user selected membership key pair
|
||||||
memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex])
|
memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex])
|
||||||
memIndexOpt= some(rlnRelayMemIndex)
|
memIndexOpt = some(rlnRelayMemIndex)
|
||||||
|
|
||||||
return (groupOpt, memKeyPairOpt, memIndexOpt)
|
return (groupOpt, memKeyPairOpt, memIndexOpt)
|
||||||
|
|
||||||
proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
||||||
## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same
|
## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same
|
||||||
## epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares
|
## epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares
|
||||||
## otherwise, returns false
|
## otherwise, returns false
|
||||||
## emits an error string if `KeyError` occurs (never happens, it is just to avoid raising unnecessary `KeyError` exception )
|
## emits an error string if `KeyError` occurs (never happens, it is just to avoid raising unnecessary `KeyError` exception )
|
||||||
|
|
||||||
# extract the proof metadata of the supplied `msg`
|
# extract the proof metadata of the supplied `msg`
|
||||||
let proofMD = ProofMetadata(nullifier: msg.proof.nullifier, shareX: msg.proof.shareX, shareY: msg.proof.shareY)
|
let proofMD = ProofMetadata(nullifier: msg.proof.nullifier,
|
||||||
|
shareX: msg.proof.shareX, shareY: msg.proof.shareY)
|
||||||
|
|
||||||
# check if the epoch exists
|
# check if the epoch exists
|
||||||
if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch):
|
if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch):
|
||||||
@ -363,12 +383,14 @@ proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string
|
|||||||
return ok(false)
|
return ok(false)
|
||||||
|
|
||||||
# check for a message with the same nullifier but different secret shares
|
# check for a message with the same nullifier but different secret shares
|
||||||
let matched = rlnPeer.nullifierLog[msg.proof.epoch].filterIt((it.nullifier == proofMD.nullifier) and ((it.shareX != proofMD.shareX) or (it.shareY != proofMD.shareY)))
|
let matched = rlnPeer.nullifierLog[msg.proof.epoch].filterIt((
|
||||||
|
it.nullifier == proofMD.nullifier) and ((it.shareX != proofMD.shareX) or
|
||||||
|
(it.shareY != proofMD.shareY)))
|
||||||
|
|
||||||
if matched.len != 0:
|
if matched.len != 0:
|
||||||
# there is a duplicate
|
# there is a duplicate
|
||||||
return ok(true)
|
return ok(true)
|
||||||
|
|
||||||
# there is no duplicate
|
# there is no duplicate
|
||||||
return ok(false)
|
return ok(false)
|
||||||
|
|
||||||
@ -376,17 +398,18 @@ proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string
|
|||||||
return err("the epoch was not found")
|
return err("the epoch was not found")
|
||||||
|
|
||||||
proc updateLog*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
proc updateLog*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
||||||
## extracts the `ProofMetadata` of the supplied messages `msg` and
|
## extracts the `ProofMetadata` of the supplied messages `msg` and
|
||||||
## saves it in the `nullifierLog` of the `rlnPeer`
|
## saves it in the `nullifierLog` of the `rlnPeer`
|
||||||
|
|
||||||
let proofMD = ProofMetadata(nullifier: msg.proof.nullifier, shareX: msg.proof.shareX, shareY: msg.proof.shareY)
|
let proofMD = ProofMetadata(nullifier: msg.proof.nullifier,
|
||||||
debug "proof metadata", proofMD=proofMD
|
shareX: msg.proof.shareX, shareY: msg.proof.shareY)
|
||||||
|
debug "proof metadata", proofMD = proofMD
|
||||||
|
|
||||||
# check if the epoch exists
|
# check if the epoch exists
|
||||||
if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch):
|
if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch):
|
||||||
rlnPeer.nullifierLog[msg.proof.epoch]= @[proofMD]
|
rlnPeer.nullifierLog[msg.proof.epoch] = @[proofMD]
|
||||||
return ok(true)
|
return ok(true)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# check if an identical record exists
|
# check if an identical record exists
|
||||||
if rlnPeer.nullifierLog[msg.proof.epoch].contains(proofMD):
|
if rlnPeer.nullifierLog[msg.proof.epoch].contains(proofMD):
|
||||||
@ -400,17 +423,17 @@ proc updateLog*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
|||||||
proc toEpoch*(t: uint64): Epoch =
|
proc toEpoch*(t: uint64): Epoch =
|
||||||
## converts `t` to `Epoch` in little-endian order
|
## converts `t` to `Epoch` in little-endian order
|
||||||
let bytes = toBytes(t, Endianness.littleEndian)
|
let bytes = toBytes(t, Endianness.littleEndian)
|
||||||
debug "bytes", bytes=bytes
|
debug "bytes", bytes = bytes
|
||||||
var epoch: Epoch
|
var epoch: Epoch
|
||||||
discard epoch.copyFrom(bytes)
|
discard epoch.copyFrom(bytes)
|
||||||
return epoch
|
return epoch
|
||||||
|
|
||||||
proc fromEpoch*(epoch: Epoch): uint64 =
|
proc fromEpoch*(epoch: Epoch): uint64 =
|
||||||
## decodes bytes of `epoch` (in little-endian) to uint64
|
## decodes bytes of `epoch` (in little-endian) to uint64
|
||||||
let t = fromBytesLE(uint64, array[32,byte](epoch))
|
let t = fromBytesLE(uint64, array[32, byte](epoch))
|
||||||
return t
|
return t
|
||||||
|
|
||||||
proc calcEpoch*(t: float64): Epoch =
|
proc calcEpoch*(t: float64): Epoch =
|
||||||
## gets time `t` as `flaot64` with subseconds resolution in the fractional part
|
## gets time `t` as `flaot64` with subseconds resolution in the fractional part
|
||||||
## and returns its corresponding rln `Epoch` value
|
## and returns its corresponding rln `Epoch` value
|
||||||
let e = uint64(t/EPOCH_UNIT_SECONDS)
|
let e = uint64(t/EPOCH_UNIT_SECONDS)
|
||||||
@ -420,25 +443,26 @@ proc getCurrentEpoch*(): Epoch =
|
|||||||
## gets the current rln Epoch time
|
## gets the current rln Epoch time
|
||||||
return calcEpoch(epochTime())
|
return calcEpoch(epochTime())
|
||||||
|
|
||||||
proc compare*(e1, e2: Epoch): int64 =
|
proc diff*(e1, e2: Epoch): int64 =
|
||||||
## returns the difference between the two rln `Epoch`s `e1` and `e2`
|
## returns the difference between the two rln `Epoch`s `e1` and `e2`
|
||||||
## i.e., e1 - e2
|
## i.e., e1 - e2
|
||||||
|
|
||||||
# convert epochs to their corresponding unsigned numerical values
|
# convert epochs to their corresponding unsigned numerical values
|
||||||
let
|
let
|
||||||
epoch1 = fromEpoch(e1)
|
epoch1 = fromEpoch(e1)
|
||||||
epoch2 = fromEpoch(e2)
|
epoch2 = fromEpoch(e2)
|
||||||
return int64(epoch1) - int64(epoch2)
|
return int64(epoch1) - int64(epoch2)
|
||||||
|
|
||||||
proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Option[float64] = none(float64)): MessageValidationResult =
|
proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage,
|
||||||
|
timeOption: Option[float64] = none(float64)): MessageValidationResult =
|
||||||
## validate the supplied `msg` based on the waku-rln-relay routing protocol i.e.,
|
## validate the supplied `msg` based on the waku-rln-relay routing protocol i.e.,
|
||||||
## the `msg`'s epoch is within MAX_EPOCH_GAP of the current epoch
|
## the `msg`'s epoch is within MAX_EPOCH_GAP of the current epoch
|
||||||
## the `msg` has valid rate limit proof
|
## the `msg` has valid rate limit proof
|
||||||
## the `msg` does not violate the rate limit
|
## the `msg` does not violate the rate limit
|
||||||
## `timeOption` indicates Unix epoch time (fractional part holds sub-seconds)
|
## `timeOption` indicates Unix epoch time (fractional part holds sub-seconds)
|
||||||
## if `timeOption` is supplied, then the current epoch is calculated based on that
|
## if `timeOption` is supplied, then the current epoch is calculated based on that
|
||||||
|
|
||||||
|
|
||||||
# checks if the `msg`'s epoch is far from the current epoch
|
# checks if the `msg`'s epoch is far from the current epoch
|
||||||
# it corresponds to the validation of rln external nullifier
|
# it corresponds to the validation of rln external nullifier
|
||||||
var epoch: Epoch
|
var epoch: Epoch
|
||||||
@ -448,77 +472,79 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
|
|||||||
# get current rln epoch
|
# get current rln epoch
|
||||||
epoch = getCurrentEpoch()
|
epoch = getCurrentEpoch()
|
||||||
|
|
||||||
debug "current epoch", currentEpoch=fromEpoch(epoch)
|
debug "current epoch", currentEpoch = fromEpoch(epoch)
|
||||||
let
|
let
|
||||||
msgEpoch = msg.proof.epoch
|
msgEpoch = msg.proof.epoch
|
||||||
# calculate the gaps
|
# calculate the gaps
|
||||||
gap = compare(epoch, msgEpoch)
|
gap = diff(epoch, msgEpoch)
|
||||||
|
|
||||||
debug "message epoch", msgEpoch=fromEpoch(msgEpoch)
|
debug "message epoch", msgEpoch = fromEpoch(msgEpoch)
|
||||||
|
|
||||||
# validate the epoch
|
# validate the epoch
|
||||||
if abs(gap) >= MAX_EPOCH_GAP:
|
if abs(gap) >= MAX_EPOCH_GAP:
|
||||||
# message's epoch is too old or too ahead
|
# message's epoch is too old or too ahead
|
||||||
# accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch
|
# accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch
|
||||||
debug "invalid message: epoch gap exceeds a threshold",gap=gap, payload=string.fromBytes(msg.payload)
|
debug "invalid message: epoch gap exceeds a threshold", gap = gap,
|
||||||
|
payload = string.fromBytes(msg.payload)
|
||||||
return MessageValidationResult.Invalid
|
return MessageValidationResult.Invalid
|
||||||
|
|
||||||
# verify the proof
|
# verify the proof
|
||||||
let
|
let
|
||||||
contentTopicBytes = msg.contentTopic.toBytes
|
contentTopicBytes = msg.contentTopic.toBytes
|
||||||
input = concat(msg.payload, contentTopicBytes)
|
input = concat(msg.payload, contentTopicBytes)
|
||||||
if not rlnPeer.rlnInstance.proofVerify(input, msg.proof):
|
if not rlnPeer.rlnInstance.proofVerify(input, msg.proof):
|
||||||
# invalid proof
|
# invalid proof
|
||||||
debug "invalid message: invalid proof", payload=string.fromBytes(msg.payload)
|
debug "invalid message: invalid proof", payload = string.fromBytes(msg.payload)
|
||||||
return MessageValidationResult.Invalid
|
return MessageValidationResult.Invalid
|
||||||
|
|
||||||
# check if double messaging has happened
|
# check if double messaging has happened
|
||||||
let hasDup = rlnPeer.hasDuplicate(msg)
|
let hasDup = rlnPeer.hasDuplicate(msg)
|
||||||
if hasDup.isOk and hasDup.value == true:
|
if hasDup.isOk and hasDup.value == true:
|
||||||
debug "invalid message: message is a spam", payload=string.fromBytes(msg.payload)
|
debug "invalid message: message is a spam", payload = string.fromBytes(msg.payload)
|
||||||
return MessageValidationResult.Spam
|
return MessageValidationResult.Spam
|
||||||
|
|
||||||
# insert the message to the log
|
# insert the message to the log
|
||||||
# the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e.,
|
# the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e.,
|
||||||
# it will never error out
|
# it will never error out
|
||||||
discard rlnPeer.updateLog(msg)
|
discard rlnPeer.updateLog(msg)
|
||||||
debug "message is valid", payload=string.fromBytes(msg.payload)
|
debug "message is valid", payload = string.fromBytes(msg.payload)
|
||||||
return MessageValidationResult.Valid
|
return MessageValidationResult.Valid
|
||||||
|
|
||||||
|
|
||||||
proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =
|
proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =
|
||||||
## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module
|
## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module
|
||||||
## it extracts the `contentTopic` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence
|
## it extracts the `contentTopic` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence
|
||||||
let
|
let
|
||||||
contentTopicBytes = wakumessage.contentTopic.toBytes
|
contentTopicBytes = wakumessage.contentTopic.toBytes
|
||||||
output = concat(wakumessage.payload, contentTopicBytes)
|
output = concat(wakumessage.payload, contentTopicBytes)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage, senderEpochTime: float64): bool =
|
|
||||||
|
proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage,
|
||||||
|
senderEpochTime: float64): bool =
|
||||||
## returns true if it can create and append a `RateLimitProof` to the supplied `msg`
|
## returns true if it can create and append a `RateLimitProof` to the supplied `msg`
|
||||||
## returns false otherwise
|
## returns false otherwise
|
||||||
## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds.
|
## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds.
|
||||||
## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`)
|
## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`)
|
||||||
|
|
||||||
let input = msg.toRLNSignal()
|
let input = msg.toRLNSignal()
|
||||||
|
|
||||||
var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input,
|
var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input,
|
||||||
memKeys = rlnPeer.membershipKeyPair,
|
memKeys = rlnPeer.membershipKeyPair,
|
||||||
memIndex = rlnPeer.membershipIndex,
|
memIndex = rlnPeer.membershipIndex,
|
||||||
epoch = calcEpoch(senderEpochTime))
|
epoch = calcEpoch(senderEpochTime))
|
||||||
|
|
||||||
if proof.isErr:
|
if proof.isErr:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
msg.proof = proof.value
|
msg.proof = proof.value
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool =
|
proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool =
|
||||||
# add members to the Merkle tree of the `rlnInstance`
|
# add members to the Merkle tree of the `rlnInstance`
|
||||||
for i in 0..list.len-1:
|
for i in 0..list.len-1:
|
||||||
let member = list[i]
|
let member = list[i]
|
||||||
let member_is_added = rlnInstance.insertMember(member)
|
let member_is_added = rlnInstance.insertMember(member)
|
||||||
if not member_is_added:
|
if not member_is_added:
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user