From 63a9d9e63b326844434b0c8dd52a36cf2aaf2e02 Mon Sep 17 00:00:00 2001 From: Sanaz Taheri Boshrooyeh <35961250+staheri14@users.noreply.github.com> Date: Tue, 10 May 2022 14:09:18 -0700 Subject: [PATCH] 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 --- tests/all_tests_v2.nim | 2 + tests/v2/membershipContract.txt | 1 - tests/v2/poseidonHasher.txt | 1 - tests/v2/test_waku_rln_relay.nim | 595 ++++++------------ tests/v2/test_waku_rln_relay_onchain.nim | 271 ++++++++ .../waku_rln_relay/membershipContract.txt | 0 .../waku_rln_relay/rln_relay_contract.nim | 107 ++++ .../waku_rln_relay/waku_rln_relay_types.nim | 280 +++++++-- .../waku_rln_relay/waku_rln_relay_utils.nim | 274 ++++---- 9 files changed, 962 insertions(+), 569 deletions(-) delete mode 100644 tests/v2/membershipContract.txt delete mode 100644 tests/v2/poseidonHasher.txt create mode 100644 tests/v2/test_waku_rln_relay_onchain.nim create mode 100644 waku/v2/protocol/waku_rln_relay/membershipContract.txt create mode 100644 waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim diff --git a/tests/all_tests_v2.nim b/tests/all_tests_v2.nim index 54a22119d..022bd52ae 100644 --- a/tests/all_tests_v2.nim +++ b/tests/all_tests_v2.nim @@ -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 diff --git a/tests/v2/membershipContract.txt b/tests/v2/membershipContract.txt deleted file mode 100644 index 8494319d7..000000000 --- a/tests/v2/membershipContract.txt +++ /dev/null @@ -1 +0,0 @@ -0x60e06040526000805534801561001457600080fd5b50604051610c33380380610c338339818101604052606081101561003757600080fd5b5080516020820151604090920151608082905260a08390526001831b60c0819052600280546001600160a01b0319166001600160a01b0390931692909217909155909190610b776100bc6000398061037a52806105c352806105e752806106eb52508061047f5250806103f2528061065d52806106c7528061087b5250610b776000f3fe6080604052600436106100915760003560e01c806398366e351161005957806398366e35146101c7578063a9d85eba146101dc578063d0383d68146102f7578063f207564e1461030c578063f220b9ec1461032957610091565b80630ad58d2f14610096578063331b6ab3146100d75780635daf08ca1461010857806361579a931461014457806369e4863f14610159575b600080fd5b3480156100a257600080fd5b506100d5600480360360608110156100b957600080fd5b50803590602081013590604001356001600160a01b031661033e565b005b3480156100e357600080fd5b506100ec61034e565b604080516001600160a01b039092168252519081900360200190f35b34801561011457600080fd5b506101326004803603602081101561012b57600080fd5b503561035d565b60408051918252519081900360200190f35b34801561015057600080fd5b5061013261036f565b6100d56004803603602081101561016f57600080fd5b810190602081018135600160201b81111561018957600080fd5b82018360208201111561019b57600080fd5b803590602001918460208302840111600160201b831117156101bc57600080fd5b509092509050610375565b3480156101d357600080fd5b5061013261047d565b3480156101e857600080fd5b506100d5600480360360608110156101ff57600080fd5b810190602081018135600160201b81111561021957600080fd5b82018360208201111561022b57600080fd5b803590602001918460208302840111600160201b8311171561024c57600080fd5b919390929091602081019035600160201b81111561026957600080fd5b82018360208201111561027b57600080fd5b803590602001918460208302840111600160201b8311171561029c57600080fd5b919390929091602081019035600160201b8111156102b957600080fd5b8201836020820111156102cb57600080fd5b803590602001918460208302840111600160201b831117156102ec57600080fd5b5090925090506104a1565b34801561030357600080fd5b506101326105c1565b6100d56004803603602081101561032257600080fd5b50356105e5565b34801561033557600080fd5b506101326106c5565b6103498383836106e9565b505050565b6002546001600160a01b031681565b60016020526000908152604090205481565b60005481565b6000547f000000000000000000000000000000000000000000000000000000000000000090820111156103ef576040805162461bcd60e51b815260206004820152601f60248201527f524c4e2c20726567697374657242617463683a207365742069732066756c6c00604482015290519081900360640190fd5b347f000000000000000000000000000000000000000000000000000000000000000082021461044f5760405162461bcd60e51b8152600401808060200182810382526037815260200180610a3a6037913960400191505060405180910390fd5b60005b818110156103495761047583838381811061046957fe5b905060200201356108f4565b600101610452565b7f000000000000000000000000000000000000000000000000000000000000000081565b84806104de5760405162461bcd60e51b8152600401808060200182810382526023815260200180610a176023913960400191505060405180910390fd5b80841461051c5760405162461bcd60e51b81526004018080602001828103825260368152602001806109e16036913960400191505060405180910390fd5b80821461055a5760405162461bcd60e51b8152600401808060200182810382526031815260200180610a716031913960400191505060405180910390fd5b60005b818110156105b7576105af88888381811061057457fe5b9050602002013587878481811061058757fe5b9050602002013586868581811061059a57fe5b905060200201356001600160a01b03166106e9565b60010161055d565b5050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006000541061065b576040805162461bcd60e51b815260206004820152601a60248201527f524c4e2c2072656769737465723a207365742069732066756c6c000000000000604482015290519081900360640190fd5b7f000000000000000000000000000000000000000000000000000000000000000034146106b95760405162461bcd60e51b8152600401808060200182810382526032815260200180610aa26032913960400191505060405180910390fd5b6106c2816108f4565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000082106107475760405162461bcd60e51b8152600401808060200182810382526024815260200180610ad46024913960400191505060405180910390fd5b6000828152600160205260409020546107915760405162461bcd60e51b8152600401808060200182810382526024815260200180610af86024913960400191505060405180910390fd5b6001600160a01b0381166107d65760405162461bcd60e51b8152600401808060200182810382526026815260200180610b1c6026913960400191505060405180910390fd5b60006107f66040518060400160405280868152602001600081525061093f565b600084815260016020526040902054909150811461085b576040805162461bcd60e51b815260206004820152601c60248201527f524c4e2c205f77697468647261773a206e6f7420766572696669656400000000604482015290519081900360640190fd5b600083815260016020526040808220829055516001600160a01b038416917f000000000000000000000000000000000000000000000000000000000000000080156108fc02929091818181858888f193505050501580156108c0573d6000803e3d6000fd5b50604051839082907f62ec3a516d22a993ce5cb4e7593e878c74f4d799dde522a88dc27a994fd5a94390600090a350505050565b6000805481526001602052604080822083905581549051909183917f5a92c2530f207992057b9c3e544108ffce3beda4a63719f316967c49bf6159d29190a350600080546001019055565b60025460408051632b0aac7f60e11b81526000926001600160a01b03169163561558fe918591600490910190819083908083838a5b8381101561098c578181015183820152602001610974565b5050505090500191505060206040518083038186803b1580156109ae57600080fd5b505afa1580156109c2573d6000803e3d6000fd5b505050506040513d60208110156109d857600080fd5b50519291505056fe524c4e2c20776974686472617742617463683a2062617463682073697a65206d69736d61746368207075626b657920696e6465786573524c4e2c20776974686472617742617463683a2062617463682073697a65207a65726f524c4e2c20726567697374657242617463683a206d656d62657273686970206465706f736974206973206e6f7420736174697366696564524c4e2c20776974686472617742617463683a2062617463682073697a65206d69736d6174636820726563656976657273524c4e2c2072656769737465723a206d656d62657273686970206465706f736974206973206e6f7420736174697366696564524c4e2c205f77697468647261773a20696e76616c6964207075626b657920696e646578524c4e2c205f77697468647261773a206d656d62657220646f65736e2774206578697374524c4e2c205f77697468647261773a20656d7074792072656365697665722061646472657373a264697066735822122022de6c6a33f5de4d85f4d4c4b6095ef05927fe01341734446f0ac66a240cdd6a64736f6c63430007040033 \ No newline at end of file diff --git a/tests/v2/poseidonHasher.txt b/tests/v2/poseidonHasher.txt deleted file mode 100644 index db9a490d5..000000000 --- a/tests/v2/poseidonHasher.txt +++ /dev/null @@ -1 +0,0 @@ -0x608060405234801561001057600080fd5b50612142806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632c159a1a1461003b578063561558fe14610055575b600080fd5b6100436100a0565b60408051918252519081900360200190f35b6100436004803603604081101561006b57600080fd5b604080518082018252918301929181830191839060029083908390808284376000920191909152509194506100af9350505050565b60006100aa6100c0565b905090565b60006100ba826100e4565b92915050565b7f2ff267fd23782a5625e6d804f0a7fa700b8dc6084e2e7a5aff7cd4b1c506d30b90565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016040517f165d45ae851912f9a33800b04cc6617b184bf67db11ce904dc82601244551ae281527f10fc284d0af588165f4fc650fe7c53b1d80fbaac16d30518bf142117f42f820460208201527f06b687bd3c688aa9a03545d0835bca75ae82c434bf7d5fb065a2818b5c74814f60408201527f01057eb8e4bba26f12f4ea819251708d72e0605e6de827e990c3ba4ae13f5ecd60608201527e23779a38eb9ef4a9beaf4dc0a2ab5233a28ce6d10ad2512230a275b83017c360808201527f012e5dfdd4f34081753b70c897773f5d2987c8bbae32ad202a27cd61d7fba2fb7f0d1807f022a8d80d9304a1522087b8692dc0acf7b43fea28782d2ae672c0b11f7f17d468d0e6541de501481931453ed1e4a250e5e301f27dc91fe3b4bd522aa53c7f1ea09a4bd33f14eafd75e775d85e568fa668938fdd2f16fad1d4d2d2b9862b007f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b8851017f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b60208a0151017f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b8883840989848b83840909935089838409905089838b83840909925089828309905089828b8384090991507f0e4d154ca9b7f5111958289f43ed5bbc4d4f6118d45d9aefeb778179d921a59b9050808a60408b015184098b60208c015186098c8c518809010101818b8a85098c60808d015187098d60608e01518909010101828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f298d683000ab71c72fe4371cf6cd37bb584b6d816a653ee4bfea62518a337e079250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995508b85860992508b858d8586090994508b84850992508b848d8586090993507f2f860295bc93d694e74905913ddcae47290b9b5abb43a537fe40d9305bd1e1679250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f1dd8b95942d95c7896be7f3f455e595cbfee5e1023c5d45e6573c8513f1f3dfa9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f189aa3023aeaa7267dd3c74e2c8b9cba363546619bb9c54bcb02b22fe64c51619250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0da6b697fd05fe54a523131d91e2a7f6ac18184e237c1b20ab1936616f08e5239250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f019df963bfafa7f0e34cf092f33ce93b38b9d130fb44f276196e59f16e63ac5a9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f295332d5ab168bc3c5eb671528c9896b7bd5034fb02472ff2af4fa1087ae658f9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0f423b84792458876c314228be9361a604c75010411b87add91b791df2f980fa9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f1171744890a155b5ad9cde4f1f5c4eab878f3730a9fc71f546eb737c84abec5f9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2a557a13928c7eacff5dc049bc603cff1d7959c3975c5c9fc035b0c67173abac9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507ec9c858d5d1ab1622d7ea2174cd9a35b07c2655dd08dcda3f5a9f7222c021af9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0b358e01fc7cf925b6d3ba6e80c345881b54ff289479cc532f9fec01b500b9789250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f26c26f6e5a92ff96ec05a61d5f36df4f8134f8d61ff0eaa28cf7a480ee2b7f849250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f020930074e8c6c15295104a58533a601f202d3b3f959650d7b8cfa754ae41e7f9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f10f5f06286fdde345a5cd8ba4ad94828271ea16309f5fa906841e72eb4c330279250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f116ce0fb46b45c99fec7d369ab5c0d11d374a16b2107c5d6f21e3792d33fc9699250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2a59da7487bf7a05c3dbdfca6084e2ec589b1baf87ddd790d9bbc9c23557255b9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1559c4b2c04c419e948f42a7f8fecfb1525dbdf2b6830632723ff4fa77030ea89250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f030b1745767217e6d8df6885a85651f9aebefcf468003d2dd25906897c3ca92a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2668ff336585f1e64cd6ddf2cbcaa8a695585f0db79cbd1727becfeabe76d0449250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f158c1ceb605d6cb3e0c3a7e6650b1f297c4fccb1aeed560a09dfd474c4e47f999250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f27506f4872c8b8bb7f42921afbefd206cef54f5001f1c32efbc2a22094d172d49250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0e915e793df4e8ee01fd861b22ee463c985c691fb2a54c8657575ed93ab92c739250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f264bd2bc0486212803e5ef729bcaf466eb14f5b6ac18b898ed94844dda28d8a99250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2f23c5d822cd0d5b27fee9ff2c51f30a40654cdd08677543244f6aad8f2ebc8a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2776a03893ed5c5ce680ca5acda3c7d4b60b81269cabca38bfe060ce6eb6c4129250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f07aba324bd6d746b9f23313249f6a1809f3cfea3b1f3eb70c37951eb77120d3a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f09c0a6b8cf7f1c457987d7bb476b8ed8ddf88dbb146f2d887821d93d453d0fa39250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f292e4542f0e5b148bc7c9188b9db100f0f2aa2a62edb236041c4e4ebf44eb1469250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f01b08e9289e3a73080f49265283832fc48dfb2da2d540503b8274d7c0e52c9b69250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0d897c5adfe393f4c0247e718eb0b227f87226cc416b703eb2a96d61581af2e39250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f057582e2ebdd031c39c69ed08086d969d22de5d1bf79ee1be837234977e3477c9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0be6726fc2d3d2b9681fdc9ce4e274395f9e6dc5c6e647a3a5343e3f3fe77edf9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0e919ea6e332df81ae88f1155eac9e3c4b7dd7d8637feb112bcb2176f45fd9429250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2dadfa8057c413974c1c03aa2d38aac0d8c09748d08edc0e97450320c889c40d9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2954c27dbde3ba4f887becde3fc6c47db5266d436914d50d4e93faa79e40f7789250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f1e8f4f1b4fa7cc676e0d02084ce2bbf095adf6bcee924520e57c2f2a4b43f98a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f01892c0a3e4c3ffce3763f5d67ea86157c3b2461e08ff82ee28342ad41e636259250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f297b80afd662c439ff4837049ea3d95f565007b2defd87d981bfa45ce9ce80bf9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f28def6e44cc1e6b1826dd02d19d3383ddfd0dcdf96d5e9ce0b2834c6ad5c17b89250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0e7efce2651888246927ee306c83f4b24982ae2a126ff3be0217ea155e850d3e9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1b61bcbf0030070f3ca673d814ad8ea269bf0b4ddf75c4b4dd8383835be6b3809250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2bfc5e31157cdf08d36dcba28d869c209688dcb9da74df689427efbfe7ef20409250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2535e86274b29f682edc2f95d8fc9aa4b660b1992da99d3e3ccfd551b25280ae9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2e26f31955ddb83a0866535943960e6bf1d75532494e07382a1886df2eba4f2c9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1367f3092e878d4204f8597f09a6e75e596f8f859f7c4f0e4ee3bdefb14185759250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0786e0a89d59f9f1ec5a24b4e77471afb551993358839a2ad7f0cd30ee34a5939250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f05a623c15705f67c426798eb1aeb61d16bd599b73c618801e79ce87e4acf44439250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f216a2cd9a5dec0ce0f298c37a605d63a7a241fd04f74ee76d955ec729f75c7749250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1ac59c277950c8cf903e1e0b04c13b6be622d88a899267ccd30957e8b24feb899250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f21181ae96428a48600806eb2d2b94b4972bc69f4b69d99fb1edcbce69d1a73029250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2a24403e8cf5fb93ed9408b11d96e377bf1f1a2e183b0e14a18d6b794fc482909250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f13324a9a9db19a8f877c22de405a88bd39bec4696dc84721e7ccfef724a8e4f59250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f17f556db73809befc58822ad53ad5785aee4ff6891f211ae7183284b57a3910a9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f22e4f8f9c9ae56f5254ca73dc68572e805b9406a45fb4e10a831a7619b129fe59250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f066e36c90fda2dd52aa0c05909831d1dd345f75fcb17934ecb754d74e7ce8df99250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f190c65fb8440d50383891cbe244a8b063f0f9afbf14adce401416de7d6c755e19250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0b4fec4e028a67f9a9b932ae35a1269eeec8b98b96793d29811a766ad089ab1b9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f1def7475769de3cd372283c7d36bd153637723ff14615542bfe8b27108ad4de79250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995508b85860992508b858d8586090994508b84850992508b848d8586090993507f119217aded5f1ebf525a69e23a08f6a976178e5939646f6c87033299d2d3c8749250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f036bd8311510a8d03c5b354f03c664f9f2d23f57b1e4286d1938f8dbe4a7c28c9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c86090101019350505089848509905089848b83840909935089838409905089838b83840909925089828309905089828b838409505050929b9a505050505050505050505056fea2646970667358221220eef5419e4124debe6900ddea7f02a5dfbb1c6b867ba8e61b34edbc913728191664736f6c63430007040033 \ No newline at end of file diff --git a/tests/v2/test_waku_rln_relay.nim b/tests/v2/test_waku_rln_relay.nim index a26fe70a6..5de242a8d 100644 --- a/tests/v2/test_waku_rln_relay.nim +++ b/tests/v2/test_waku_rln_relay.nim @@ -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,69 +28,72 @@ procSuite "Waku rln relay": # create a group of 100 membership keys let (groupKeys, root) = createMembershipList(100) - check groupKeys.len == 100 - let + check: + groupKeys.len == 100 + let # 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 + + # 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 + # index also represents the index of the leaf in the Merkle tree that contains node's commitment key let index = MembershipIndex(5) # -------- mount rln-relay in the off-chain mode node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), - memIndexOpt = some(index), - onchainMode = false, + memIndexOpt = some(index), + onchainMode = false, pubsubTopic = RLNRELAY_PUBSUB_TOPIC, contentTopic = RLNRELAY_CONTENT_TOPIC) - + # 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 + # 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() suite "Waku rln relay": test "key_gen Nim Wrappers": - var + 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 + # 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) + len: csize_t = uint(pbytes.len) parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len) check: # check the parameters.key is not empty pbytes.len != 0 - var + 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 - # keysBufferPtr will hold the generated key pairs i.e., secret and public keys - var - keysBuffer : Buffer + # keysBufferPtr will hold the generated key pairs i.e., secret and public keys + var + keysBuffer: Buffer keysBufferPtr = addr(keysBuffer) - done = key_gen(rlnInstance, keysBufferPtr) + done = key_gen(rlnInstance, keysBufferPtr) check: # check whether the keys are generated successfully done == true @@ -350,8 +103,8 @@ suite "Waku rln relay": check: # the public and secret keys together are 64 bytes generatedKeys.len == 64 - debug "generated keys: ", generatedKeys - + debug "generated keys: ", generatedKeys + test "membership Key Gen": # create an RLN instance var rlnInstance = createRLNInstance() @@ -359,15 +112,15 @@ suite "Waku rln relay": rlnInstance.isOk == true var key = membershipKeyGen(rlnInstance.value) - var empty : array[32,byte] + var empty: array[32, byte] check: key.isSome key.get().idKey.len == 32 key.get().idCommitment.len == 32 key.get().idKey != empty key.get().idCommitment != empty - - debug "the generated membership key pair: ", key + + debug "the generated membership key pair: ", key test "get_root Nim binding": # create an RLN instance which also includes an empty Merkle tree @@ -376,8 +129,8 @@ suite "Waku rln relay": rlnInstance.isOk == true # read the Merkle Tree root - var - root1 {.noinit.} : Buffer = Buffer() + var + root1 {.noinit.}: Buffer = Buffer() rootPtr1 = addr(root1) get_root_successful1 = get_root(rlnInstance.value, rootPtr1) check: @@ -385,22 +138,23 @@ suite "Waku rln relay": root1.len == 32 # read the Merkle Tree root - var - root2 {.noinit.} : Buffer = Buffer() + var + root2 {.noinit.}: Buffer = Buffer() rootPtr2 = addr(root2) get_root_successful2 = get_root(rlnInstance.value, rootPtr2) - check: + check: get_root_successful2 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 - var rootValue2 = cast[ptr array[32,byte]] (root2.`ptr`) + var rootValue2 = cast[ptr array[32, byte]] (root2.`ptr`) 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 @@ -436,17 +194,18 @@ suite "Waku rln relay": var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr) check: member_is_added == true - + test "delete_member Nim wrapper": # create an RLN instance which also includes an empty Merkle tree var rlnInstance = createRLNInstance() check: rlnInstance.isOk == true - # delete the first member + # 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,17 +215,18 @@ suite "Waku rln relay": var rln = rlnInstance.value # generate a key pair var keypair = rln.membershipKeyGen() - check keypair.isSome() check: - rln.insertMember(keypair.get().idCommitment) - + keypair.isSome() + check: + rln.insertMember(keypair.get().idCommitment) + test "removeMember rln utils": # create an RLN instance which also includes an empty Merkle tree var rlnInstance = createRLNInstance() check: rlnInstance.isOk == true var rln = rlnInstance.value - check: + check: rln.removeMember(MembershipIndex(0)) test "Merkle tree consistency check between deletion and insertion": @@ -476,14 +236,14 @@ suite "Waku rln relay": rlnInstance.isOk == true # read the Merkle Tree root - var - root1 {.noinit.} : Buffer = Buffer() + var + root1 {.noinit.}: Buffer = Buffer() rootPtr1 = addr(root1) get_root_successful1 = get_root(rlnInstance.value, rootPtr1) check: get_root_successful1 root1.len == 32 - + # generate a key pair var keypair = membershipKeyGen(rlnInstance.value) check: keypair.isSome() @@ -492,49 +252,52 @@ 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 - root2 {.noinit.} : Buffer = Buffer() + var + root2 {.noinit.}: Buffer = Buffer() rootPtr2 = addr(root2) get_root_successful2 = get_root(rlnInstance.value, rootPtr2) check: get_root_successful2 root2.len == 32 - # delete the first member + # 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 - root3 {.noinit.} : Buffer = Buffer() + var + root3 {.noinit.}: Buffer = Buffer() rootPtr3 = addr(root3) get_root_successful3 = get_root(rlnInstance.value, rootPtr3) check: get_root_successful3 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 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 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 debug "The root after deletion", rootHex3 # the root must change after the insertion 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 - 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() - let member_inserted = rln.insertMember(keypair.get().idCommitment) - check member_inserted + check: + keypair.isSome() + let member_inserted = rln.insertMember(keypair.get().idCommitment) + 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 + + # 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,35 +344,40 @@ 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 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 var rlnInstance = createRLNInstance() check: rlnInstance.isOk == true - + # prepare the input - var + var msg = "Hello".toBytes() hashInput = appendLength(msg) - hashInputBuffer = toBuffer(hashInput) + hashInputBuffer = toBuffer(hashInput) # prepare other inputs to the hash function 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 - hashOutput = cast[ptr array[32,byte]] (outputBuffer.`ptr`)[] + 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 + hashOutput = cast[ptr array[32, byte]] (outputBuffer.`ptr`)[] hashOutputHex = hashOutput.toHex() debug "hash output", hashOutputHex @@ -614,26 +388,27 @@ suite "Waku rln relay": check: rlnInstance.isOk == true let rln = rlnInstance.value - + # prepare the input let msg = "Hello".toBytes() 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 + let groupSize = 100 - (list, root) = createMembershipList(groupSize) + (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 + 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 @@ -648,14 +423,14 @@ suite "Waku rln relay": debug "groupIDCommitments", groupIDCommitments debug "root", root - check: + check: # check that the correct number of key pairs is created groupKeyPairs.len == StaticGroupSize # compare the calculated root against the correct root root == STATIC_GROUP_MERKLE_ROOT - + test "RateLimitProof Protobuf encode/init test": - var + var proof: ZKSNARK merkleRoot: MerkleNode epoch: Epoch @@ -663,14 +438,14 @@ suite "Waku rln relay": 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 + 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 rateLimitProof = RateLimitProof(proof: proof, merkleRoot: merkleRoot, epoch: epoch, @@ -686,16 +461,17 @@ 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 + 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 + # Create a Merkle tree with random members for i in 0..10: var member_is_added: bool = false if (i == index): @@ -706,27 +482,30 @@ 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 + # prepare the message let messageBytes = "Hello".toBytes() # prepare the epoch - var epoch : Epoch - debug "epoch", epochHex=epoch.toHex() + var epoch: Epoch + debug "epoch", epochHex = epoch.toHex() # generate proof - let proofRes = rln.proofGen(data = messageBytes, + let proofRes = rln.proofGen(data = messageBytes, 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() @@ -734,13 +513,13 @@ suite "Waku rln relay": rlnInstance.isOk == true var rln = rlnInstance.value - let + 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 + # Create a Merkle tree with random members for i in 0..10: var member_is_added: bool = false if (i == index): @@ -751,50 +530,56 @@ 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 + # prepare the message let messageBytes = "Hello".toBytes() # prepare the epoch - var epoch : Epoch - debug "epoch in bytes", epochHex=epoch.toHex() + var epoch: Epoch + debug "epoch in bytes", epochHex = epoch.toHex() let badIndex = 4 # generate proof - let proofRes = rln.proofGen(data = messageBytes, + let proofRes = rln.proofGen(data = messageBytes, 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 - + let + 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 - let + 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) - + check: + diff(epoch1, epoch2) == int64(1) + diff(epoch2, epoch1) == int64(-1) + test "updateLog and hasDuplicate tests": - let + let wakurlnrelay = WakuRLNRelay() epoch = getCurrentEpoch() @@ -816,10 +601,13 @@ suite "Waku rln relay": 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)) + 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 @@ -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 let result3 = wakurlnrelay.hasDuplicate(wm3) check: - result3.isOk + result3.isOk # it is a duplicate result3.value == true @@ -859,10 +647,10 @@ suite "Waku rln relay": 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 + + # 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 + # 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 @@ -873,24 +661,25 @@ suite "Waku rln relay": # add members discard rln.addAll(groupIDCommitments) - let - wakuRlnRelay = WakuRLNRelay(membershipIndex: index, membershipKeyPair: groupKeyPairs[index], rlnInstance: rln) + let + wakuRlnRelay = WakuRLNRelay(membershipIndex: index, + membershipKeyPair: groupKeyPairs[index], rlnInstance: rln) - # get the current epoch time + # get the current epoch time let time = epochTime() # create some messages from the same peer and append rln proof to them, except wm4 - var + 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 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()) - + wm4 = WakuMessage(payload: "Invalid message".toBytes()) + # checks proofs are added check: proofAdded1 @@ -903,15 +692,15 @@ suite "Waku rln relay": msgValidate1 = wakuRlnRelay.validateMessage(wm1, some(time)) # wm2 is published within the same Epoch as wm1 and should be found as spam 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)) # wm4 has no rln proof and should not be validated msgValidate4 = wakuRlnRelay.validateMessage(wm4, some(time)) - + check: msgValidate1 == MessageValidationResult.Valid - msgValidate2 == MessageValidationResult.Spam + msgValidate2 == MessageValidationResult.Spam msgValidate3 == MessageValidationResult.Valid msgValidate4 == MessageValidationResult.Invalid diff --git a/tests/v2/test_waku_rln_relay_onchain.nim b/tests/v2/test_waku_rln_relay_onchain.nim new file mode 100644 index 000000000..49ef77270 --- /dev/null +++ b/tests/v2/test_waku_rln_relay_onchain.nim @@ -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() + diff --git a/waku/v2/protocol/waku_rln_relay/membershipContract.txt b/waku/v2/protocol/waku_rln_relay/membershipContract.txt new file mode 100644 index 000000000..e69de29bb diff --git a/waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim b/waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim new file mode 100644 index 000000000..c54fa7f3d --- /dev/null +++ b/waku/v2/protocol/waku_rln_relay/rln_relay_contract.nim @@ -0,0 +1,107 @@ +# src: https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/RLN.sol +# pragma solidity 0.7.4; + +# import { IPoseidonHasher } from "./crypto/PoseidonHasher.sol"; + +# contract RLN { +# 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 pubkey, uint256 index); +# event MemberWithdrawn(uint256 pubkey, uint256 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 _register(uint256 pubkey) internal { +# members[pubkeyIndex] = pubkey; +# emit MemberRegistered(pubkey, pubkeyIndex); +# pubkeyIndex += 1; +# } + +# 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); +# } + +# function _withdraw( +# uint256 secret, +# uint256 _pubkeyIndex, +# address payable receiver +# ) internal { +# require(_pubkeyIndex < SET_SIZE, "RLN, _withdraw: invalid pubkey index"); +# require(members[_pubkeyIndex] != 0, "RLN, _withdraw: member doesn't exist"); +# require(receiver != address(0), "RLN, _withdraw: empty receiver address"); + +# // derive public key +# uint256 pubkey = hash([secret, 0]); +# require(members[_pubkeyIndex] == pubkey, "RLN, _withdraw: not verified"); + +# // delete member +# members[_pubkeyIndex] = 0; + +# // refund deposit +# receiver.transfer(MEMBERSHIP_DEPOSIT); + +# emit MemberWithdrawn(pubkey, _pubkeyIndex); +# } + +# function hash(uint256[2] memory input) internal view returns (uint256) { +# return poseidonHasher.hash(input); +# } +# } + +# 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* = "0x608060405234801561001057600080fd5b50612142806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632c159a1a1461003b578063561558fe14610055575b600080fd5b6100436100a0565b60408051918252519081900360200190f35b6100436004803603604081101561006b57600080fd5b604080518082018252918301929181830191839060029083908390808284376000920191909152509194506100af9350505050565b60006100aa6100c0565b905090565b60006100ba826100e4565b92915050565b7f2ff267fd23782a5625e6d804f0a7fa700b8dc6084e2e7a5aff7cd4b1c506d30b90565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016040517f165d45ae851912f9a33800b04cc6617b184bf67db11ce904dc82601244551ae281527f10fc284d0af588165f4fc650fe7c53b1d80fbaac16d30518bf142117f42f820460208201527f06b687bd3c688aa9a03545d0835bca75ae82c434bf7d5fb065a2818b5c74814f60408201527f01057eb8e4bba26f12f4ea819251708d72e0605e6de827e990c3ba4ae13f5ecd60608201527e23779a38eb9ef4a9beaf4dc0a2ab5233a28ce6d10ad2512230a275b83017c360808201527f012e5dfdd4f34081753b70c897773f5d2987c8bbae32ad202a27cd61d7fba2fb7f0d1807f022a8d80d9304a1522087b8692dc0acf7b43fea28782d2ae672c0b11f7f17d468d0e6541de501481931453ed1e4a250e5e301f27dc91fe3b4bd522aa53c7f1ea09a4bd33f14eafd75e775d85e568fa668938fdd2f16fad1d4d2d2b9862b007f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b8851017f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b60208a0151017f061f2e832c23bee84c2f09d63d371cc5eda2f749cdbe9a6a6d20469e9fa36e8b8883840989848b83840909935089838409905089838b83840909925089828309905089828b8384090991507f0e4d154ca9b7f5111958289f43ed5bbc4d4f6118d45d9aefeb778179d921a59b9050808a60408b015184098b60208c015186098c8c518809010101818b8a85098c60808d015187098d60608e01518909010101828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f298d683000ab71c72fe4371cf6cd37bb584b6d816a653ee4bfea62518a337e079250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995508b85860992508b858d8586090994508b84850992508b848d8586090993507f2f860295bc93d694e74905913ddcae47290b9b5abb43a537fe40d9305bd1e1679250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f1dd8b95942d95c7896be7f3f455e595cbfee5e1023c5d45e6573c8513f1f3dfa9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f189aa3023aeaa7267dd3c74e2c8b9cba363546619bb9c54bcb02b22fe64c51619250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0da6b697fd05fe54a523131d91e2a7f6ac18184e237c1b20ab1936616f08e5239250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f019df963bfafa7f0e34cf092f33ce93b38b9d130fb44f276196e59f16e63ac5a9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f295332d5ab168bc3c5eb671528c9896b7bd5034fb02472ff2af4fa1087ae658f9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0f423b84792458876c314228be9361a604c75010411b87add91b791df2f980fa9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f1171744890a155b5ad9cde4f1f5c4eab878f3730a9fc71f546eb737c84abec5f9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2a557a13928c7eacff5dc049bc603cff1d7959c3975c5c9fc035b0c67173abac9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507ec9c858d5d1ab1622d7ea2174cd9a35b07c2655dd08dcda3f5a9f7222c021af9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0b358e01fc7cf925b6d3ba6e80c345881b54ff289479cc532f9fec01b500b9789250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f26c26f6e5a92ff96ec05a61d5f36df4f8134f8d61ff0eaa28cf7a480ee2b7f849250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f020930074e8c6c15295104a58533a601f202d3b3f959650d7b8cfa754ae41e7f9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f10f5f06286fdde345a5cd8ba4ad94828271ea16309f5fa906841e72eb4c330279250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f116ce0fb46b45c99fec7d369ab5c0d11d374a16b2107c5d6f21e3792d33fc9699250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2a59da7487bf7a05c3dbdfca6084e2ec589b1baf87ddd790d9bbc9c23557255b9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1559c4b2c04c419e948f42a7f8fecfb1525dbdf2b6830632723ff4fa77030ea89250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f030b1745767217e6d8df6885a85651f9aebefcf468003d2dd25906897c3ca92a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2668ff336585f1e64cd6ddf2cbcaa8a695585f0db79cbd1727becfeabe76d0449250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f158c1ceb605d6cb3e0c3a7e6650b1f297c4fccb1aeed560a09dfd474c4e47f999250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f27506f4872c8b8bb7f42921afbefd206cef54f5001f1c32efbc2a22094d172d49250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0e915e793df4e8ee01fd861b22ee463c985c691fb2a54c8657575ed93ab92c739250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f264bd2bc0486212803e5ef729bcaf466eb14f5b6ac18b898ed94844dda28d8a99250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2f23c5d822cd0d5b27fee9ff2c51f30a40654cdd08677543244f6aad8f2ebc8a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2776a03893ed5c5ce680ca5acda3c7d4b60b81269cabca38bfe060ce6eb6c4129250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f07aba324bd6d746b9f23313249f6a1809f3cfea3b1f3eb70c37951eb77120d3a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f09c0a6b8cf7f1c457987d7bb476b8ed8ddf88dbb146f2d887821d93d453d0fa39250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f292e4542f0e5b148bc7c9188b9db100f0f2aa2a62edb236041c4e4ebf44eb1469250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f01b08e9289e3a73080f49265283832fc48dfb2da2d540503b8274d7c0e52c9b69250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0d897c5adfe393f4c0247e718eb0b227f87226cc416b703eb2a96d61581af2e39250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f057582e2ebdd031c39c69ed08086d969d22de5d1bf79ee1be837234977e3477c9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0be6726fc2d3d2b9681fdc9ce4e274395f9e6dc5c6e647a3a5343e3f3fe77edf9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0e919ea6e332df81ae88f1155eac9e3c4b7dd7d8637feb112bcb2176f45fd9429250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2dadfa8057c413974c1c03aa2d38aac0d8c09748d08edc0e97450320c889c40d9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2954c27dbde3ba4f887becde3fc6c47db5266d436914d50d4e93faa79e40f7789250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f1e8f4f1b4fa7cc676e0d02084ce2bbf095adf6bcee924520e57c2f2a4b43f98a9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f01892c0a3e4c3ffce3763f5d67ea86157c3b2461e08ff82ee28342ad41e636259250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f297b80afd662c439ff4837049ea3d95f565007b2defd87d981bfa45ce9ce80bf9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f28def6e44cc1e6b1826dd02d19d3383ddfd0dcdf96d5e9ce0b2834c6ad5c17b89250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0e7efce2651888246927ee306c83f4b24982ae2a126ff3be0217ea155e850d3e9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1b61bcbf0030070f3ca673d814ad8ea269bf0b4ddf75c4b4dd8383835be6b3809250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2bfc5e31157cdf08d36dcba28d869c209688dcb9da74df689427efbfe7ef20409250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2535e86274b29f682edc2f95d8fc9aa4b660b1992da99d3e3ccfd551b25280ae9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f2e26f31955ddb83a0866535943960e6bf1d75532494e07382a1886df2eba4f2c9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1367f3092e878d4204f8597f09a6e75e596f8f859f7c4f0e4ee3bdefb14185759250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f0786e0a89d59f9f1ec5a24b4e77471afb551993358839a2ad7f0cd30ee34a5939250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f05a623c15705f67c426798eb1aeb61d16bd599b73c618801e79ce87e4acf44439250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f216a2cd9a5dec0ce0f298c37a605d63a7a241fd04f74ee76d955ec729f75c7749250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f1ac59c277950c8cf903e1e0b04c13b6be622d88a899267ccd30957e8b24feb899250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f21181ae96428a48600806eb2d2b94b4972bc69f4b69d99fb1edcbce69d1a73029250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f2a24403e8cf5fb93ed9408b11d96e377bf1f1a2e183b0e14a18d6b794fc482909250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f13324a9a9db19a8f877c22de405a88bd39bec4696dc84721e7ccfef724a8e4f59250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f17f556db73809befc58822ad53ad5785aee4ff6891f211ae7183284b57a3910a9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f22e4f8f9c9ae56f5254ca73dc68572e805b9406a45fb4e10a831a7619b129fe59250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f066e36c90fda2dd52aa0c05909831d1dd345f75fcb17934ecb754d74e7ce8df99250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991507f190c65fb8440d50383891cbe244a8b063f0f9afbf14adce401416de7d6c755e19250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995507f0b4fec4e028a67f9a9b932ae35a1269eeec8b98b96793d29811a766ad089ab1b9250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f1def7475769de3cd372283c7d36bd153637723ff14615542bfe8b27108ad4de79250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c860901010193508b86870992508b868d8586090995508b85860992508b858d8586090994508b84850992508b848d8586090993507f119217aded5f1ebf525a69e23a08f6a976178e5939646f6c87033299d2d3c8749250828c60408d015186098d60208e015188098e8e518a090101019150828c8b86098d60808e015188098e60608f01518a090101019050828c8886098d8a88098e8c8a0901010193508b82830992508b828d8586090991508b81820992508b818d8586090990508b84850992508b848d8586090993507f036bd8311510a8d03c5b354f03c664f9f2d23f57b1e4286d1938f8dbe4a7c28c9250828c60408d015186098d60208e015184098e8e5186090101019550828c8b86098d60808e015184098e60608f015186090101019450828c8886098d8a84098e8c86090101019350505089848509905089848b83840909935089838409905089838b83840909925089828309905089828b838409505050929b9a505050505050505050505056fea2646970667358221220eef5419e4124debe6900ddea7f02a5dfbb1c6b867ba8e61b34edbc913728191664736f6c63430007040033" + +# 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* = "0x60e06040526000805534801561001457600080fd5b50604051610c4f380380610c4f8339818101604052606081101561003757600080fd5b5080516020820151604090920151608082905260a08390526001831b60c0819052600280546001600160a01b0319166001600160a01b0390931692909217909155909190610b936100bc6000398061037a52806105c352806105e752806106eb52508061047f5250806103f2528061065d52806106c7528061087b5250610b936000f3fe6080604052600436106100915760003560e01c806398366e351161005957806398366e35146101c7578063a9d85eba146101dc578063d0383d68146102f7578063f207564e1461030c578063f220b9ec1461032957610091565b80630ad58d2f14610096578063331b6ab3146100d75780635daf08ca1461010857806361579a931461014457806369e4863f14610159575b600080fd5b3480156100a257600080fd5b506100d5600480360360608110156100b957600080fd5b50803590602081013590604001356001600160a01b031661033e565b005b3480156100e357600080fd5b506100ec61034e565b604080516001600160a01b039092168252519081900360200190f35b34801561011457600080fd5b506101326004803603602081101561012b57600080fd5b503561035d565b60408051918252519081900360200190f35b34801561015057600080fd5b5061013261036f565b6100d56004803603602081101561016f57600080fd5b810190602081018135600160201b81111561018957600080fd5b82018360208201111561019b57600080fd5b803590602001918460208302840111600160201b831117156101bc57600080fd5b509092509050610375565b3480156101d357600080fd5b5061013261047d565b3480156101e857600080fd5b506100d5600480360360608110156101ff57600080fd5b810190602081018135600160201b81111561021957600080fd5b82018360208201111561022b57600080fd5b803590602001918460208302840111600160201b8311171561024c57600080fd5b919390929091602081019035600160201b81111561026957600080fd5b82018360208201111561027b57600080fd5b803590602001918460208302840111600160201b8311171561029c57600080fd5b919390929091602081019035600160201b8111156102b957600080fd5b8201836020820111156102cb57600080fd5b803590602001918460208302840111600160201b831117156102ec57600080fd5b5090925090506104a1565b34801561030357600080fd5b506101326105c1565b6100d56004803603602081101561032257600080fd5b50356105e5565b34801561033557600080fd5b506101326106c5565b6103498383836106e9565b505050565b6002546001600160a01b031681565b60016020526000908152604090205481565b60005481565b6000547f000000000000000000000000000000000000000000000000000000000000000090820111156103ef576040805162461bcd60e51b815260206004820152601f60248201527f524c4e2c20726567697374657242617463683a207365742069732066756c6c00604482015290519081900360640190fd5b347f000000000000000000000000000000000000000000000000000000000000000082021461044f5760405162461bcd60e51b8152600401808060200182810382526037815260200180610a566037913960400191505060405180910390fd5b60005b818110156103495761047583838381811061046957fe5b90506020020135610902565b600101610452565b7f000000000000000000000000000000000000000000000000000000000000000081565b84806104de5760405162461bcd60e51b8152600401808060200182810382526023815260200180610a336023913960400191505060405180910390fd5b80841461051c5760405162461bcd60e51b81526004018080602001828103825260368152602001806109fd6036913960400191505060405180910390fd5b80821461055a5760405162461bcd60e51b8152600401808060200182810382526031815260200180610a8d6031913960400191505060405180910390fd5b60005b818110156105b7576105af88888381811061057457fe5b9050602002013587878481811061058757fe5b9050602002013586868581811061059a57fe5b905060200201356001600160a01b03166106e9565b60010161055d565b5050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006000541061065b576040805162461bcd60e51b815260206004820152601a60248201527f524c4e2c2072656769737465723a207365742069732066756c6c000000000000604482015290519081900360640190fd5b7f000000000000000000000000000000000000000000000000000000000000000034146106b95760405162461bcd60e51b8152600401808060200182810382526032815260200180610abe6032913960400191505060405180910390fd5b6106c281610902565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000082106107475760405162461bcd60e51b8152600401808060200182810382526024815260200180610af06024913960400191505060405180910390fd5b6000828152600160205260409020546107915760405162461bcd60e51b8152600401808060200182810382526024815260200180610b146024913960400191505060405180910390fd5b6001600160a01b0381166107d65760405162461bcd60e51b8152600401808060200182810382526026815260200180610b386026913960400191505060405180910390fd5b60006107f66040518060400160405280868152602001600081525061095b565b600084815260016020526040902054909150811461085b576040805162461bcd60e51b815260206004820152601c60248201527f524c4e2c205f77697468647261773a206e6f7420766572696669656400000000604482015290519081900360640190fd5b600083815260016020526040808220829055516001600160a01b038416917f000000000000000000000000000000000000000000000000000000000000000080156108fc02929091818181858888f193505050501580156108c0573d6000803e3d6000fd5b50604080518281526020810185905281517f62ec3a516d22a993ce5cb4e7593e878c74f4d799dde522a88dc27a994fd5a943929181900390910190a150505050565b600080548152600160209081526040808320849055915482518481529182015281517f5a92c2530f207992057b9c3e544108ffce3beda4a63719f316967c49bf6159d2929181900390910190a150600080546001019055565b60025460408051632b0aac7f60e11b81526000926001600160a01b03169163561558fe918591600490910190819083908083838a5b838110156109a8578181015183820152602001610990565b5050505090500191505060206040518083038186803b1580156109ca57600080fd5b505afa1580156109de573d6000803e3d6000fd5b505050506040513d60208110156109f457600080fd5b50519291505056fe524c4e2c20776974686472617742617463683a2062617463682073697a65206d69736d61746368207075626b657920696e6465786573524c4e2c20776974686472617742617463683a2062617463682073697a65207a65726f524c4e2c20726567697374657242617463683a206d656d62657273686970206465706f736974206973206e6f7420736174697366696564524c4e2c20776974686472617742617463683a2062617463682073697a65206d69736d6174636820726563656976657273524c4e2c2072656769737465723a206d656d62657273686970206465706f736974206973206e6f7420736174697366696564524c4e2c205f77697468647261773a20696e76616c6964207075626b657920696e646578524c4e2c205f77697468647261773a206d656d62657220646f65736e2774206578697374524c4e2c205f77697468647261773a20656d7074792072656365697665722061646472657373a2646970667358221220be0b0b1f842029cde290573a70f50674e9f4d1db01b636facff8e065d03cb1e764736f6c63430007040033" diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim index 197064165..776284975 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim @@ -1,6 +1,6 @@ {.push raises: [Defect].} -import +import std/tables, options, chronos, stint, web3, @@ -8,53 +8,53 @@ import libp2p/protobuf/minprotobuf, stew/arrayops -## Bn256 and RLN are Nim wrappers for the data types used in +## Bn256 and RLN are Nim wrappers for the data types used in ## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs type Bn256* = pointer type RLN*[E] = pointer -type +type # identity key as defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership IDKey* = array[32, byte] # hash of identity key as defined ed in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership IDCommitment* = array[32, byte] - -type - MerkleNode* = array[32,byte] # Each node of the Merkle tee is a Poseidon hash which is a 32 byte value - Nullifier* = array[32,byte] + +type + MerkleNode* = array[32, byte] # Each node of the Merkle tee is a Poseidon hash which is a 32 byte value + Nullifier* = array[32, byte] ZKSNARK* = array[256, byte] - Epoch* = array[32,byte] + Epoch* = array[32, byte] # Custom data types defined for waku rln relay ------------------------- -type MembershipKeyPair* = object - ## user's identity key (a secret key) which is selected randomly +type MembershipKeyPair* = object + ## user's identity key (a secret key) which is selected randomly ## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership - idKey*: IDKey - # hash of user's identity key generated by + idKey*: IDKey + # hash of user's identity key generated by # Poseidon hash function implemented in rln lib # more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership - idCommitment*: IDCommitment + idCommitment*: IDCommitment type RateLimitProof* = object - ## RateLimitProof holds the public inputs to rln circuit as + ## RateLimitProof holds the public inputs to rln circuit as ## defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs - ## the `proof` field carries the actual zkSNARK proof + ## the `proof` field carries the actual zkSNARK proof proof*: ZKSNARK - ## the root of Merkle tree used for the generation of the `proof` + ## the root of Merkle tree used for the generation of the `proof` merkleRoot*: MerkleNode - ## the epoch used for the generation of the `proof` + ## the epoch used for the generation of the `proof` epoch*: Epoch ## shareX and shareY are shares of user's identity key - ## these shares are created using Shamir secret sharing scheme + ## these shares are created using Shamir secret sharing scheme ## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS shareX*: MerkleNode shareY*: MerkleNode ## nullifier enables linking two messages published during the same epoch ## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers nullifier*: Nullifier - + type MembershipIndex* = uint type ProofMetadata* = object @@ -62,12 +62,12 @@ type ProofMetadata* = object shareX*: MerkleNode shareY*: MerkleNode -type WakuRLNRelay* = ref object +type WakuRLNRelay* = ref object membershipKeyPair*: MembershipKeyPair - # membershipIndex denotes the index of a leaf in the Merkle tree + # membershipIndex denotes the index of a leaf in the Merkle tree # that contains the pk of the current peer # this index is used to retrieve the peer's authentication path - membershipIndex*: MembershipIndex + membershipIndex*: MembershipIndex membershipContractAddress*: Address ethClientAddress*: string ethAccountAddress*: Address @@ -77,18 +77,18 @@ type WakuRLNRelay* = ref object ethAccountPrivateKey*: Option[PrivateKey] rlnInstance*: RLN[Bn256] pubsubTopic*: string # the pubsub topic for which rln relay is mounted - # contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead - # TODO a long-term solution is to place types with recursive dependency inside one file - contentTopic*: string + # contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead + # TODO a long-term solution is to place types with recursive dependency inside one file + contentTopic*: string # the log of nullifiers and Shamir shares of the past messages grouped per epoch nullifierLog*: Table[Epoch, seq[ProofMetadata]] type MessageValidationResult* {.pure.} = enum - Valid, Invalid, Spam + Valid, Invalid, Spam # inputs of the membership contract constructor # TODO may be able to make these constants private and put them inside the waku_rln_relay_utils -const +const MEMBERSHIP_FEE* = 5.u256 # the current implementation of the rln lib only supports a circuit for Merkle tree with depth 32 MERKLE_TREE_DEPTH* = 20 @@ -96,30 +96,230 @@ const # the current address is the address of ganache-cli when run locally ETH_CLIENT* = "ws://localhost:8540/" -const +const # the size of poseidon hash output in bits - HASH_BIT_SIZE* = 256 + HASH_BIT_SIZE* = 256 # the size of poseidon hash output as the number hex digits HASH_HEX_SIZE* = int(HASH_BIT_SIZE/4) # temporary variables to test waku-rln-relay performance in the static group mode const STATIC_GROUP_SIZE* = 100 - # STATIC_GROUP_KEYS is a static list of 100 membership keys in the form of (identity key, identity commitment) - # keys are created locally, using createMembershipList proc from waku_rln_relay_utils module, and the results are hardcoded in here + # STATIC_GROUP_KEYS is a static list of 100 membership keys in the form of (identity key, identity commitment) + # keys are created locally, using createMembershipList proc from waku_rln_relay_utils module, and the results are hardcoded in here # this list is temporary and is created to test the performance of waku-rln-relay for the static groups # in the later versions, this static hardcoded group will be replaced with a dynamic one - STATIC_GROUP_KEYS* = @[("e9a4d05b1f539d65c59015a079ee89aabeafbcfc9734342d9559f81601e85417", "b74d3a5b3200ab1126fbee393496f33da497d4d9a7c56693f44d6155c0c34e13"), ("27b2bfc25257e53819beaf36ce1070007e04e7aad2e440a1f1fc066f59a61123", "522ce51aff96041e79a8476f508fb9661f146f189e288f83cb4837517cfc0127"), ("66392eaae6674267c55fe393d39443ba90317a709d6e8f92a9f3e4abc18eff1d", "e3dc235e48c1811943fc249fecd0f1415a50ebe839ccefb0bd820a76fb77ba2a"), ("e7462eebb81405230db8014b052d65fe7b269c3870e40b12cf64668ed6c2d40e", "727df0965e34144ea637be18208cc81e57e423010b3159c20f0ccff45c42212a"), ("1ad8528b4c7075013a2d6561a02517b0482c0733dc018ac68774db857deb5004", "5df3a77577135784da2a9ee78f5026092b7d6bb9e6e95882d6cb172c0cb62208"), ("aa0ff53bfc50861f871d94df18c3ac0b97f44ceb13436b33490cec5f6ce8e700", "30421d05b905aeaec0473ba29ace034bf73c406866d7dc23007eb9c34a596827"), ("0448c0a6ed57b177c4c45de478b58d29f24e7ea842814305443e87188ae24324", "5ea1a704d8972af5a028367c8e3fb48ee61a603c6ea3a4c9247b0f611a6ab002"), ("4b6a6edbdd11e69befe3f4a3c976baca320c4bcb188f129b603ebb198f663000", "451d6185e8ad2c8873f034683b9caca43ea7ceb1b839abd3e01c3f19f3e6bd1e"), ("c3fba34855b33f025696326d2980ddc3fb47d90459ed6a4488fbb2e4e12ccf2b", "74674a86144ea866ad8fe633e256783bda4a07b997cb412c53a5eaf4cc7b6a0e"), ("0c35b8b94a720f1c26d7c6241c9f3ea5332a87cf3730b25ef31b68854c10e405", "6915df8d8ad19ec17be37c299eed762f9b63e841cd7963e13e8db6890dba082d"), ("a60b021677da95ba46c8c3411ac77f3e3b06937a8d189517111c045880029909", "bbfcd22ecb44cec6fd0717cd0f21b26e6e8b2c91e1a6cb5d8610e2f2ad41c90f"), ("2f4d662e66fbe754b708b87ea3d75a01d2ea4d7bf33c615c2376211dcc3b560f", "c67f7e622c3293028b9f86571e82c49551eb5fd308a35eb663498cffac208810"), ("d90a1afa96c14c8d3b989a9cf23d6e8b9907da42724e44a3ac74ae015b6ce22e", "f0795b1bdd0a907252b6ab047642d97be076a16ea69d463f1a4bec00c817202e"), ("dfa0764d89c8da10777504e5274f1baccf8b4145deba72b26503474318fb6410", "9f8aa8e833ea2f13cccfb6d9f2f04fd7be9c9f3019540c05c1986b3bce254e25"), ("e17490013b6b53a40964ff1067b922d4d73521e32fe394527b39c1bfd4a5e712", "7a8fa23a0e4b14a36f2818a7d98639f6e4934c028da780a6cc658fbf76e80a26"), ("7a9328d1075373dddb1b1100e8217ffaa1f9b632911b95a8fdc08870b15a8410", "94b2ae70c046b94873098c19fe18e7b17db2d31fe6a7eb73fea8168395e3c122"), ("ce319bb1447da5bf51a88ab3379dbea539b5a431d3c4f131048cf7b05c52161d", "cdfa264ab8a51bbc9fff5732cf544bb06abba7e807a8c252a5f9ce785c6ff22c"), ("d3818503bfcbef9ca03fc4472be77cb4936a1720001fc5e54852f769448fd313", "e72792abd906976c75a5670de514894a720c5293cf74338a9c2987640a949c0b"), ("4f9b8d118460736eb62602d12d3aed62938d4d4374b8c88704cf40c415c0901e", "2ecbe4588ade31924a1053fe0204950a0b4924878b312e56b2d0522f92a5f01c"), ("39b59fd96adaf9633edcc8cef10049cddf5f7df8ac80af8aafa436d62ee7f905", "b94f7a979df8a95fc2766a9a96308ff39a14daf7b7d6bc48591d2ea4c764bf00"), ("73cb0d25995d182b361c8237852bafeab8bb951de99f730da2913a239400c322", "da04e6e4446b6bcd54667b741444d826abbc5b76572d28474dfa94db91144606"), ("2753423b83bba5bb8b2799bc58125c46fb03ec05e8579d772cdc6e75b0875009", "ee040010ec20e7293431a3692d06416f71162d176a316b0329a76edf6f3fb30d"), ("cb73ec9b67355ce6275b51144a0759de28bb9390aab20514ee49a3bd8ce5361b", "c7c6e2ad3efe1f03b398241bddb67b008dd5e8caf1a17db9c33d2e2388d58e0d"), ("bdb386d9f3fe5613ede926d80246682a5d32392f7f6c9d818a80d8e7a12e371b", "21d48c74f422ef72cb9db18799916b3c11d0cb99cec808563e2db22047840902"), ("60f2fcfe341034449046c1fa330f6aceff737a9837a7d6d3a4885f8afce4a809", "e4c8f26f9e4127511b5ab21f1705913d7beaa2767cb7d033564e36bd2693370c"), ("1223d64fb5c44921c1ae66d91543780634c2f7bae7e184e09c4e5447e6d1180b", "290ab84714fc3534c5ba22b2aa7696ac03ed12c9cf1c409a777bac05475ea406"), ("b79c593316c42280d316bb7f3b43c7a5f5e29786d1ca6c0424463470eefe2b2f", "f4e44ac9574c4d32ab403e71dd00554eee0d8e34d04611b66ab2e59c49bff025"), ("e6548f669ebe655cb6600432e1af14c43da7286e3620289b51cc947517db9c14", "ace261e3964a1dcb389a693f52104018cd475e6856ae37fe4892df9482954902"), ("81be54409c9364f4842209e0f79b190afd9df017cc9790e11196d4bf5108f100", "d986b22e422703e065f12b6fd608813028ff913d4ffeb54b19e6537456391b23"), ("940ff0d3b4549d1baf7ad900cf6aedde02833b777e39c411e6efe7bcdd2ef305", "d672bb8a6e75b47fdbb365de0516f3fa827b287c9666e64d1b6768e2c8949412"), ("b4fe300aa1c8c836b3cc4c167df86d08fb7213200b8d9f7ba7963170e6dc8c0a", "5f7db6a9867f3f5c9fd1955c02cf96056d1225be950cd432818f8f1c16152020"), ("2c34a2bdb3738cb78270207dcae40ae47178bf4216bd2044ba124ec49240522a", "8c490c78d7d735ab32493510cc2ec9c3742d3f47e46cfd754859e91271673e15"), ("fc03857a3ff92d17d40bf9bb9b99e01e85fe4c698450147dc74cde60b2e9fa2d", "c0a22acdababbc42bba5bc0245a2ef9f2c74b215663440aed37c8e0bd7e00809"), ("a267a96a35c63b03788e90bb9a440da9ac832b0be02537b6f0ecc928ea989223", "636ceacda5bce999ca303b7df32e9352af10083c6db6ed93c8a221efc385172d"), ("ce4e15a2c1667a9dac4c4732de6c91f8c523c449f8c9be6b895644460717a209", "d55c6523f40b36d2519b30292873bd469aeda6dbdbafe7c5fd405438dcc6bd12"), ("4c617cdea4e3cb008c396c59b701375409304d84cfa17fe9d6d91a15fa412306", "a6dc21a9c35418a39c69f2d8e9df738db0b36fecc44eddf364846b84c5362819"), ("8c33aa2b3d94f9b11cc62ed1a304b92be370384ebbbf67c886dcd6ce544cc806", "7caca2b1ab8c70b58ba36794a988fd6ec50c2a04dd09d7cd28e67ca1f1401d2c"), ("1b6fc8f4007e6cdcf89a496cf0e711b0a3bbdbbae66830450a9744f5c802a228", "c3b79478bd32feb99a0c92001efb97953efdad8c2338ed2ac14a313592c12418"), ("0c6837e83ee0f1b1e5fe31bdf0960aefa2162ef7de7c0c886df930839bd4db06", "8b2667b5151ab13f0f87fb014cd3ebb7ab1e92a528f00ebd91b514ea61b8f52b"), ("8da38060963597c34544d8b10432a2e34d8eada7d1479f4fa5e96dca32250c27", "7bf525115a8e6f772feb6d4db0f2a6b0d4233e0ff85e180c3249e18f0d08f42e"), ("50b0c6d85f6a9d11fc52f1fc9e43b778380bdf936ddfd293940c77e79bc8340d", "bbb9e297de81ef341e032484d7b33b29e7d9e3be1414feaf2bbc0957ba408811"), ("6e8fd3ad20cf32ff8ba6e4bd8d4f05f3cc20d88631cbd3aa5f7f98446d725108", "a9fe7404613b040412049a205301f2175b53d7012087047a8dc7501a381f5210"), ("c291bb32d69cfef7e80b6dadd2a1198d05ac23eb30be6503d1c4c04350de2028", "f44f8ca09d849551f72a6692c793319eefaa0c02d3301ae79c2f869f4251d422"), ("116d4c2e21101f819a87f0de31dec56a4649c9ad9e539b71680dd439b288681d", "9f4df65e05390be63bd6e3e38d8afb6117d5d357d56f35ecf63406096fdd810c"), ("1813ed3760299a16e623084a852de9b12caaf8138744f8903a357fa9a2dbb00c", "f37baacccd07bc489f50b851f807c88a425e41dd9dda8068e75cffb8bf653a0f"), ("545e62d72cbb89db1c17e7ad8bb0660a74f8bc411b16eb0452d9a2d7c3e5b01e", "96ddc93a86b091c7b1951d7371f80a352d85c38606e450aa2af272e8929fae19"), ("0f5d5941474697c21ea6aecc29ff7cadbd28dbf29531431a40aaea3b7acd1514", "e83d0ff3ab4db81fb19c1b6125d7d2e4a34a83a8d463049d06df4870dbb2a525"), ("52605bc1f48092f9d5ad4c2a840a60f129e6aef2eef9c92474951ed1e10e381c", "222df3afb8b20685c872f29ebfc7e4056fcdafe1d8a77a9f9ba08ac8f426190c"), ("eee8cdee57e9f11ad0174c1514bba4c3a2c2a10d099876195726217445462f0b", "a000e5799ac933154bae228c09b522c714fb9a57f530d1954947277be9db0a24"), ("8c7d04dc3916370f8186dcffc92ad482294ab49c9ce36a5bd13473dc7c4d491a", "788e35bcd82efbce15b3444f36330337ce3adb67bfc6329149cd25069ac9eb19"), ("004a720f9edabb7f82eb30a78da45aceccbca5eb69583e1e8018ec1359a61f27", "e3d319d64bea8069a649acd33859b22361da799e0296f3d4f5117e16feb18e16"), ("38716f1d7cde7e37795a5b2d6d6317eecbf0adc6dcf7dcc6ee02cc25d1efb22f", "95c229d0cfc1485f2be0a23de6e49601a2ea55652beead84a67cd727c21a1301"), ("22cac7a49e99f3d071812abb0addafa4bf9a65308769728e4cd53cc7486f6c13", "a2e5b90606809964bc20d776b9cbcaab93a2f25124998ae3bd698d061f7afd00"), ("5109c7a41f73baa4787358b72ff1095439602add7a86a034b87b74360ee2e20f", "a31b3e2033ed828b5a51b9428f8f6ea40267259df08fdcd2c0e34dd335bdf90c"), ("957a87470f29a135567085c3d6d6ed14885bab4eb659725534a45a9f100a471c", "8f3005df282d5a87fa33405e35a313233d05731e87cfcbe060fa067596fa3013"), ("e0dad57606c2b293dc7c841c965cb29736d2411003e9284a0ae94d13e3d03d2e", "0543643ef0b617030dcc292451ebcace8bad20706528cb6aedb98dcea66aba27"), ("f1f10938c8a55b6b15b3f12beebb702133401135937c5c3f2d7fba702f24da09", "dc862a8a5ad5107421d550731a7f561e4064878c3654bf88b230cc249e91091d"), ("4bbc08d78c9970235778d6bd9939c7b2b1bd88b9d1cde6473663ae96ae776911", "3584be9ce31a7fb3aae8515011f4d3eeb86a573b225a88577e4911050bc35013"), ("c8325b31c9295757ca23d8f5256eebc5ccc517d28e00bfda5f4d709441d66713", "b81f360903160485c470625519cb18219b44d8b740273ef742fefc5653daf009"), ("69a6b3e22fbe5879efa56cdb5d50605732bf7f311e28ba037916b4db61ae8a14", "10ddf800eb3e67da20575456150cb1f0d49506d97ed4ef2d91b951af48966924"), ("c2a0e3586d4bb49f2ef979686c3a9a1619d0d54ac89641d592b4628b19dee401", "bc3ba677f6d13ef9f023d4a3b9f073c3eea910ef90cfd24b7f54414d2d02d315"), ("5b89051b79ff37457760d7af2ebc68be955e47eb6cdf306fbd369dc19fc52c05", "7bfe2a9d5bb2ade0f9058ea27a07c867af21670d2b9e84bdcc8967d4cdc4ba11"), ("46ac45f35cbc23bf68906933ad29240054a0a1d89c1832ebb54aa1bc32644105", "e891e783d5615a1b8ab838f68f6f2ce4e359510ddd40882d4650327d08a5bd11"), ("581e067b37c40caf861c190922f816e6cb850540df7ceb159f96c48c1c70cd23", "0d49fa8c74202369c36f4121eab0aa3ac9206ad3419fc9517a88493b07d6fb25"), ("bb7eb4ba2b45a22e14fad963c04eadbe8e7aab6ff912b008c9e7dd2a2c7d3615", "758ed0c8cef51f82508072fa758265f0f0eef6c7b2401b94ebe27a638ce33125"), ("a08f3cc9904f672b94d1f5a14dedbdef4ed229da5b66eca0135090d6f0ba0728", "542c952395d241cfa15bd48922e9c7fc292dbdd120daa3d66dad67fb3abb700a"), ("105bac5c449441bd5f5cf39b431f703e2e8f6a30a90d86cfdf425ec728fdbd08", "b59206f1d556fb98329a7111e3c89bf1e6a4861ec38c82eafa81cff70b713e0d"), ("45df2edfaea234dab99efa9a402c0d2feaa841ff3c990c926473173283d95913", "db008cf922c3a50549373a14f5f18b2dc827bd6e168402dfe3e6ece4cb137527"), ("488dd6437c15ef5b85e84407b7e599cc078c195ed2fc27b366ddac7739f1eb14", "0448887f55c677464e7e540adf107ba10fc6713e8a2718fe92cbd4794ef4be17"), ("3fe697698f3504e15eb5d509efa91624d9aa4eae24beeb42ffcb3c6ea8372a30", "441ccb28e7c6c1893a58f7513c9b7c1c5d0adf3de452644a837d7e08e1ab8117"), ("9bb5801272264c74db75f1c6812747bf338ea6880fa4dd2d51ef651ef73c2e04", "e2eefd80e838c30b3f1fb0313fcfcb1e1556439d0346d17334df83c33247b20e"), ("bad37a1467c4fe875b78508656f2816414ef602ae2ccd4e9430d94ca5c1cd911", "21c72ccdbaed2e3ea9dac3881531467b64c9322199127f2ae2fa4bb31bad591d"), ("95c72237ccbaedc185b1abfc59059c454175df81bb3ff65e5a61e2cb5263ed0d", "1040728a4775ac5cf7a9c75dda2f0aee1fabd6b202b5e916636e3aec73a09d0d"), ("738e8b9af4d199bc95f70b2a9b25e999b39af15c6a02f2eca058326078745f2a", "004835960aa03e101d818907df453cefdfb910b44ad9bd056a83b2ef40a5ca0d"), ("0d88caf486f2fe60bfc08697d1b617b586ec880e38e4ff56f145a5db0ab6d12e", "a7b19323815295bc84c50535b62c3d4d53dbfc434441eb3c009568fb8f7b2b1a"), ("b37af7fd314d90590fba8ebf730397cfdaa97f86d3f31d1a7ddc817ff303791f", "8296a80eceae97f92784642da34f121e29db2c5f3baf9a2722896db22e98b703"), ("6b67dd3051a8cb113431f5a14e279d910b7b4798ccb03ff588cf312ffe366a0b", "9c38cc543a0b793be4a0d66de3b2cd30b1f33d36c4402a165da924d7e6f89a12"), ("72fc4c40e406b9d1b34daba614460922fc53951abd6db5834e1f1b07fa319f27", "daa4d50481ba47c4a78ae3848a9b268b1d8e0579b4967ebcdece47d386410d02"), ("a4ce5cb2b2a23dcf019f275058a4092901c4793e6ee7d29282e755dd0d0df000", "00749453d23051f3587911c34d52e2c2d093273d2283cd6dfba94d7a89cdb226"), ("9e00df0216e8ac8e72abfeab1106373a13699714c691f04a549f6b58ea974521", "0e69dbfd9023b8bb58ce7e17972d7c94d49e8464f9c22161d7564ed32c53ef27"), ("fe1a558c6315f425fea7c04dcf6db869acd7b62b1b848200d5709e73d53de71b", "66cd33cc8d61cb807092d76ac0e506014fc55624a39ee2afa67f9ba58eb21022"), ("3ca542e17e4cbc74f47afb399b12295d15dfbae5e966509b7e6cae2df61a430a", "57a0df429ed3720dee61ca720a4617243b41472f0c7766cc7ce625afddb3b41b"), ("c1a77855e9b0b1f7381c9acd69ff68fdfa65f1f753dcea22de5a28ed088c2b2e", "4fce521d725abb8a30d8c39a2a22f496b374d8512b7a76afd3803f7200c86d19"), ("17cda8d590adf3042bbf1452fe3d79b959f284f3df1e15ee3c696286f0ba832e", "8919e92a175b55003379ca6e546a04228aa9da861a9669e0e9b106e4a204d404"), ("959fa9337b4b479ce5fcff7ff9096a344acf7fcde2f8852e28904236472f2f02", "79e688ecfece239223b745e4b95460bfcb02aee12b154e15db616fda857dcd06"), ("d062d34cbdfbc50004a631e3dd8f21b446673b53830aa71fdcbc741e7d4fb31c", "2203af0127657f4e8a1c5ec44bb25028e64ad388efd0b9bfe2105f4f3ff66d0f"), ("d2d3e3034864944914709549ce27068b5f27bd18b0edc940593df2dc5c5dae23", "5ae75206bfdc8f54a8673d5d4c4b464ba1e84ae2410418b5c5e98fa993608113"), ("4ab457d6478fb27214345f0bc2e00cbb29cd55bed1f8e05c23b8f7810e134a1f", "3db37711e355ed46ea81bb6e31ae87b37b706a8d17e2aa976c7c7706c8e18d19"), ("5aa17884c96ecdaf81e2b343dbf051e4ace41935e53378ac10a4387e2cb0c404", "7118986268a748881f6144fc1008e90b40afc702be53784ed4cbceb8606e0714"), ("82f4ca6b751ac067e451ae1d5115806e7a1d14ab444084a8ac48046b88b43b30", "8a09186c2a5cb0f16e273be6a8710620006a77afe7c54149bea3dce906345a02"), ("55d5d97de81e0cc4d2ac97e507d4d6d9874cb55e1522b89ed2050ff3c20a4912", "27facefe4e2041e11a21889205af6c11eba73e3e8779b801bf3f7253730f1d04"), ("845177f4584f61071ddea21e9a6c0a17e1a9408e0c073047c339498ff383c109", "25bc572d1c861fe80156c2fd440e7d9110be8252723d276e806dcc012f8da103"), ("ecec46dfdaf45f49f82ee78fd9c8c747c049de9becaf440f90391c04dd119002", "b922e1a7caf72db69dcc062424c7ce75728d010c4c201c2ed8e635a617c81a1f"), ("8e265fd9f2a4b158a5eb4bda000a6124af909e118b7510f0ec418559aad85825", "aed92a21e9933bd2ce6c9f09bb7753b46ea31248b324c12726cff08e464bdd0c"), ("99d7181572d3ae637a277dcc0c35b9b98bbb6ecf3cdcc69af3e946a5bfeb520f", "89217b554371a2495ee473f867b2e26ea70d4245aceb462a98dd31a37d2d5330"), ("a5e1fd149ae27f34e7902ebf380277c0b7f09f37e8809d25a0b8df2965d24e29", "2d6a16a254cfc3894d5500f8c40feb1e84876bc51c68c381109e83f001b41c2f"), ("7fd221da235bb6e5fd0ae047f1aa49615ddbeecfb71ccb976bf3e0534425661c", "9ea41ccc3e09572927b0d9f618e1135d384f33618b6a0f80002f0a70be5eb324"), ("b125c1b45daa68f96be6a5b3e4dd24e4a0e49e46226d841eb73754a498c43b21", "360b9c39d25451ae58cf651c530155f91e324292b55475a92506de726f153c18"), ("d64536234849ababefa90b84f7b7cacf4b073809aa9b0c35581426521f18d81a", "d2dae030312cd4325bb036aa3436b26b9bed69b4d78d68bd49dbdde3173f1510"), ("d1ce3aea6cfb7be132d17e8d76fcbe4b7e34cef3979b4b905acfeff2f6d19724", "be47b76297791f535f4b56f973a19f07ec22d4eede2a41ff23c696089938bb21")] + STATIC_GROUP_KEYS* = @[("e9a4d05b1f539d65c59015a079ee89aabeafbcfc9734342d9559f81601e85417", + "b74d3a5b3200ab1126fbee393496f33da497d4d9a7c56693f44d6155c0c34e13"), ( + "27b2bfc25257e53819beaf36ce1070007e04e7aad2e440a1f1fc066f59a61123", + "522ce51aff96041e79a8476f508fb9661f146f189e288f83cb4837517cfc0127"), ( + "66392eaae6674267c55fe393d39443ba90317a709d6e8f92a9f3e4abc18eff1d", + "e3dc235e48c1811943fc249fecd0f1415a50ebe839ccefb0bd820a76fb77ba2a"), ( + "e7462eebb81405230db8014b052d65fe7b269c3870e40b12cf64668ed6c2d40e", + "727df0965e34144ea637be18208cc81e57e423010b3159c20f0ccff45c42212a"), ( + "1ad8528b4c7075013a2d6561a02517b0482c0733dc018ac68774db857deb5004", + "5df3a77577135784da2a9ee78f5026092b7d6bb9e6e95882d6cb172c0cb62208"), ( + "aa0ff53bfc50861f871d94df18c3ac0b97f44ceb13436b33490cec5f6ce8e700", + "30421d05b905aeaec0473ba29ace034bf73c406866d7dc23007eb9c34a596827"), ( + "0448c0a6ed57b177c4c45de478b58d29f24e7ea842814305443e87188ae24324", + "5ea1a704d8972af5a028367c8e3fb48ee61a603c6ea3a4c9247b0f611a6ab002"), ( + "4b6a6edbdd11e69befe3f4a3c976baca320c4bcb188f129b603ebb198f663000", + "451d6185e8ad2c8873f034683b9caca43ea7ceb1b839abd3e01c3f19f3e6bd1e"), ( + "c3fba34855b33f025696326d2980ddc3fb47d90459ed6a4488fbb2e4e12ccf2b", + "74674a86144ea866ad8fe633e256783bda4a07b997cb412c53a5eaf4cc7b6a0e"), ( + "0c35b8b94a720f1c26d7c6241c9f3ea5332a87cf3730b25ef31b68854c10e405", + "6915df8d8ad19ec17be37c299eed762f9b63e841cd7963e13e8db6890dba082d"), ( + "a60b021677da95ba46c8c3411ac77f3e3b06937a8d189517111c045880029909", + "bbfcd22ecb44cec6fd0717cd0f21b26e6e8b2c91e1a6cb5d8610e2f2ad41c90f"), ( + "2f4d662e66fbe754b708b87ea3d75a01d2ea4d7bf33c615c2376211dcc3b560f", + "c67f7e622c3293028b9f86571e82c49551eb5fd308a35eb663498cffac208810"), ( + "d90a1afa96c14c8d3b989a9cf23d6e8b9907da42724e44a3ac74ae015b6ce22e", + "f0795b1bdd0a907252b6ab047642d97be076a16ea69d463f1a4bec00c817202e"), ( + "dfa0764d89c8da10777504e5274f1baccf8b4145deba72b26503474318fb6410", + "9f8aa8e833ea2f13cccfb6d9f2f04fd7be9c9f3019540c05c1986b3bce254e25"), ( + "e17490013b6b53a40964ff1067b922d4d73521e32fe394527b39c1bfd4a5e712", + "7a8fa23a0e4b14a36f2818a7d98639f6e4934c028da780a6cc658fbf76e80a26"), ( + "7a9328d1075373dddb1b1100e8217ffaa1f9b632911b95a8fdc08870b15a8410", + "94b2ae70c046b94873098c19fe18e7b17db2d31fe6a7eb73fea8168395e3c122"), ( + "ce319bb1447da5bf51a88ab3379dbea539b5a431d3c4f131048cf7b05c52161d", + "cdfa264ab8a51bbc9fff5732cf544bb06abba7e807a8c252a5f9ce785c6ff22c"), ( + "d3818503bfcbef9ca03fc4472be77cb4936a1720001fc5e54852f769448fd313", + "e72792abd906976c75a5670de514894a720c5293cf74338a9c2987640a949c0b"), ( + "4f9b8d118460736eb62602d12d3aed62938d4d4374b8c88704cf40c415c0901e", + "2ecbe4588ade31924a1053fe0204950a0b4924878b312e56b2d0522f92a5f01c"), ( + "39b59fd96adaf9633edcc8cef10049cddf5f7df8ac80af8aafa436d62ee7f905", + "b94f7a979df8a95fc2766a9a96308ff39a14daf7b7d6bc48591d2ea4c764bf00"), ( + "73cb0d25995d182b361c8237852bafeab8bb951de99f730da2913a239400c322", + "da04e6e4446b6bcd54667b741444d826abbc5b76572d28474dfa94db91144606"), ( + "2753423b83bba5bb8b2799bc58125c46fb03ec05e8579d772cdc6e75b0875009", + "ee040010ec20e7293431a3692d06416f71162d176a316b0329a76edf6f3fb30d"), ( + "cb73ec9b67355ce6275b51144a0759de28bb9390aab20514ee49a3bd8ce5361b", + "c7c6e2ad3efe1f03b398241bddb67b008dd5e8caf1a17db9c33d2e2388d58e0d"), ( + "bdb386d9f3fe5613ede926d80246682a5d32392f7f6c9d818a80d8e7a12e371b", + "21d48c74f422ef72cb9db18799916b3c11d0cb99cec808563e2db22047840902"), ( + "60f2fcfe341034449046c1fa330f6aceff737a9837a7d6d3a4885f8afce4a809", + "e4c8f26f9e4127511b5ab21f1705913d7beaa2767cb7d033564e36bd2693370c"), ( + "1223d64fb5c44921c1ae66d91543780634c2f7bae7e184e09c4e5447e6d1180b", + "290ab84714fc3534c5ba22b2aa7696ac03ed12c9cf1c409a777bac05475ea406"), ( + "b79c593316c42280d316bb7f3b43c7a5f5e29786d1ca6c0424463470eefe2b2f", + "f4e44ac9574c4d32ab403e71dd00554eee0d8e34d04611b66ab2e59c49bff025"), ( + "e6548f669ebe655cb6600432e1af14c43da7286e3620289b51cc947517db9c14", + "ace261e3964a1dcb389a693f52104018cd475e6856ae37fe4892df9482954902"), ( + "81be54409c9364f4842209e0f79b190afd9df017cc9790e11196d4bf5108f100", + "d986b22e422703e065f12b6fd608813028ff913d4ffeb54b19e6537456391b23"), ( + "940ff0d3b4549d1baf7ad900cf6aedde02833b777e39c411e6efe7bcdd2ef305", + "d672bb8a6e75b47fdbb365de0516f3fa827b287c9666e64d1b6768e2c8949412"), ( + "b4fe300aa1c8c836b3cc4c167df86d08fb7213200b8d9f7ba7963170e6dc8c0a", + "5f7db6a9867f3f5c9fd1955c02cf96056d1225be950cd432818f8f1c16152020"), ( + "2c34a2bdb3738cb78270207dcae40ae47178bf4216bd2044ba124ec49240522a", + "8c490c78d7d735ab32493510cc2ec9c3742d3f47e46cfd754859e91271673e15"), ( + "fc03857a3ff92d17d40bf9bb9b99e01e85fe4c698450147dc74cde60b2e9fa2d", + "c0a22acdababbc42bba5bc0245a2ef9f2c74b215663440aed37c8e0bd7e00809"), ( + "a267a96a35c63b03788e90bb9a440da9ac832b0be02537b6f0ecc928ea989223", + "636ceacda5bce999ca303b7df32e9352af10083c6db6ed93c8a221efc385172d"), ( + "ce4e15a2c1667a9dac4c4732de6c91f8c523c449f8c9be6b895644460717a209", + "d55c6523f40b36d2519b30292873bd469aeda6dbdbafe7c5fd405438dcc6bd12"), ( + "4c617cdea4e3cb008c396c59b701375409304d84cfa17fe9d6d91a15fa412306", + "a6dc21a9c35418a39c69f2d8e9df738db0b36fecc44eddf364846b84c5362819"), ( + "8c33aa2b3d94f9b11cc62ed1a304b92be370384ebbbf67c886dcd6ce544cc806", + "7caca2b1ab8c70b58ba36794a988fd6ec50c2a04dd09d7cd28e67ca1f1401d2c"), ( + "1b6fc8f4007e6cdcf89a496cf0e711b0a3bbdbbae66830450a9744f5c802a228", + "c3b79478bd32feb99a0c92001efb97953efdad8c2338ed2ac14a313592c12418"), ( + "0c6837e83ee0f1b1e5fe31bdf0960aefa2162ef7de7c0c886df930839bd4db06", + "8b2667b5151ab13f0f87fb014cd3ebb7ab1e92a528f00ebd91b514ea61b8f52b"), ( + "8da38060963597c34544d8b10432a2e34d8eada7d1479f4fa5e96dca32250c27", + "7bf525115a8e6f772feb6d4db0f2a6b0d4233e0ff85e180c3249e18f0d08f42e"), ( + "50b0c6d85f6a9d11fc52f1fc9e43b778380bdf936ddfd293940c77e79bc8340d", + "bbb9e297de81ef341e032484d7b33b29e7d9e3be1414feaf2bbc0957ba408811"), ( + "6e8fd3ad20cf32ff8ba6e4bd8d4f05f3cc20d88631cbd3aa5f7f98446d725108", + "a9fe7404613b040412049a205301f2175b53d7012087047a8dc7501a381f5210"), ( + "c291bb32d69cfef7e80b6dadd2a1198d05ac23eb30be6503d1c4c04350de2028", + "f44f8ca09d849551f72a6692c793319eefaa0c02d3301ae79c2f869f4251d422"), ( + "116d4c2e21101f819a87f0de31dec56a4649c9ad9e539b71680dd439b288681d", + "9f4df65e05390be63bd6e3e38d8afb6117d5d357d56f35ecf63406096fdd810c"), ( + "1813ed3760299a16e623084a852de9b12caaf8138744f8903a357fa9a2dbb00c", + "f37baacccd07bc489f50b851f807c88a425e41dd9dda8068e75cffb8bf653a0f"), ( + "545e62d72cbb89db1c17e7ad8bb0660a74f8bc411b16eb0452d9a2d7c3e5b01e", + "96ddc93a86b091c7b1951d7371f80a352d85c38606e450aa2af272e8929fae19"), ( + "0f5d5941474697c21ea6aecc29ff7cadbd28dbf29531431a40aaea3b7acd1514", + "e83d0ff3ab4db81fb19c1b6125d7d2e4a34a83a8d463049d06df4870dbb2a525"), ( + "52605bc1f48092f9d5ad4c2a840a60f129e6aef2eef9c92474951ed1e10e381c", + "222df3afb8b20685c872f29ebfc7e4056fcdafe1d8a77a9f9ba08ac8f426190c"), ( + "eee8cdee57e9f11ad0174c1514bba4c3a2c2a10d099876195726217445462f0b", + "a000e5799ac933154bae228c09b522c714fb9a57f530d1954947277be9db0a24"), ( + "8c7d04dc3916370f8186dcffc92ad482294ab49c9ce36a5bd13473dc7c4d491a", + "788e35bcd82efbce15b3444f36330337ce3adb67bfc6329149cd25069ac9eb19"), ( + "004a720f9edabb7f82eb30a78da45aceccbca5eb69583e1e8018ec1359a61f27", + "e3d319d64bea8069a649acd33859b22361da799e0296f3d4f5117e16feb18e16"), ( + "38716f1d7cde7e37795a5b2d6d6317eecbf0adc6dcf7dcc6ee02cc25d1efb22f", + "95c229d0cfc1485f2be0a23de6e49601a2ea55652beead84a67cd727c21a1301"), ( + "22cac7a49e99f3d071812abb0addafa4bf9a65308769728e4cd53cc7486f6c13", + "a2e5b90606809964bc20d776b9cbcaab93a2f25124998ae3bd698d061f7afd00"), ( + "5109c7a41f73baa4787358b72ff1095439602add7a86a034b87b74360ee2e20f", + "a31b3e2033ed828b5a51b9428f8f6ea40267259df08fdcd2c0e34dd335bdf90c"), ( + "957a87470f29a135567085c3d6d6ed14885bab4eb659725534a45a9f100a471c", + "8f3005df282d5a87fa33405e35a313233d05731e87cfcbe060fa067596fa3013"), ( + "e0dad57606c2b293dc7c841c965cb29736d2411003e9284a0ae94d13e3d03d2e", + "0543643ef0b617030dcc292451ebcace8bad20706528cb6aedb98dcea66aba27"), ( + "f1f10938c8a55b6b15b3f12beebb702133401135937c5c3f2d7fba702f24da09", + "dc862a8a5ad5107421d550731a7f561e4064878c3654bf88b230cc249e91091d"), ( + "4bbc08d78c9970235778d6bd9939c7b2b1bd88b9d1cde6473663ae96ae776911", + "3584be9ce31a7fb3aae8515011f4d3eeb86a573b225a88577e4911050bc35013"), ( + "c8325b31c9295757ca23d8f5256eebc5ccc517d28e00bfda5f4d709441d66713", + "b81f360903160485c470625519cb18219b44d8b740273ef742fefc5653daf009"), ( + "69a6b3e22fbe5879efa56cdb5d50605732bf7f311e28ba037916b4db61ae8a14", + "10ddf800eb3e67da20575456150cb1f0d49506d97ed4ef2d91b951af48966924"), ( + "c2a0e3586d4bb49f2ef979686c3a9a1619d0d54ac89641d592b4628b19dee401", + "bc3ba677f6d13ef9f023d4a3b9f073c3eea910ef90cfd24b7f54414d2d02d315"), ( + "5b89051b79ff37457760d7af2ebc68be955e47eb6cdf306fbd369dc19fc52c05", + "7bfe2a9d5bb2ade0f9058ea27a07c867af21670d2b9e84bdcc8967d4cdc4ba11"), ( + "46ac45f35cbc23bf68906933ad29240054a0a1d89c1832ebb54aa1bc32644105", + "e891e783d5615a1b8ab838f68f6f2ce4e359510ddd40882d4650327d08a5bd11"), ( + "581e067b37c40caf861c190922f816e6cb850540df7ceb159f96c48c1c70cd23", + "0d49fa8c74202369c36f4121eab0aa3ac9206ad3419fc9517a88493b07d6fb25"), ( + "bb7eb4ba2b45a22e14fad963c04eadbe8e7aab6ff912b008c9e7dd2a2c7d3615", + "758ed0c8cef51f82508072fa758265f0f0eef6c7b2401b94ebe27a638ce33125"), ( + "a08f3cc9904f672b94d1f5a14dedbdef4ed229da5b66eca0135090d6f0ba0728", + "542c952395d241cfa15bd48922e9c7fc292dbdd120daa3d66dad67fb3abb700a"), ( + "105bac5c449441bd5f5cf39b431f703e2e8f6a30a90d86cfdf425ec728fdbd08", + "b59206f1d556fb98329a7111e3c89bf1e6a4861ec38c82eafa81cff70b713e0d"), ( + "45df2edfaea234dab99efa9a402c0d2feaa841ff3c990c926473173283d95913", + "db008cf922c3a50549373a14f5f18b2dc827bd6e168402dfe3e6ece4cb137527"), ( + "488dd6437c15ef5b85e84407b7e599cc078c195ed2fc27b366ddac7739f1eb14", + "0448887f55c677464e7e540adf107ba10fc6713e8a2718fe92cbd4794ef4be17"), ( + "3fe697698f3504e15eb5d509efa91624d9aa4eae24beeb42ffcb3c6ea8372a30", + "441ccb28e7c6c1893a58f7513c9b7c1c5d0adf3de452644a837d7e08e1ab8117"), ( + "9bb5801272264c74db75f1c6812747bf338ea6880fa4dd2d51ef651ef73c2e04", + "e2eefd80e838c30b3f1fb0313fcfcb1e1556439d0346d17334df83c33247b20e"), ( + "bad37a1467c4fe875b78508656f2816414ef602ae2ccd4e9430d94ca5c1cd911", + "21c72ccdbaed2e3ea9dac3881531467b64c9322199127f2ae2fa4bb31bad591d"), ( + "95c72237ccbaedc185b1abfc59059c454175df81bb3ff65e5a61e2cb5263ed0d", + "1040728a4775ac5cf7a9c75dda2f0aee1fabd6b202b5e916636e3aec73a09d0d"), ( + "738e8b9af4d199bc95f70b2a9b25e999b39af15c6a02f2eca058326078745f2a", + "004835960aa03e101d818907df453cefdfb910b44ad9bd056a83b2ef40a5ca0d"), ( + "0d88caf486f2fe60bfc08697d1b617b586ec880e38e4ff56f145a5db0ab6d12e", + "a7b19323815295bc84c50535b62c3d4d53dbfc434441eb3c009568fb8f7b2b1a"), ( + "b37af7fd314d90590fba8ebf730397cfdaa97f86d3f31d1a7ddc817ff303791f", + "8296a80eceae97f92784642da34f121e29db2c5f3baf9a2722896db22e98b703"), ( + "6b67dd3051a8cb113431f5a14e279d910b7b4798ccb03ff588cf312ffe366a0b", + "9c38cc543a0b793be4a0d66de3b2cd30b1f33d36c4402a165da924d7e6f89a12"), ( + "72fc4c40e406b9d1b34daba614460922fc53951abd6db5834e1f1b07fa319f27", + "daa4d50481ba47c4a78ae3848a9b268b1d8e0579b4967ebcdece47d386410d02"), ( + "a4ce5cb2b2a23dcf019f275058a4092901c4793e6ee7d29282e755dd0d0df000", + "00749453d23051f3587911c34d52e2c2d093273d2283cd6dfba94d7a89cdb226"), ( + "9e00df0216e8ac8e72abfeab1106373a13699714c691f04a549f6b58ea974521", + "0e69dbfd9023b8bb58ce7e17972d7c94d49e8464f9c22161d7564ed32c53ef27"), ( + "fe1a558c6315f425fea7c04dcf6db869acd7b62b1b848200d5709e73d53de71b", + "66cd33cc8d61cb807092d76ac0e506014fc55624a39ee2afa67f9ba58eb21022"), ( + "3ca542e17e4cbc74f47afb399b12295d15dfbae5e966509b7e6cae2df61a430a", + "57a0df429ed3720dee61ca720a4617243b41472f0c7766cc7ce625afddb3b41b"), ( + "c1a77855e9b0b1f7381c9acd69ff68fdfa65f1f753dcea22de5a28ed088c2b2e", + "4fce521d725abb8a30d8c39a2a22f496b374d8512b7a76afd3803f7200c86d19"), ( + "17cda8d590adf3042bbf1452fe3d79b959f284f3df1e15ee3c696286f0ba832e", + "8919e92a175b55003379ca6e546a04228aa9da861a9669e0e9b106e4a204d404"), ( + "959fa9337b4b479ce5fcff7ff9096a344acf7fcde2f8852e28904236472f2f02", + "79e688ecfece239223b745e4b95460bfcb02aee12b154e15db616fda857dcd06"), ( + "d062d34cbdfbc50004a631e3dd8f21b446673b53830aa71fdcbc741e7d4fb31c", + "2203af0127657f4e8a1c5ec44bb25028e64ad388efd0b9bfe2105f4f3ff66d0f"), ( + "d2d3e3034864944914709549ce27068b5f27bd18b0edc940593df2dc5c5dae23", + "5ae75206bfdc8f54a8673d5d4c4b464ba1e84ae2410418b5c5e98fa993608113"), ( + "4ab457d6478fb27214345f0bc2e00cbb29cd55bed1f8e05c23b8f7810e134a1f", + "3db37711e355ed46ea81bb6e31ae87b37b706a8d17e2aa976c7c7706c8e18d19"), ( + "5aa17884c96ecdaf81e2b343dbf051e4ace41935e53378ac10a4387e2cb0c404", + "7118986268a748881f6144fc1008e90b40afc702be53784ed4cbceb8606e0714"), ( + "82f4ca6b751ac067e451ae1d5115806e7a1d14ab444084a8ac48046b88b43b30", + "8a09186c2a5cb0f16e273be6a8710620006a77afe7c54149bea3dce906345a02"), ( + "55d5d97de81e0cc4d2ac97e507d4d6d9874cb55e1522b89ed2050ff3c20a4912", + "27facefe4e2041e11a21889205af6c11eba73e3e8779b801bf3f7253730f1d04"), ( + "845177f4584f61071ddea21e9a6c0a17e1a9408e0c073047c339498ff383c109", + "25bc572d1c861fe80156c2fd440e7d9110be8252723d276e806dcc012f8da103"), ( + "ecec46dfdaf45f49f82ee78fd9c8c747c049de9becaf440f90391c04dd119002", + "b922e1a7caf72db69dcc062424c7ce75728d010c4c201c2ed8e635a617c81a1f"), ( + "8e265fd9f2a4b158a5eb4bda000a6124af909e118b7510f0ec418559aad85825", + "aed92a21e9933bd2ce6c9f09bb7753b46ea31248b324c12726cff08e464bdd0c"), ( + "99d7181572d3ae637a277dcc0c35b9b98bbb6ecf3cdcc69af3e946a5bfeb520f", + "89217b554371a2495ee473f867b2e26ea70d4245aceb462a98dd31a37d2d5330"), ( + "a5e1fd149ae27f34e7902ebf380277c0b7f09f37e8809d25a0b8df2965d24e29", + "2d6a16a254cfc3894d5500f8c40feb1e84876bc51c68c381109e83f001b41c2f"), ( + "7fd221da235bb6e5fd0ae047f1aa49615ddbeecfb71ccb976bf3e0534425661c", + "9ea41ccc3e09572927b0d9f618e1135d384f33618b6a0f80002f0a70be5eb324"), ( + "b125c1b45daa68f96be6a5b3e4dd24e4a0e49e46226d841eb73754a498c43b21", + "360b9c39d25451ae58cf651c530155f91e324292b55475a92506de726f153c18"), ( + "d64536234849ababefa90b84f7b7cacf4b073809aa9b0c35581426521f18d81a", + "d2dae030312cd4325bb036aa3436b26b9bed69b4d78d68bd49dbdde3173f1510"), ( + "d1ce3aea6cfb7be132d17e8d76fcbe4b7e34cef3979b4b905acfeff2f6d19724", + "be47b76297791f535f4b56f973a19f07ec22d4eede2a41ff23c696089938bb21")] # STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above # only identity commitments are used for the Merkle tree construction - # the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here - STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d" + # the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here + STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d" -const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds +const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers in seconds -# maximum allowed gap between the epochs of messages' RateLimitProofs -const MAX_EPOCH_GAP* = int64(MAX_CLOCK_GAP_SECONDS/EPOCH_UNIT_SECONDS) + +# maximum allowed gap between the epochs of messages' RateLimitProofs +const MAX_EPOCH_GAP* = int64(MAX_CLOCK_GAP_SECONDS/EPOCH_UNIT_SECONDS) # Protobufs enc and init proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] = @@ -150,9 +350,9 @@ proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] = discard ? pb.getField(6, nullifier) discard nsp.nullifier.copyFrom(nullifier) - return ok(nsp) + return ok(nsp) -proc encode*(nsp: RateLimitProof): ProtoBuffer = +proc encode*(nsp: RateLimitProof): ProtoBuffer = var output = initProtoBuffer() output.write(1, nsp.proof) @@ -162,4 +362,4 @@ proc encode*(nsp: RateLimitProof): ProtoBuffer = output.write(5, nsp.shareY) output.write(6, nsp.nullifier) - return output \ No newline at end of file + return output diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim index 4b955a9af..d7261b717 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim @@ -1,12 +1,12 @@ {.push raises: [Defect].} -import +import std/sequtils, tables, times, chronicles, options, chronos, stint, web3, stew/results, stew/[byteutils, arrayops, endians2], - rln, + rln, waku_rln_relay_types, ../waku_message @@ -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): @@ -26,14 +27,14 @@ contract(MembershipContract): proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult {.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 - ## d indicates the depth of Merkle tree - var + ## d indicates the depth of Merkle tree + var rlnInstance: RLN[Bn256] merkleDepth: csize_t = uint(d) ## 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 ## cargo run --example export_test_keys ## 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 parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key") pbytes = parameters.toBytes() - len : csize_t = uint(pbytes.len) + len: csize_t = uint(pbytes.len) parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len) # check the parameters.key is not empty if (pbytes.len == 0): debug "error in parameters.key" 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): + if (res == false): debug "error in parameters generation" return err("error in parameters generation") return ok(rlnInstance) proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] = ## 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 - var - keysBuffer : Buffer + + # keysBufferPtr will hold the generated key pairs i.e., secret and public keys + var + keysBuffer: Buffer keysBufferPtr = addr(keysBuffer) - done = key_gen(ctxPtr, keysBufferPtr) + done = key_gen(ctxPtr, keysBufferPtr) # check whether the keys are generated successfully if(done == false): debug "error in key generation" return none(MembershipKeyPair) - + var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[] # the public and secret keys together are 64 bytes if (generatedKeys.len != 64): debug "the generated keys are invalid" return none(MembershipKeyPair) - + # TODO define a separate proc to decode the generated keys to the secret and public components - var - secret: array[32, byte] + var + secret: array[32, byte] public: array[32, byte] - for (i,x) in secret.mpairs: x = generatedKeys[i] - for (i,x) in public.mpairs: x = generatedKeys[i+32] - - var + for (i, x) in secret.mpairs: x = generatedKeys[i] + for (i, x) in public.mpairs: x = generatedKeys[i+32] + + var keypair = MembershipKeyPair(idKey: secret, idCommitment: public) 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 + return true proc appendLength*(input: openArray[byte]): seq[byte] = ## returns length prefixed version of the input ## with the following format [len<8>|input] ## len: 8-byte value that represents the number of bytes in the `input` ## len is serialized in little-endian - ## input: the supplied `input` - let + ## input: the supplied `input` + let # the length should be serialized in little-endian len = toBytes(uint64(input.len), Endianness.littleEndian) 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)) return output -proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode = - ## a thin layer on top of the Nim wrapper of the Poseidon hasher - debug "hash input", hashhex=data.toHex() +proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode = + ## a thin layer on top of the Nim wrapper of the Poseidon hasher + debug "hash input", hashhex = data.toHex() var lenPrefData = appendLength(data) - var + var hashInputBuffer = lenPrefData.toBuffer() outputBuffer: Buffer # will holds the hash output - - debug "hash input buffer length", bufflen=hashInputBuffer.len - let + + debug "hash input buffer length", bufflen = hashInputBuffer.len + let hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer) output = cast[ptr MerkleNode](outputBuffer.`ptr`)[] 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 ## [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal ] let memIndexBytes = toBytes(uint64(memIndex), Endianness.littleEndian) let lenPrefMsg = appendLength(msg) - let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg) + 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, @@ -168,12 +180,12 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh if not proofIsSuccessful: return err("could not generate the proof") - var proofValue = cast[ptr array[416,byte]] (proof.`ptr`) - let proofBytes: array[416,byte] = proofValue[] - debug "proof content", proofHex=proofValue[].toHex + var proofValue = cast[ptr array[416, byte]] (proof.`ptr`) + let proofBytes: array[416, byte] = proofValue[] + debug "proof content", proofHex = proofValue[].toHex ## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>| - let + let proofOffset = 256 rootOffset = proofOffset + 32 epochOffset = rootOffset + 32 @@ -181,8 +193,8 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh shareYOffset = shareXOffset + 32 nullifierOffset = shareYOffset + 32 - var - zkproof: ZKSNARK + var + zkproof: ZKSNARK proofRoot, shareX, shareY: MerkleNode epoch: Epoch 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 ## [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal ] let lenPrefMsg = appendLength(@data) - var proofBytes = concat(@(proof.proof), + var proofBytes = concat(@(proof.proof), @(proof.merkleRoot), @(proof.epoch), @(proof.shareX), @@ -219,23 +231,24 @@ proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] = return proofBytes -proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte], proof: RateLimitProof): bool = - var - proofBytes= serialize(proof, data) +proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte], + proof: RateLimitProof): bool = + var + proofBytes = serialize(proof, data) proofBuffer = proofBytes.toBuffer() f = 0.uint32 - trace "serialized proof", proof=proofBytes.toHex() + trace "serialized proof", proof = proofBytes.toHex() let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f) if not verifyIsSuccessful: # something went wrong in verification - return false + return false # f = 0 means the proof is verified if f == 0: return true return false -proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool = +proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool = var pkBuffer = toBuffer(idComm) 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) 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) return deletion_success -proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult = +proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult = # read the Merkle Tree root after insertion - var - root {.noinit.} : Buffer = Buffer() + var + root {.noinit.}: Buffer = Buffer() rootPtr = addr(root) get_root_successful = get_root(rlnInstance, rootPtr) 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`)[] 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 - + var groupKeyPairs = newSeq[MembershipKeyPair]() - + for i in 0..groupKeys.len-1: - let + 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].} = - ## returns the root of the Merkle tree that is computed from the supplied list +proc calcMerkleRoot*(list: seq[IDCommitment]): string {.raises: [Defect, IOError].} = + ## returns the root of the Merkle tree that is computed from the supplied list ## the root is in hexadecimal format - + var rlnInstance = createRLNInstance() doAssert(rlnInstance.isOk) var rln = rlnInstance.value - # create a Merkle tree + # create a Merkle tree for i in 0..list.len-1: var member_is_added = false member_is_added = rln.insertMember(list[i]) - doAssert(member_is_added) + doAssert(member_is_added) - let root = rln.getMerkleRoot().value().toHex + 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) - + # initialize a Merkle tree var rlnInstance = createRLNInstance() if not rlnInstance.isOk: return (@[], "") var rln = rlnInstance.value - var output = newSeq[(string,string)]() + var output = newSeq[(string, string)]() for i in 0..n-1: # generate a key pair let keypair = rln.membershipKeyGen() doAssert(keypair.isSome()) - + let keyTuple = (keypair.get().idKey.toHex, keypair.get().idCommitment.toHex) output.add(keyTuple) @@ -314,12 +330,14 @@ proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [De let inserted = rln.insertMember(keypair.get().idCommitment) if not inserted: return (@[], "") - + 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,31 +346,33 @@ 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)) - + # 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) groupKeyPairs = groupKeys.toMembershipKeyPairs() # extract id commitment keys groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment) - groupOpt= some(groupIDCommitments) + groupOpt = some(groupIDCommitments) # user selected membership key pair memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex]) - memIndexOpt= some(rlnRelayMemIndex) - + memIndexOpt = some(rlnRelayMemIndex) + return (groupOpt, memKeyPairOpt, memIndexOpt) -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 +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 ## epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares ## otherwise, returns false ## 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,12 +383,14 @@ 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 return ok(true) - + # there is no duplicate return ok(false) @@ -376,17 +398,18 @@ proc hasDuplicate*(rlnPeer: WakuRLNRelay, msg: WakuMessage): Result[bool, string return err("the epoch was not found") 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` - let proofMD = ProofMetadata(nullifier: msg.proof.nullifier, shareX: msg.proof.shareX, shareY: msg.proof.shareY) - debug "proof metadata", proofMD=proofMD + 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 if not rlnPeer.nullifierLog.hasKey(msg.proof.epoch): - rlnPeer.nullifierLog[msg.proof.epoch]= @[proofMD] + rlnPeer.nullifierLog[msg.proof.epoch] = @[proofMD] return ok(true) - + try: # check if an identical record exists 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 = ## converts `t` to `Epoch` in little-endian order let bytes = toBytes(t, Endianness.littleEndian) - debug "bytes", bytes=bytes + debug "bytes", bytes = bytes var epoch: Epoch discard epoch.copyFrom(bytes) return epoch proc fromEpoch*(epoch: Epoch): 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 -proc calcEpoch*(t: float64): Epoch = +proc calcEpoch*(t: float64): Epoch = ## gets time `t` as `flaot64` with subseconds resolution in the fractional part ## and returns its corresponding rln `Epoch` value let e = uint64(t/EPOCH_UNIT_SECONDS) @@ -420,25 +443,26 @@ 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 - + ## i.e., e1 - e2 + # convert epochs to their corresponding unsigned numerical values - let + let epoch1 = fromEpoch(e1) 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 ## 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 - + # checks if the `msg`'s epoch is far from the current epoch # it corresponds to the validation of rln external nullifier var epoch: Epoch @@ -448,77 +472,79 @@ proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Optio # get current rln epoch epoch = getCurrentEpoch() - debug "current epoch", currentEpoch=fromEpoch(epoch) - let + debug "current epoch", currentEpoch = fromEpoch(epoch) + let msgEpoch = msg.proof.epoch # 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 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 - let + let contentTopicBytes = msg.contentTopic.toBytes input = concat(msg.payload, contentTopicBytes) if not rlnPeer.rlnInstance.proofVerify(input, msg.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 - + # check if double messaging has happened let hasDup = rlnPeer.hasDuplicate(msg) 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 - # 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., # it will never error out 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 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 extracts the `contentTopic` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence - let + let contentTopicBytes = wakumessage.contentTopic.toBytes output = concat(wakumessage.payload, contentTopicBytes) 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. ## The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`) let input = msg.toRLNSignal() - + var proof: RateLimitProofResult = proofGen(rlnInstance = rlnPeer.rlnInstance, data = input, - memKeys = rlnPeer.membershipKeyPair, - memIndex = rlnPeer.membershipIndex, + memKeys = rlnPeer.membershipKeyPair, + memIndex = rlnPeer.membershipIndex, epoch = calcEpoch(senderEpochTime)) - + if proof.isErr: return false msg.proof = proof.value 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` for i in 0..list.len-1: let member = list[i] let member_is_added = rlnInstance.insertMember(member) if not member_is_added: return false - return true \ No newline at end of file + return true