2021-06-08 18:56:32 +00:00
2021-03-23 08:04:51 +00:00
{. used . }
2021-01-07 20:34:24 +00:00
import
2021-11-23 22:48:40 +00:00
std / options , sequtils , times ,
2021-03-26 09:52:04 +00:00
testutils / unittests , chronos , chronicles , stint , web3 ,
2021-02-22 17:40:02 +00:00
stew / byteutils , stew / shims / net as stewNet ,
libp2p / crypto / crypto ,
2021-06-08 18:56:32 +00:00
.. / .. / waku / v2 / protocol / waku_rln_relay / [ rln , waku_rln_relay_utils , waku_rln_relay_types ] ,
2021-02-22 17:40:02 +00:00
.. / .. / waku / v2 / node / wakunode2 ,
2021-02-17 21:54:49 +00:00
.. / test_helpers ,
2021-03-26 09:52:04 +00:00
. / test_utils
2021-02-17 21:54:49 +00:00
2021-09-24 17:32:45 +00:00
const RLNRELAY_PUBSUB_TOPIC = " waku/2/rlnrelay/proto "
2022-02-04 23:58:27 +00:00
const RLNRELAY_CONTENT_TOPIC = " waku/2/rlnrelay/proto "
2021-09-24 17:32:45 +00:00
2021-09-17 17:31:25 +00:00
# POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract:
2021-02-04 20:10:25 +00:00
# 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
2021-09-17 17:31:25 +00:00
const POSEIDON_HASHER_CODE = readFile ( " tests/v2/poseidonHasher.txt " )
# MEMBERSHIP_CONTRACT_CODE contains the bytecode of the membership solidity smart contract:
2021-02-04 20:10:25 +00:00
# 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
2021-09-17 17:31:25 +00:00
const MEMBERSHIP_CONTRACT_CODE = readFile ( " tests/v2/membershipContract.txt " )
2021-02-04 20:10:25 +00:00
# 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);
# }
2021-01-07 20:34:24 +00:00
2021-10-26 21:42:24 +00:00
2021-01-07 20:34:24 +00:00
contract ( MembershipContract ) :
2021-02-04 20:10:25 +00:00
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])
2021-02-17 21:54:49 +00:00
proc uploadContract ( ethClientAddress : string ) : Future [ Address ] {. async . } =
let web3 = await newWeb3 ( ethClientAddress )
debug " web3 connected to " , ethClientAddress
2021-02-04 19:30:00 +00:00
# fetch the list of registered accounts
2021-01-07 20:34:24 +00:00
let accounts = await web3 . provider . eth_accounts ( )
web3 . defaultAccount = accounts [ 1 ]
2021-02-17 21:54:49 +00:00
let add = web3 . defaultAccount
debug " contract deployer account address " , add
2021-01-07 20:34:24 +00:00
var balance = await web3 . provider . eth_getBalance ( web3 . defaultAccount , " latest " )
2021-02-17 21:54:49 +00:00
debug " Initial account balance: " , balance
2021-01-07 20:34:24 +00:00
2021-02-04 20:10:25 +00:00
# deploy the poseidon hash first
let
2021-09-17 17:31:25 +00:00
hasherReceipt = await web3 . deployContract ( POSEIDON_HASHER_CODE )
2021-02-04 20:10:25 +00:00
hasherAddress = hasherReceipt . contractAddress . get
2021-02-17 21:54:49 +00:00
debug " hasher address: " , hasherAddress
2021-02-04 20:10:25 +00:00
2021-02-17 21:54:49 +00:00
2021-02-04 20:10:25 +00:00
# encode membership contract inputs to 32 bytes zero-padded
let
2021-02-17 21:54:49 +00:00
membershipFeeEncoded = encode ( MembershipFee ) . data
2021-09-17 17:31:25 +00:00
depthEncoded = encode ( MERKLE_TREE_DEPTH . u256 ) . data
2021-02-04 20:10:25 +00:00
hasherAddressEncoded = encode ( hasherAddress ) . data
# this is the contract constructor input
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
2021-02-17 21:54:49 +00:00
debug " encoded membership fee: " , membershipFeeEncoded
debug " encoded depth: " , depthEncoded
debug " encoded hasher address: " , hasherAddressEncoded
debug " encoded contract input: " , contractInput
2021-02-04 20:10:25 +00:00
# deploy membership contract with its constructor inputs
2021-09-17 17:31:25 +00:00
let receipt = await web3 . deployContract ( MEMBERSHIP_CONTRACT_CODE , contractInput = contractInput )
2021-01-07 20:34:24 +00:00
var contractAddress = receipt . contractAddress . get
2021-02-17 21:54:49 +00:00
debug " Address of the deployed membership contract: " , contractAddress
2021-01-07 20:34:24 +00:00
2021-02-04 20:10:25 +00:00
# balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest")
2021-02-17 21:54:49 +00:00
# debug "Account balance after the contract deployment: ", balance
2021-01-07 20:34:24 +00:00
await web3 . close ( )
2021-02-17 21:54:49 +00:00
debug " disconnected from " , ethClientAddress
return contractAddress
procSuite " Waku rln relay " :
2022-01-28 21:57:17 +00:00
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 ] )
2022-02-04 23:58:27 +00:00
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 )
2022-01-28 21:57:17 +00:00
let calculatedRoot = node . wakuRlnRelay . rlnInstance . getMerkleRoot ( ) . value ( ) . toHex
debug " calculated root " , calculatedRoot
check expectedRoot = = calculatedRoot
await node . stop ( )
2021-03-22 09:44:45 +00:00
2021-09-17 17:31:25 +00:00
asyncTest " mount waku-rln-relay in the off-chain mode " :
let
nodeKey = crypto . PrivateKey . random ( Secp256k1 , rng [ ] ) [ ]
node = WakuNode . new ( nodeKey , ValidIpAddress . init ( " 0.0.0.0 " ) ,
Port ( 60000 ) )
await node . start ( )
# preparing inputs to mount rln-relay
# create a group of 100 membership keys
let
( groupKeys , root ) = createMembershipList ( 100 )
2021-10-20 00:37:29 +00:00
check groupKeys . len = = 100
let
2021-09-17 17:31:25 +00:00
# convert the keys to MembershipKeyPair structs
groupKeyPairs = groupKeys . toMembershipKeyPairs ( )
# extract the id commitments
groupIDCommitments = groupKeyPairs . mapIt ( it . idCommitment )
debug " groupKeyPairs " , groupKeyPairs
debug " groupIDCommitments " , groupIDCommitments
# index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs
# the corresponding key pair will be used to mount rlnRelay on the current node
# index also represents the index of the leaf in the Merkle tree that contains node's commitment key
2021-09-24 17:32:45 +00:00
let index = MembershipIndex ( 5 )
2021-09-17 17:31:25 +00:00
# -------- mount rln-relay in the off-chain mode
2021-09-28 19:03:53 +00:00
node . mountRelay ( @ [ RLNRELAY_PUBSUB_TOPIC ] )
2022-02-04 23:58:27 +00:00
await node . mountRlnRelay ( groupOpt = some ( groupIDCommitments ) ,
memKeyPairOpt = some ( groupKeyPairs [ index ] ) ,
memIndexOpt = some ( index ) ,
onchainMode = false ,
pubsubTopic = RLNRELAY_PUBSUB_TOPIC ,
contentTopic = RLNRELAY_CONTENT_TOPIC )
2021-09-17 17:31:25 +00:00
# get the root of Merkle tree which is constructed inside the mountRlnRelay proc
let calculatedRoot = node . wakuRlnRelay . rlnInstance . getMerkleRoot ( ) . value ( ) . toHex
debug " calculated root by mountRlnRelay " , calculatedRoot
# this part checks whether the Merkle tree is constructed correctly inside the mountRlnRelay proc
# this check is done by comparing the tree root resulted from mountRlnRelay i.e., calculatedRoot
# against the root which is the expected root
check calculatedRoot = = root
await node . stop ( )
2021-03-24 17:26:56 +00:00
2021-02-17 21:54:49 +00:00
suite " Waku rln relay " :
2021-03-24 17:26:56 +00:00
test " key_gen Nim Wrappers " :
2021-02-17 21:54:49 +00:00
var
merkleDepth : csize_t = 32
# parameters.key contains the parameters related to the Poseidon hasher
# 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
# cargo run --example export_test_keys
# the file is generated separately and copied here
parameters = readFile ( " waku/v2/protocol/waku_rln_relay/parameters.key " )
pbytes = parameters . toBytes ( )
len : csize_t = uint ( pbytes . len )
2021-03-24 17:26:56 +00:00
parametersBuffer = Buffer ( ` ptr ` : addr ( pbytes [ 0 ] ) , len : len )
2021-02-17 21:54:49 +00:00
check :
# check the parameters.key is not empty
pbytes . len ! = 0
var
2021-08-24 19:25:29 +00:00
rlnInstance : RLN [ Bn256 ]
let res = new_circuit_from_params ( merkleDepth , addr parametersBuffer , addr rlnInstance )
2021-02-17 21:54:49 +00:00
check :
# check whether the circuit parameters are generated successfully
res = = true
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
var
2021-03-24 17:26:56 +00:00
keysBuffer : Buffer
keysBufferPtr = addr ( keysBuffer )
2021-08-24 19:25:29 +00:00
done = key_gen ( rlnInstance , keysBufferPtr )
2021-02-17 21:54:49 +00:00
check :
# check whether the keys are generated successfully
done = = true
if done :
var generatedKeys = cast [ ptr array [ 64 , byte ] ] ( keysBufferPtr . ` ptr ` ) [ ]
check :
# the public and secret keys together are 64 bytes
generatedKeys . len = = 64
2021-02-18 22:59:10 +00:00
debug " generated keys: " , generatedKeys
2021-06-08 18:56:32 +00:00
2021-02-18 22:59:10 +00:00
test " membership Key Gen " :
2021-03-24 17:26:56 +00:00
# create an RLN instance
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-03-24 17:26:56 +00:00
2021-08-24 19:25:29 +00:00
var key = membershipKeyGen ( rlnInstance . value )
2021-02-18 22:59:10 +00:00
var empty : array [ 32 , byte ]
check :
key . isSome
2021-08-26 23:14:51 +00:00
key . get ( ) . idKey . len = = 32
key . get ( ) . idCommitment . len = = 32
key . get ( ) . idKey ! = empty
key . get ( ) . idCommitment ! = empty
2021-02-18 22:59:10 +00:00
2021-03-24 17:26:56 +00:00
debug " the generated membership key pair: " , key
2021-06-08 18:56:32 +00:00
2021-03-24 17:26:56 +00:00
test " get_root Nim binding " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-03-24 17:26:56 +00:00
# read the Merkle Tree root
var
root1 {. noinit . } : Buffer = Buffer ( )
rootPtr1 = addr ( root1 )
2021-08-24 19:25:29 +00:00
get_root_successful1 = get_root ( rlnInstance . value , rootPtr1 )
2021-10-26 21:42:24 +00:00
check :
get_root_successful1
root1 . len = = 32
2021-03-24 17:26:56 +00:00
# read the Merkle Tree root
var
root2 {. noinit . } : Buffer = Buffer ( )
rootPtr2 = addr ( root2 )
2021-08-24 19:25:29 +00:00
get_root_successful2 = get_root ( rlnInstance . value , rootPtr2 )
2021-10-26 21:42:24 +00:00
check :
get_root_successful2
root2 . len = = 32
2021-03-24 17:26:56 +00:00
var rootValue1 = cast [ ptr array [ 32 , byte ] ] ( root1 . ` ptr ` )
let rootHex1 = rootValue1 [ ] . toHex
var rootValue2 = cast [ ptr array [ 32 , byte ] ] ( root2 . ` ptr ` )
let rootHex2 = rootValue2 [ ] . toHex
2021-08-26 23:14:51 +00:00
# the two roots must be identical
2021-10-26 21:42:24 +00:00
check rootHex1 = = rootHex2
2021-08-26 23:14:51 +00:00
test " getMerkleRoot utils " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-26 23:14:51 +00:00
check :
rlnInstance . isOk = = true
# read the Merkle Tree root
var root1 = getMerkleRoot ( rlnInstance . value ( ) )
2021-10-26 21:42:24 +00:00
check root1 . isOk
2021-08-26 23:14:51 +00:00
let rootHex1 = root1 . value ( ) . toHex
# read the Merkle Tree root
var root2 = getMerkleRoot ( rlnInstance . value ( ) )
2021-10-26 21:42:24 +00:00
check root2 . isOk
2021-08-26 23:14:51 +00:00
let rootHex2 = root2 . value ( ) . toHex
2021-03-24 17:26:56 +00:00
# the two roots must be identical
2021-10-26 21:42:24 +00:00
check rootHex1 = = rootHex2
2021-03-24 17:26:56 +00:00
test " update_next_member Nim Wrapper " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-03-24 17:26:56 +00:00
# generate a key pair
2021-08-24 19:25:29 +00:00
var keypair = membershipKeyGen ( rlnInstance . value )
2021-10-26 21:42:24 +00:00
check keypair . isSome ( )
var pkBuffer = toBuffer ( keypair . get ( ) . idCommitment )
2021-03-24 17:26:56 +00:00
let pkBufferPtr = addr pkBuffer
# add the member to the tree
2021-08-24 19:25:29 +00:00
var member_is_added = update_next_member ( rlnInstance . value , pkBufferPtr )
2021-03-24 17:26:56 +00:00
check :
member_is_added = = true
2021-06-08 18:56:32 +00:00
2021-03-24 17:26:56 +00:00
test " delete_member Nim wrapper " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-03-24 17:26:56 +00:00
# delete the first member
2021-09-24 17:32:45 +00:00
var deleted_member_index = MembershipIndex ( 0 )
2021-08-24 19:25:29 +00:00
let deletion_success = delete_member ( rlnInstance . value , deleted_member_index )
2021-10-26 21:42:24 +00:00
check deletion_success
2021-06-08 18:56:32 +00:00
2021-08-26 23:14:51 +00:00
test " insertMember rln utils " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-26 23:14:51 +00:00
check :
rlnInstance . isOk = = true
var rln = rlnInstance . value
# generate a key pair
var keypair = rln . membershipKeyGen ( )
2021-10-26 21:42:24 +00:00
check keypair . isSome ( )
2021-08-26 23:14:51 +00:00
check :
rln . insertMember ( keypair . get ( ) . idCommitment )
test " removeMember rln utils " :
# create an RLN instance which also includes an empty Merkle tree
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-26 23:14:51 +00:00
check :
rlnInstance . isOk = = true
var rln = rlnInstance . value
check :
2021-09-24 17:32:45 +00:00
rln . removeMember ( MembershipIndex ( 0 ) )
2021-08-26 23:14:51 +00:00
2021-03-24 17:26:56 +00:00
test " Merkle tree consistency check between deletion and insertion " :
# create an RLN instance
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-03-24 17:26:56 +00:00
# read the Merkle Tree root
var
root1 {. noinit . } : Buffer = Buffer ( )
rootPtr1 = addr ( root1 )
2021-08-24 19:25:29 +00:00
get_root_successful1 = get_root ( rlnInstance . value , rootPtr1 )
2021-10-26 21:42:24 +00:00
check :
get_root_successful1
root1 . len = = 32
2021-03-24 17:26:56 +00:00
# generate a key pair
2021-08-24 19:25:29 +00:00
var keypair = membershipKeyGen ( rlnInstance . value )
2021-10-26 21:42:24 +00:00
check : keypair . isSome ( )
var pkBuffer = toBuffer ( keypair . get ( ) . idCommitment )
2021-03-24 17:26:56 +00:00
let pkBufferPtr = addr pkBuffer
# add the member to the tree
2021-08-24 19:25:29 +00:00
var member_is_added = update_next_member ( rlnInstance . value , pkBufferPtr )
2021-10-26 21:42:24 +00:00
check member_is_added
2021-03-24 17:26:56 +00:00
# read the Merkle Tree root after insertion
var
root2 {. noinit . } : Buffer = Buffer ( )
rootPtr2 = addr ( root2 )
2021-08-24 19:25:29 +00:00
get_root_successful2 = get_root ( rlnInstance . value , rootPtr2 )
2021-10-26 21:42:24 +00:00
check :
get_root_successful2
root2 . len = = 32
2021-03-24 17:26:56 +00:00
# delete the first member
2021-09-24 17:32:45 +00:00
var deleted_member_index = MembershipIndex ( 0 )
2021-08-24 19:25:29 +00:00
let deletion_success = delete_member ( rlnInstance . value , deleted_member_index )
2021-10-26 21:42:24 +00:00
check deletion_success
2021-03-24 17:26:56 +00:00
# read the Merkle Tree root after the deletion
var
root3 {. noinit . } : Buffer = Buffer ( )
rootPtr3 = addr ( root3 )
2021-08-24 19:25:29 +00:00
get_root_successful3 = get_root ( rlnInstance . value , rootPtr3 )
2021-10-26 21:42:24 +00:00
check :
get_root_successful3
root3 . len = = 32
2021-03-24 17:26:56 +00:00
var rootValue1 = cast [ ptr array [ 32 , byte ] ] ( root1 . ` ptr ` )
let rootHex1 = rootValue1 [ ] . toHex
debug " The initial root " , rootHex1
var rootValue2 = cast [ ptr array [ 32 , byte ] ] ( root2 . ` ptr ` )
let rootHex2 = rootValue2 [ ] . toHex
debug " The root after insertion " , rootHex2
var rootValue3 = cast [ ptr array [ 32 , byte ] ] ( root3 . ` ptr ` )
let rootHex3 = rootValue3 [ ] . toHex
debug " The root after deletion " , rootHex3
# the root must change after the insertion
2021-10-26 21:42:24 +00:00
check : not ( rootHex1 = = rootHex2 )
2021-03-24 17:26:56 +00:00
## The initial root of the tree (empty tree) must be identical to
## the root of the tree after one insertion followed by a deletion
2021-10-26 21:42:24 +00:00
check rootHex1 = = rootHex3
2021-08-26 23:14:51 +00:00
test " Merkle tree consistency check between deletion and insertion using rln utils " :
# create an RLN instance
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-26 23:14:51 +00:00
check :
rlnInstance . isOk = = true
var rln = rlnInstance . value ( )
# read the Merkle Tree root
var root1 = rln . getMerkleRoot ( )
2021-10-26 21:42:24 +00:00
check root1 . isOk
2021-08-26 23:14:51 +00:00
let rootHex1 = root1 . value ( ) . toHex ( )
# generate a key pair
var keypair = rln . membershipKeyGen ( )
2021-10-26 21:42:24 +00:00
check keypair . isSome ( )
2021-08-26 23:14:51 +00:00
let member_inserted = rln . insertMember ( keypair . get ( ) . idCommitment )
check member_inserted
# read the Merkle Tree root after insertion
var root2 = rln . getMerkleRoot ( )
2021-10-26 21:42:24 +00:00
check root2 . isOk
2021-08-26 23:14:51 +00:00
let rootHex2 = root2 . value ( ) . toHex ( )
# delete the first member
2021-09-24 17:32:45 +00:00
var deleted_member_index = MembershipIndex ( 0 )
2021-08-26 23:14:51 +00:00
let deletion_success = rln . removeMember ( deleted_member_index )
2021-10-26 21:42:24 +00:00
check deletion_success
2021-08-26 23:14:51 +00:00
# read the Merkle Tree root after the deletion
var root3 = rln . getMerkleRoot ( )
2021-10-26 21:42:24 +00:00
check root3 . isOk
2021-08-26 23:14:51 +00:00
let rootHex3 = root3 . value ( ) . toHex ( )
debug " The initial root " , rootHex1
debug " The root after insertion " , rootHex2
debug " The root after deletion " , rootHex3
# the root must change after the insertion
2021-10-26 21:42:24 +00:00
check not ( rootHex1 = = rootHex2 )
2021-08-26 23:14:51 +00:00
## The initial root of the tree (empty tree) must be identical to
## the root of the tree after one insertion followed by a deletion
2021-10-26 21:42:24 +00:00
check rootHex1 = = rootHex3
2021-08-26 23:14:51 +00:00
2021-04-01 00:39:27 +00:00
test " hash Nim Wrappers " :
# create an RLN instance
2021-09-17 17:31:25 +00:00
var rlnInstance = createRLNInstance ( )
2021-08-24 19:25:29 +00:00
check :
rlnInstance . isOk = = true
2021-04-01 00:39:27 +00:00
# prepare the input
var
2021-10-26 21:42:24 +00:00
msg = " Hello " . toBytes ( )
hashInput = appendLength ( msg )
hashInputBuffer = toBuffer ( hashInput )
2021-04-01 00:39:27 +00:00
# prepare other inputs to the hash function
2021-10-26 21:42:24 +00:00
var outputBuffer : Buffer
2021-04-01 00:39:27 +00:00
2021-10-26 21:42:24 +00:00
let hashSuccess = hash ( rlnInstance . value , addr hashInputBuffer , addr outputBuffer )
check hashSuccess
2021-04-01 00:39:27 +00:00
let outputArr = cast [ ptr array [ 32 , byte ] ] ( outputBuffer . ` ptr ` ) [ ]
2021-10-26 21:42:24 +00:00
check :
" efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d " = = outputArr . toHex ( )
2021-04-01 00:39:27 +00:00
var
hashOutput = cast [ ptr array [ 32 , byte ] ] ( outputBuffer . ` ptr ` ) [ ]
hashOutputHex = hashOutput . toHex ( )
debug " hash output " , hashOutputHex
2021-10-20 00:37:29 +00:00
test " hash utils " :
# create an RLN instance
var rlnInstance = createRLNInstance ( )
check :
rlnInstance . isOk = = true
let rln = rlnInstance . value
# prepare the input
2021-10-26 21:42:24 +00:00
let msg = " Hello " . toBytes ( )
2021-04-01 00:39:27 +00:00
2021-10-26 21:42:24 +00:00
let hash = rln . hash ( msg )
2021-08-24 19:25:29 +00:00
check :
2021-10-26 21:42:24 +00:00
" efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d " = = hash . toHex ( )
2021-09-17 17:31:25 +00:00
test " create a list of membership keys and construct a Merkle tree based on the list " :
let
groupSize = 100
( list , root ) = createMembershipList ( groupSize )
debug " created membership key list " , list
debug " the Merkle tree root " , root
check :
list . len = = groupSize # check the number of keys
root . len = = HASH_HEX_SIZE # check the size of the calculated tree root
test " check correctness of toMembershipKeyPairs and calcMerkleRoot " :
let groupKeys = STATIC_GROUP_KEYS
# create a set of MembershipKeyPair objects from groupKeys
let groupKeyPairs = groupKeys . toMembershipKeyPairs ( )
# extract the id commitments
let groupIDCommitments = groupKeyPairs . mapIt ( it . idCommitment )
# calculate the Merkle tree root out of the extracted id commitments
let root = calcMerkleRoot ( groupIDCommitments )
debug " groupKeyPairs " , groupKeyPairs
debug " groupIDCommitments " , groupIDCommitments
debug " root " , root
check :
# check that the correct number of key pairs is created
groupKeyPairs . len = = StaticGroupSize
# compare the calculated root against the correct root
2021-10-20 00:37:29 +00:00
root = = STATIC_GROUP_MERKLE_ROOT
test " RateLimitProof Protobuf encode/init test " :
var
proof : ZKSNARK
merkleRoot : MerkleNode
epoch : Epoch
shareX : MerkleNode
shareY : MerkleNode
nullifier : Nullifier
# populate fields with dummy values
for x in proof . mitems : x = 1
for x in merkleRoot . mitems : x = 2
for x in epoch . mitems : x = 3
for x in shareX . mitems : x = 4
for x in shareY . mitems : x = 5
for x in nullifier . mitems : x = 6
let
2021-10-26 21:42:24 +00:00
rateLimitProof = RateLimitProof ( proof : proof ,
2021-10-20 00:37:29 +00:00
merkleRoot : merkleRoot ,
epoch : epoch ,
shareX : shareX ,
shareY : shareY ,
nullifier : nullifier )
2021-10-26 21:42:24 +00:00
protobuf = rateLimitProof . encode ( )
2021-10-20 00:37:29 +00:00
decodednsp = RateLimitProof . init ( protobuf . buffer )
check :
decodednsp . isErr = = false
2021-10-26 21:42:24 +00:00
decodednsp . value = = rateLimitProof
2021-10-20 00:37:29 +00:00
test " test proofVerify and proofGen for a valid proof " :
var rlnInstance = createRLNInstance ( )
2021-10-26 21:42:24 +00:00
check rlnInstance . isOk
2021-10-20 00:37:29 +00:00
var rln = rlnInstance . value
let
# create a membership key pair
memKeys = membershipKeyGen ( rln ) . get ( )
# peer's index in the Merkle Tree
index = 5
# Create a Merkle tree with random members
for i in 0 .. 10 :
var member_is_added : bool = false
if ( i = = index ) :
# insert the current peer's pk
member_is_added = rln . insertMember ( memKeys . idCommitment )
else :
# create a new key pair
let memberKeys = rln . membershipKeyGen ( )
member_is_added = rln . insertMember ( memberKeys . get ( ) . idCommitment )
# check the member is added
2021-10-26 21:42:24 +00:00
check member_is_added
2021-10-20 00:37:29 +00:00
# prepare the message
2021-10-26 21:42:24 +00:00
let messageBytes = " Hello " . toBytes ( )
2021-10-20 00:37:29 +00:00
# prepare the epoch
var epoch : Epoch
debug " epoch " , epochHex = epoch . toHex ( )
# generate proof
2021-10-26 21:42:24 +00:00
let proofRes = rln . proofGen ( data = messageBytes ,
memKeys = memKeys ,
memIndex = MembershipIndex ( index ) ,
epoch = epoch )
check proofRes . isOk ( )
2021-10-20 00:37:29 +00:00
let proof = proofRes . value
# verify the proof
let verified = rln . proofVerify ( data = messageBytes ,
2021-10-26 21:42:24 +00:00
proof = proof )
2021-10-20 00:37:29 +00:00
check verified = = true
test " test proofVerify and proofGen for an invalid proof " :
var rlnInstance = createRLNInstance ( )
check :
rlnInstance . isOk = = true
var rln = rlnInstance . value
let
# create a membership key pair
memKeys = membershipKeyGen ( rln ) . get ( )
# peer's index in the Merkle Tree
index = 5
# Create a Merkle tree with random members
for i in 0 .. 10 :
var member_is_added : bool = false
if ( i = = index ) :
# insert the current peer's pk
member_is_added = rln . insertMember ( memKeys . idCommitment )
else :
# create a new key pair
let memberKeys = rln . membershipKeyGen ( )
member_is_added = rln . insertMember ( memberKeys . get ( ) . idCommitment )
# check the member is added
2021-10-26 21:42:24 +00:00
check member_is_added
2021-10-20 00:37:29 +00:00
2021-10-26 21:42:24 +00:00
# prepare the message
let messageBytes = " Hello " . toBytes ( )
2021-10-20 00:37:29 +00:00
# prepare the epoch
var epoch : Epoch
debug " epoch in bytes " , epochHex = epoch . toHex ( )
let badIndex = 4
# generate proof
2021-10-26 21:42:24 +00:00
let proofRes = rln . proofGen ( data = messageBytes ,
memKeys = memKeys ,
memIndex = MembershipIndex ( badIndex ) ,
epoch = epoch )
check proofRes . isOk ( )
2021-10-20 00:37:29 +00:00
let proof = proofRes . value
# verify the proof (should not be verified)
let verified = rln . proofVerify ( data = messageBytes ,
proof = proof )
check verified = = false
2021-11-23 22:48:40 +00:00
test " toEpoch and fromEpoch consistency check " :
# check edge cases
let
time = uint64 . high
epoch = time . toEpoch ( )
decodedTime = epoch . fromEpoch ( )
check time = = decodedTime
debug " encoded and decode time " , time = time , epoch = epoch , decodedTime = decodedTime
test " Epoch comparison " :
# check edge cases
let
time1 = uint64 . high
time2 = uint64 . high - 1
epoch1 = time1 . toEpoch ( )
epoch2 = time2 . toEpoch ( )
check compare ( epoch1 , epoch2 ) = = int64 ( 1 )
check compare ( epoch2 , epoch1 ) = = int64 ( - 1 )
test " updateLog and hasDuplicate tests " :
let
wakurlnrelay = WakuRLNRelay ( )
epoch = getCurrentEpoch ( )
# cretae some dummy nullifiers and secret shares
var nullifier1 : Nullifier
for index , x in nullifier1 . mpairs : nullifier1 [ index ] = 1
var shareX1 : MerkleNode
for index , x in shareX1 . mpairs : shareX1 [ index ] = 1
let shareY1 = shareX1
var nullifier2 : Nullifier
for index , x in nullifier2 . mpairs : nullifier2 [ index ] = 2
var shareX2 : MerkleNode
for index , x in shareX2 . mpairs : shareX2 [ index ] = 2
let shareY2 = shareX2
let nullifier3 = nullifier1
var shareX3 : MerkleNode
for index , x in shareX3 . mpairs : shareX3 [ index ] = 3
let shareY3 = shareX3
let
wm1 = WakuMessage ( proof : RateLimitProof ( epoch : epoch , nullifier : nullifier1 , shareX : shareX1 , shareY : shareY1 ) )
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
# no duplicate for wm1 should be found, since the log is empty
let result1 = wakurlnrelay . hasDuplicate ( wm1 )
check :
result1 . isOk
# no duplicate is found
result1 . value = = false
# add it to the log
discard wakurlnrelay . updateLog ( wm1 )
# # no duplicate for wm2 should be found, its nullifier differs from wm1
let result2 = wakurlnrelay . hasDuplicate ( wm2 )
check :
result2 . isOk
# no duplicate is found
result2 . value = = false
# add it to the log
discard wakurlnrelay . updateLog ( wm2 )
# wm3 has the same nullifier as wm1 but different secret shares, it should be detected as duplicate
let result3 = wakurlnrelay . hasDuplicate ( wm3 )
check :
result3 . isOk
# it is a duplicate
result3 . value = = true
test " validateMessage test " :
# setup a wakurlnrelay peer with a static group----------
# create a group of 100 membership keys
let
( groupKeys , root ) = createMembershipList ( 100 )
# convert the keys to MembershipKeyPair structs
groupKeyPairs = groupKeys . toMembershipKeyPairs ( )
# extract the id commitments
groupIDCommitments = groupKeyPairs . mapIt ( it . idCommitment )
debug " groupKeyPairs " , groupKeyPairs
debug " groupIDCommitments " , groupIDCommitments
# index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs
# the corresponding key pair will be used to mount rlnRelay on the current node
# index also represents the index of the leaf in the Merkle tree that contains node's commitment key
let index = MembershipIndex ( 5 )
# create an RLN instance
var rlnInstance = createRLNInstance ( )
doAssert ( rlnInstance . isOk )
var rln = rlnInstance . value
# add members
discard rln . addAll ( groupIDCommitments )
let
wakuRlnRelay = WakuRLNRelay ( membershipIndex : index , membershipKeyPair : groupKeyPairs [ index ] , rlnInstance : rln )
# get the current epoch time
let time = epochTime ( )
# create some messages from the same peer and append rln proof to them, except wm4
var
wm1 = WakuMessage ( payload : " Valid message " . toBytes ( ) )
proofAdded1 = wakuRlnRelay . appendRLNProof ( wm1 , time )
# another message in the same epoch as wm1, it will break the messaging rate limit
wm2 = WakuMessage ( payload : " Spam " . toBytes ( ) )
proofAdded2 = wakuRlnRelay . appendRLNProof ( wm2 , time )
# wm3 points to the next epoch
wm3 = WakuMessage ( payload : " Valid message " . toBytes ( ) )
proofAdded3 = wakuRlnRelay . appendRLNProof ( wm3 , time + EPOCH_UNIT_SECONDS )
wm4 = WakuMessage ( payload : " Invalid message " . toBytes ( ) )
# checks proofs are added
check :
proofAdded1
proofAdded2
proofAdded3
# validate messages
# validateMessage proc checks the validity of the message fields and adds it to the log (if valid)
let
2022-01-28 21:57:17 +00:00
msgValidate1 = wakuRlnRelay . validateMessage ( wm1 , some ( time ) )
2021-11-23 22:48:40 +00:00
# wm2 is published within the same Epoch as wm1 and should be found as spam
2022-01-28 21:57:17 +00:00
msgValidate2 = wakuRlnRelay . validateMessage ( wm2 , some ( time ) )
2021-11-23 22:48:40 +00:00
# a valid message should be validated successfully
2022-01-28 21:57:17 +00:00
msgValidate3 = wakuRlnRelay . validateMessage ( wm3 , some ( time ) )
2021-11-23 22:48:40 +00:00
# wm4 has no rln proof and should not be validated
2022-01-28 21:57:17 +00:00
msgValidate4 = wakuRlnRelay . validateMessage ( wm4 , some ( time ) )
2021-11-23 22:48:40 +00:00
check :
msgValidate1 = = MessageValidationResult . Valid
msgValidate2 = = MessageValidationResult . Spam
msgValidate3 = = MessageValidationResult . Valid
msgValidate4 = = MessageValidationResult . Invalid