mirror of https://github.com/waku-org/nwaku.git
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
b2eb992bd3
commit
47a9fcfd77
|
@ -27,6 +27,8 @@ import
|
|||
|
||||
when defined(rln):
|
||||
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
|
||||
|
|
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,
|
||||
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],
|
||||
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils,
|
||||
waku_rln_relay_types],
|
||||
../../waku/v2/node/wakunode2,
|
||||
../test_helpers,
|
||||
./test_utils
|
||||
|
@ -14,258 +15,7 @@ import
|
|||
const RLNRELAY_PUBSUB_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":
|
||||
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":
|
||||
let
|
||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||
|
@ -278,7 +28,8 @@ procSuite "Waku rln relay":
|
|||
# create a group of 100 membership keys
|
||||
let
|
||||
(groupKeys, root) = createMembershipList(100)
|
||||
check groupKeys.len == 100
|
||||
check:
|
||||
groupKeys.len == 100
|
||||
let
|
||||
# convert the keys to MembershipKeyPair structs
|
||||
groupKeyPairs = groupKeys.toMembershipKeyPairs()
|
||||
|
@ -308,7 +59,8 @@ procSuite "Waku rln relay":
|
|||
# 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
|
||||
check:
|
||||
calculatedRoot == root
|
||||
|
||||
await node.stop()
|
||||
|
||||
|
@ -331,7 +83,8 @@ suite "Waku rln relay":
|
|||
|
||||
var
|
||||
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 whether the circuit parameters are generated successfully
|
||||
res == true
|
||||
|
@ -400,7 +153,8 @@ suite "Waku rln relay":
|
|||
let rootHex2 = rootValue2[].toHex
|
||||
|
||||
# the two roots must be identical
|
||||
check rootHex1 == rootHex2
|
||||
check:
|
||||
rootHex1 == rootHex2
|
||||
test "getMerkleRoot utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -409,16 +163,19 @@ suite "Waku rln relay":
|
|||
|
||||
# read the Merkle Tree root
|
||||
var root1 = getMerkleRoot(rlnInstance.value())
|
||||
check root1.isOk
|
||||
check:
|
||||
root1.isOk
|
||||
let rootHex1 = root1.value().toHex
|
||||
|
||||
# read the Merkle Tree root
|
||||
var root2 = getMerkleRoot(rlnInstance.value())
|
||||
check root2.isOk
|
||||
check:
|
||||
root2.isOk
|
||||
let rootHex2 = root2.value().toHex
|
||||
|
||||
# the two roots must be identical
|
||||
check rootHex1 == rootHex2
|
||||
check:
|
||||
rootHex1 == rootHex2
|
||||
|
||||
test "update_next_member Nim Wrapper":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
|
@ -428,7 +185,8 @@ suite "Waku rln relay":
|
|||
|
||||
# generate a key pair
|
||||
var keypair = membershipKeyGen(rlnInstance.value)
|
||||
check keypair.isSome()
|
||||
check:
|
||||
keypair.isSome()
|
||||
var pkBuffer = toBuffer(keypair.get().idCommitment)
|
||||
let pkBufferPtr = addr pkBuffer
|
||||
|
||||
|
@ -446,7 +204,8 @@ suite "Waku rln relay":
|
|||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
check deletion_success
|
||||
check:
|
||||
deletion_success
|
||||
|
||||
test "insertMember rln utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
|
@ -456,7 +215,8 @@ suite "Waku rln relay":
|
|||
var rln = rlnInstance.value
|
||||
# generate a key pair
|
||||
var keypair = rln.membershipKeyGen()
|
||||
check keypair.isSome()
|
||||
check:
|
||||
keypair.isSome()
|
||||
check:
|
||||
rln.insertMember(keypair.get().idCommitment)
|
||||
|
||||
|
@ -492,7 +252,8 @@ suite "Waku rln relay":
|
|||
|
||||
# add the member to the tree
|
||||
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
|
||||
var
|
||||
|
@ -506,7 +267,8 @@ suite "Waku rln relay":
|
|||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
check deletion_success
|
||||
check:
|
||||
deletion_success
|
||||
|
||||
# read the Merkle Tree root after the deletion
|
||||
var
|
||||
|
@ -534,7 +296,8 @@ suite "Waku rln relay":
|
|||
|
||||
## The initial root of the tree (empty tree) must be identical to
|
||||
## the root of the tree after one insertion followed by a deletion
|
||||
check rootHex1 == rootHex3
|
||||
check:
|
||||
rootHex1 == rootHex3
|
||||
test "Merkle tree consistency check between deletion and insertion using rln utils":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -544,29 +307,35 @@ suite "Waku rln relay":
|
|||
|
||||
# read the Merkle Tree root
|
||||
var root1 = rln.getMerkleRoot()
|
||||
check root1.isOk
|
||||
check:
|
||||
root1.isOk
|
||||
let rootHex1 = root1.value().toHex()
|
||||
|
||||
# generate a key pair
|
||||
var keypair = rln.membershipKeyGen()
|
||||
check keypair.isSome()
|
||||
check:
|
||||
keypair.isSome()
|
||||
let member_inserted = rln.insertMember(keypair.get().idCommitment)
|
||||
check member_inserted
|
||||
check:
|
||||
member_inserted
|
||||
|
||||
# read the Merkle Tree root after insertion
|
||||
var root2 = rln.getMerkleRoot()
|
||||
check root2.isOk
|
||||
check:
|
||||
root2.isOk
|
||||
let rootHex2 = root2.value().toHex()
|
||||
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = rln.removeMember(deleted_member_index)
|
||||
check deletion_success
|
||||
check:
|
||||
deletion_success
|
||||
|
||||
# read the Merkle Tree root after the deletion
|
||||
var root3 = rln.getMerkleRoot()
|
||||
check root3.isOk
|
||||
check:
|
||||
root3.isOk
|
||||
let rootHex3 = root3.value().toHex()
|
||||
|
||||
|
||||
|
@ -575,11 +344,13 @@ suite "Waku rln relay":
|
|||
debug "The root after deletion", rootHex3
|
||||
|
||||
# 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 root of the tree after one insertion followed by a deletion
|
||||
check rootHex1 == rootHex3
|
||||
check:
|
||||
rootHex1 == rootHex3
|
||||
|
||||
test "hash Nim Wrappers":
|
||||
# create an RLN instance
|
||||
|
@ -596,11 +367,14 @@ suite "Waku rln relay":
|
|||
# prepare other inputs to the hash function
|
||||
var outputBuffer: Buffer
|
||||
|
||||
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer, addr outputBuffer)
|
||||
check hashSuccess
|
||||
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer,
|
||||
addr outputBuffer)
|
||||
check:
|
||||
hashSuccess
|
||||
let outputArr = cast[ptr array[32, byte]](outputBuffer.`ptr`)[]
|
||||
check:
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == outputArr.toHex()
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||
outputArr.toHex()
|
||||
|
||||
var
|
||||
hashOutput = cast[ptr array[32, byte]] (outputBuffer.`ptr`)[]
|
||||
|
@ -620,7 +394,8 @@ suite "Waku rln relay":
|
|||
|
||||
let hash = rln.hash(msg)
|
||||
check:
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == hash.toHex()
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||
hash.toHex()
|
||||
|
||||
test "create a list of membership keys and construct a Merkle tree based on the list":
|
||||
let
|
||||
|
@ -686,7 +461,8 @@ suite "Waku rln relay":
|
|||
|
||||
test "test proofVerify and proofGen for a valid proof":
|
||||
var rlnInstance = createRLNInstance()
|
||||
check rlnInstance.isOk
|
||||
check:
|
||||
rlnInstance.isOk
|
||||
var rln = rlnInstance.value
|
||||
|
||||
let
|
||||
|
@ -706,7 +482,8 @@ suite "Waku rln relay":
|
|||
let memberKeys = rln.membershipKeyGen()
|
||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||
# check the member is added
|
||||
check member_is_added
|
||||
check:
|
||||
member_is_added
|
||||
|
||||
# prepare the message
|
||||
let messageBytes = "Hello".toBytes()
|
||||
|
@ -720,13 +497,15 @@ suite "Waku rln relay":
|
|||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(index),
|
||||
epoch = epoch)
|
||||
check proofRes.isOk()
|
||||
check:
|
||||
proofRes.isOk()
|
||||
let proof = proofRes.value
|
||||
|
||||
# verify the proof
|
||||
let verified = rln.proofVerify(data = messageBytes,
|
||||
proof = proof)
|
||||
check verified == true
|
||||
check:
|
||||
verified == true
|
||||
|
||||
test "test proofVerify and proofGen for an invalid proof":
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -751,7 +530,8 @@ suite "Waku rln relay":
|
|||
let memberKeys = rln.membershipKeyGen()
|
||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||
# check the member is added
|
||||
check member_is_added
|
||||
check:
|
||||
member_is_added
|
||||
|
||||
# prepare the message
|
||||
let messageBytes = "Hello".toBytes()
|
||||
|
@ -767,21 +547,25 @@ suite "Waku rln relay":
|
|||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(badIndex),
|
||||
epoch = epoch)
|
||||
check proofRes.isOk()
|
||||
check:
|
||||
proofRes.isOk()
|
||||
let proof = proofRes.value
|
||||
|
||||
# verify the proof (should not be verified)
|
||||
let verified = rln.proofVerify(data = messageBytes,
|
||||
proof = proof)
|
||||
check verified == false
|
||||
check:
|
||||
verified == false
|
||||
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
|
||||
epoch = uint64.high # rln epoch
|
||||
epochBytes = epoch.toEpoch()
|
||||
decodedEpoch = epochBytes.fromEpoch()
|
||||
check:
|
||||
epoch == decodedEpoch
|
||||
debug "encoded and decode time", epoch = epoch, epochBytes = epochBytes,
|
||||
decodedEpoch = decodedEpoch
|
||||
|
||||
test "Epoch comparison":
|
||||
# check edge cases
|
||||
|
@ -790,8 +574,9 @@ suite "Waku rln relay":
|
|||
time2 = uint64.high - 1
|
||||
epoch1 = time1.toEpoch()
|
||||
epoch2 = time2.toEpoch()
|
||||
check compare(epoch1, epoch2) == int64(1)
|
||||
check compare(epoch2, epoch1) == int64(-1)
|
||||
check:
|
||||
diff(epoch1, epoch2) == int64(1)
|
||||
diff(epoch2, epoch1) == int64(-1)
|
||||
|
||||
test "updateLog and hasDuplicate tests":
|
||||
let
|
||||
|
@ -817,9 +602,12 @@ suite "Waku rln relay":
|
|||
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))
|
||||
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
|
||||
|
@ -874,7 +662,8 @@ suite "Waku rln relay":
|
|||
discard rln.addAll(groupIDCommitments)
|
||||
|
||||
let
|
||||
wakuRlnRelay = WakuRLNRelay(membershipIndex: index, membershipKeyPair: groupKeyPairs[index], rlnInstance: rln)
|
||||
wakuRlnRelay = WakuRLNRelay(membershipIndex: index,
|
||||
membershipKeyPair: groupKeyPairs[index], rlnInstance: rln)
|
||||
|
||||
# get the current epoch time
|
||||
let time = epochTime()
|
||||
|
|
|
@ -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()
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,8 @@ logScope:
|
|||
type RLNResult* = Result[RLN[Bn256], string]
|
||||
type MerkleNodeResult* = Result[MerkleNode, 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
|
||||
contract(MembershipContract):
|
||||
|
@ -52,7 +53,8 @@ proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
|||
return err("error in parameters.key")
|
||||
|
||||
# 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
|
||||
if (res == false):
|
||||
debug "error in parameters generation"
|
||||
|
@ -91,17 +93,24 @@ proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
|||
|
||||
|
||||
return some(keypair)
|
||||
|
||||
proc toUInt256*(idCommitment: IDCommitment): UInt256 =
|
||||
let pk = cast[UInt256](idCommitment)
|
||||
return pk
|
||||
|
||||
proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
|
||||
## registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
|
||||
## into the membership contract whose address is in rlnPeer.membershipContractAddress
|
||||
let web3 = await newWeb3(rlnPeer.ethClientAddress)
|
||||
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
|
||||
web3.privateKey = rlnPeer.ethAccountPrivateKey
|
||||
var sender = web3.contractSender(MembershipContract, rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
||||
let pk = cast[UInt256](rlnPeer.membershipKeyPair.idCommitment)
|
||||
discard await sender.register(pk).send(MembershipFee)
|
||||
var sender = web3.contractSender(MembershipContract,
|
||||
rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address
|
||||
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
|
||||
await web3.close()
|
||||
return true
|
||||
|
@ -140,7 +149,8 @@ proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode =
|
|||
|
||||
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
|
||||
## 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
|
||||
|
@ -150,7 +160,9 @@ proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch, msg: openA
|
|||
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
||||
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
|
||||
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||
|
@ -219,7 +231,8 @@ proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
|||
|
||||
return proofBytes
|
||||
|
||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte], proof: RateLimitProof): bool =
|
||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||
proof: RateLimitProof): bool =
|
||||
var
|
||||
proofBytes = serialize(proof, data)
|
||||
proofBuffer = proofBytes.toBuffer()
|
||||
|
@ -259,7 +272,8 @@ proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
|||
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
||||
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
|
||||
## the toMembershipKeyPairs proc populates a sequence of MembershipKeyPairs using the supplied groupKeys
|
||||
|
||||
|
@ -269,7 +283,8 @@ proc toMembershipKeyPairs*(groupKeys: seq[(string, string)]): seq[MembershipKeyP
|
|||
let
|
||||
idKey = groupKeys[i][0].hexToByteArray(32)
|
||||
idCommitment = groupKeys[i][1].hexToByteArray(32)
|
||||
groupKeyPairs.add(MembershipKeyPair(idKey: idKey, idCommitment: idCommitment))
|
||||
groupKeyPairs.add(MembershipKeyPair(idKey: idKey,
|
||||
idCommitment: idCommitment))
|
||||
return groupKeyPairs
|
||||
|
||||
proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError].} =
|
||||
|
@ -289,7 +304,8 @@ proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError
|
|||
let root = rln.getMerkleRoot().value().toHex
|
||||
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
|
||||
## 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)
|
||||
|
@ -319,7 +335,9 @@ proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [De
|
|||
let root = rln.getMerkleRoot().value.toHex
|
||||
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
|
||||
# static group
|
||||
groupKeys = STATIC_GROUP_KEYS
|
||||
|
@ -328,7 +346,8 @@ proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[IDCommitment
|
|||
debug "rln-relay membership index", rlnRelayMemIndex
|
||||
|
||||
# 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"
|
||||
return(none(seq[IDCommitment]), none(MembershipKeyPair), none(MembershipIndex))
|
||||
|
||||
|
@ -352,7 +371,8 @@ proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string
|
|||
## 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`
|
||||
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
|
||||
if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch):
|
||||
|
@ -363,7 +383,9 @@ proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string
|
|||
return ok(false)
|
||||
|
||||
# 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:
|
||||
# there is a duplicate
|
||||
|
@ -379,7 +401,8 @@ proc updateLog*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string] =
|
|||
## extracts the `ProofMetadata` of the supplied messages `msg` and
|
||||
## 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,
|
||||
shareX: msg.proof.shareX, shareY: msg.proof.shareY)
|
||||
debug "proof metadata", proofMD = proofMD
|
||||
|
||||
# check if the epoch exists
|
||||
|
@ -420,7 +443,7 @@ proc getCurrentEpoch*(): Epoch =
|
|||
## gets the current rln Epoch time
|
||||
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`
|
||||
## i.e., e1 - e2
|
||||
|
||||
|
@ -430,7 +453,8 @@ proc compare*(e1, e2: Epoch): int64 =
|
|||
epoch2 = fromEpoch(e2)
|
||||
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.,
|
||||
## the `msg`'s epoch is within MAX_EPOCH_GAP of the current epoch
|
||||
## the `msg` has valid rate limit proof
|
||||
|
@ -452,7 +476,7 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
|
|||
let
|
||||
msgEpoch = msg.proof.epoch
|
||||
# calculate the gaps
|
||||
gap = compare(epoch, msgEpoch)
|
||||
gap = diff(epoch, msgEpoch)
|
||||
|
||||
debug "message epoch", msgEpoch = fromEpoch(msgEpoch)
|
||||
|
||||
|
@ -460,7 +484,8 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio
|
|||
if abs(gap) >= MAX_EPOCH_GAP:
|
||||
# message's epoch is too old or too ahead
|
||||
# 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
|
||||
|
||||
# verify the proof
|
||||
|
@ -495,7 +520,8 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] =
|
|||
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 false otherwise
|
||||
## `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds.
|
||||
|
|
Loading…
Reference in New Issue