From 9fef5eac11c8f500c30990fb3d0e1caaa3913f9a Mon Sep 17 00:00:00 2001 From: Alvaro Revuelta Date: Tue, 23 Apr 2024 15:04:28 +0200 Subject: [PATCH] Custom witness with onchain Merkle proof (#2) --- .gitignore | 1 + README.md | 48 +++-- contract/abi.abi | 2 +- contract/contract.go | 69 +++++--- go.mod | 19 +- go.sum | 34 ++-- main.go | 406 ++++++++++++++++++++++++++++++++----------- 7 files changed, 405 insertions(+), 174 deletions(-) diff --git a/.gitignore b/.gitignore index dea7cff..2b6948a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ main **/.DS_Store +**.json \ No newline at end of file diff --git a/README.md b/README.md index c24624b..02b6b78 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,19 @@ # go-waku-light -This repo contains a proof of concept of a `waku` light client, integrating [this](https://github.com/vacp2p/rln-contract/pull/31) modification in the RLN contract, which allows having the whole membership set + Merkle root on-chain. This makes light clients even lighter. +This repo contains a proof of concept for a `waku` light client, integrating [this](https://github.com/vacp2p/rln-contract/pull/31) modification of the RLN contract. It allows: +* Faster sync time. Uses `GetCommitments` instead of fetching events. +* Getting the Merkle root directly from the contract. No need to sync the whole tree. +* Getting the Merkle proof for any leaf directly from the contract. Not need to sync the whole tree. For context, RLN is a decentralized rate-limiting protocol, that allows setting a limit on the number of messages sent by each entity, using zero-knowledge proofs. Said proofs prove that i) the member is whitelisted, without revealing its index, and ii) no more than 1 message is sent every epoch, which prevents double signaling. The main motivation is to showcase how [this](https://github.com/vacp2p/rln-contract/pull/31) modification can help light clients become lighter. It makes proof generation and verification easier: * Proof verification: Only requires the Merkle root, which is now available on-chain and can be fetched with a simple call. Before, one had to sync the whole Merkle tree with emitted events, and keep a local copy of it. -* Proof generation: Requires the whole Merkle tree, which can be synced faster since this modification stores all the leaves in the contract. This approach is faster than syncing events, with the con of spending more gas on membership registration. +* Proof generation: Generating an RLN proof requires the Merkle proof of the leaf generating it. With this modification, said Merkle proof can be obtained directly from the contract. Notes: -* The new [contract](https://github.com/vacp2p/rln-contract/pull/31) uses a [BinaryIMT](https://github.com/privacy-scaling-explorations/zk-kit/blob/main/packages/imt.sol/contracts/BinaryIMT.sol). -* It is deployed in Polygon Layer 2 zkEVM, [see](https://testnet-zkevm.polygonscan.com/address/0x16aBFfCAB50E8D1ff5c22b118Be5c56F801Dce54). - -This repo provides the following functionalities: -* Register an RLN membership in the contract in Layer 2 (Polygon zkEVM) -* Listen to new membership addition -* Get on-chain Merkle root -* Locally sync the membership Merkle tree (no need to sync events as before) -* Generate RLN proofs for a message -* Verify RLN proofs against the contract Merkle root. +* Deployed in Polygon Layer 2 zkEVM, [see](https://cardona-zkevm.polygonscan.com/address/0x16abffcab50e8d1ff5c22b118be5c56f801dce54). +* This repo provides a simple CLI tool to showcase the functionalities. ## Usage @@ -37,22 +32,37 @@ Register a new membership. You must provide a valid Polygon zkEVM account, see [ ./main register --priv-key=REPLACE_YOUR_PRIV_KEY ``` -Fetches and logs the latest Merkle root of the tree, using the contract as a source. +Fetches and logs the latest Merkle root and Merkle proof of the tree, using the contract as a source. Set `leaf-index` to your leaf index. ``` ./main onchain-root +./main onchain-merkle-proof --leaf-index=1 +``` + +Syncs the Merkle tree from the contract creating a local tree. The `chunk-size` indicates how many memberships are fetched at once. If too big, the RPC provider may error. Merkle proof can be locally computed, provide your `leaf-index`. Both `onchain` and `local` results should match. +``` +./main local-root --chunk-size=500 +./main local-merkle-proof --chunk-size=500 --leaf-index=1 ``` -Syncs the Merkle tree from the contract. The `chunk-size` indicates how many memberships are fetched at once. If too big, the RPC provider may error. +Generates the RLN proof using a given membership, see the previous step. The proof is stored in a `.json` file. Message and epoch are hardcoded for simplicity. Note that the RLN can be generated `onchain` (which doesn't require to locally sync the tree since it uses the contract) and `local` (which syncs the tree locally). ``` -./main sync-tree --chunk-size=500 +./main onchain-generate-rln-proof --membership-file=membership_xxx.json +./main local-generate-rln-proof --membership-file=membership_xxx.json --chunk-size=500 ``` -Generates a proof using a given membership. The proof is stored in a `.json` file. +Any RLN proof can be verified against the smart contract Merkle root. ``` -./main generate-proof --membership-file=membership_xxx.json +./main verify-rln-proof --proof-file=proof_xxx.json ``` -Verifies a given proof against the smart contract Merkle root. + +## Advanced + +The `contract/contract.go` can be updated if the abi is changed as follows: + ``` -./main verify-proof --proof-file=proof_xxx.json +git clone https://github.com/ethereum/go-ethereum.git +cd go-ethereum +go build ./cmd/abigen +./abigen --abi=../go-waku-light/contract/abi.abi --pkg=contract --out=../go-waku-light/contract/contract.go ``` \ No newline at end of file diff --git a/contract/abi.abi b/contract/abi.abi index dd0f263..ec50262 100644 --- a/contract/abi.abi +++ b/contract/abi.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"uint256","name":"membershipDeposit","type":"uint256"},{"internalType":"uint256","name":"depth","type":"uint256"},{"internalType":"address","name":"_verifier","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DuplicateIdCommitment","type":"error"},{"inputs":[],"name":"FullTree","type":"error"},{"inputs":[],"name":"InsufficientContractBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"InsufficientDeposit","type":"error"},{"inputs":[],"name":"InsufficientWithdrawalBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"InvalidIdCommitment","type":"error"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"InvalidPaginationQuery","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"InvalidReceiverAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"MemberHasNoStake","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"MemberNotRegistered","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"idCommitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"MemberRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"idCommitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"MemberWithdrawn","type":"event"},{"inputs":[],"name":"DEPTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MEMBERSHIP_DEPOSIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Q","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SET_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployedBlockNumber","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"getCommitments","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"idCommitmentIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imtData","outputs":[{"internalType":"uint256","name":"depth","type":"uint256"},{"internalType":"uint256","name":"root","type":"uint256"},{"internalType":"uint256","name":"numberOfLeaves","type":"uint256"},{"internalType":"bool","name":"useDefaultZeroes","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"indexToCommitment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"isValidCommitment","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"memberExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"members","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"root","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"slash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"uint256","name":"membershipDeposit","type":"uint256"},{"internalType":"uint256","name":"depth","type":"uint256"},{"internalType":"address","name":"_verifier","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DuplicateIdCommitment","type":"error"},{"inputs":[],"name":"FullTree","type":"error"},{"inputs":[],"name":"InsufficientContractBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"}],"name":"InsufficientDeposit","type":"error"},{"inputs":[],"name":"InsufficientWithdrawalBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"InvalidIdCommitment","type":"error"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"InvalidPaginationQuery","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"InvalidReceiverAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"MemberHasNoStake","type":"error"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"MemberNotRegistered","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"idCommitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"MemberRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"idCommitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"MemberWithdrawn","type":"event"},{"inputs":[],"name":"DEPTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MEMBERSHIP_DEPOSIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Q","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SET_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployedBlockNumber","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"getCommitments","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"idCommitmentIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imtData","outputs":[{"internalType":"uint40","name":"maxIndex","type":"uint40"},{"internalType":"uint40","name":"numberOfLeaves","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"indexToCommitment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"isValidCommitment","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"memberExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"members","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint40","name":"index","type":"uint40"}],"name":"merkleProofElements","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"root","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"idCommitment","type":"uint256"},{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"uint256[8]","name":"proof","type":"uint256[8]"}],"name":"slash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakedAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/contract/contract.go b/contract/contract.go index 6d359d4..6cbb6b3 100644 --- a/contract/contract.go +++ b/contract/contract.go @@ -31,7 +31,7 @@ var ( // ContractMetaData contains all meta data concerning the Contract contract. var ContractMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"membershipDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_verifier\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"DuplicateIdCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FullTree\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientContractBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"provided\",\"type\":\"uint256\"}],\"name\":\"InsufficientDeposit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientWithdrawalBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"InvalidIdCommitment\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endIndex\",\"type\":\"uint256\"}],\"name\":\"InvalidPaginationQuery\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"InvalidReceiverAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"MemberHasNoStake\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"MemberNotRegistered\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"MemberRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"MemberWithdrawn\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEPTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MEMBERSHIP_DEPOSIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Q\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SET_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deployedBlockNumber\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endIndex\",\"type\":\"uint256\"}],\"name\":\"getCommitments\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"idCommitmentIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"imtData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"root\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numberOfLeaves\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"useDefaultZeroes\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"indexToCommitment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"isValidCommitment\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"memberExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"members\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"register\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"root\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"internalType\":\"addresspayable\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256[8]\",\"name\":\"proof\",\"type\":\"uint256[8]\"}],\"name\":\"slash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"stakedAmounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifier\",\"outputs\":[{\"internalType\":\"contractIVerifier\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"withdrawalBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"membershipDeposit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_verifier\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"DuplicateIdCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FullTree\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientContractBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"provided\",\"type\":\"uint256\"}],\"name\":\"InsufficientDeposit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientWithdrawalBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"InvalidIdCommitment\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endIndex\",\"type\":\"uint256\"}],\"name\":\"InvalidPaginationQuery\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"InvalidReceiverAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"MemberHasNoStake\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"MemberNotRegistered\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"MemberRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"MemberWithdrawn\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEPTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MEMBERSHIP_DEPOSIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Q\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SET_SIZE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deployedBlockNumber\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endIndex\",\"type\":\"uint256\"}],\"name\":\"getCommitments\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"idCommitmentIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"imtData\",\"outputs\":[{\"internalType\":\"uint40\",\"name\":\"maxIndex\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"numberOfLeaves\",\"type\":\"uint40\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"indexToCommitment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"isValidCommitment\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"memberExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"members\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint40\",\"name\":\"index\",\"type\":\"uint40\"}],\"name\":\"merkleProofElements\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"}],\"name\":\"register\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"root\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"idCommitment\",\"type\":\"uint256\"},{\"internalType\":\"addresspayable\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256[8]\",\"name\":\"proof\",\"type\":\"uint256[8]\"}],\"name\":\"slash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"stakedAmounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"verifier\",\"outputs\":[{\"internalType\":\"contractIVerifier\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"withdrawalBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // ContractABI is the input ABI used to generate the binding from. @@ -399,30 +399,24 @@ func (_Contract *ContractCallerSession) IdCommitmentIndex() (*big.Int, error) { // ImtData is a free data retrieval call binding the contract method 0x3c979b5f. // -// Solidity: function imtData() view returns(uint256 depth, uint256 root, uint256 numberOfLeaves, bool useDefaultZeroes) +// Solidity: function imtData() view returns(uint40 maxIndex, uint40 numberOfLeaves) func (_Contract *ContractCaller) ImtData(opts *bind.CallOpts) (struct { - Depth *big.Int - Root *big.Int - NumberOfLeaves *big.Int - UseDefaultZeroes bool + MaxIndex *big.Int + NumberOfLeaves *big.Int }, error) { var out []interface{} err := _Contract.contract.Call(opts, &out, "imtData") outstruct := new(struct { - Depth *big.Int - Root *big.Int - NumberOfLeaves *big.Int - UseDefaultZeroes bool + MaxIndex *big.Int + NumberOfLeaves *big.Int }) if err != nil { return *outstruct, err } - outstruct.Depth = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - outstruct.Root = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) - outstruct.NumberOfLeaves = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) - outstruct.UseDefaultZeroes = *abi.ConvertType(out[3], new(bool)).(*bool) + outstruct.MaxIndex = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.NumberOfLeaves = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) return *outstruct, err @@ -430,24 +424,20 @@ func (_Contract *ContractCaller) ImtData(opts *bind.CallOpts) (struct { // ImtData is a free data retrieval call binding the contract method 0x3c979b5f. // -// Solidity: function imtData() view returns(uint256 depth, uint256 root, uint256 numberOfLeaves, bool useDefaultZeroes) +// Solidity: function imtData() view returns(uint40 maxIndex, uint40 numberOfLeaves) func (_Contract *ContractSession) ImtData() (struct { - Depth *big.Int - Root *big.Int - NumberOfLeaves *big.Int - UseDefaultZeroes bool + MaxIndex *big.Int + NumberOfLeaves *big.Int }, error) { return _Contract.Contract.ImtData(&_Contract.CallOpts) } // ImtData is a free data retrieval call binding the contract method 0x3c979b5f. // -// Solidity: function imtData() view returns(uint256 depth, uint256 root, uint256 numberOfLeaves, bool useDefaultZeroes) +// Solidity: function imtData() view returns(uint40 maxIndex, uint40 numberOfLeaves) func (_Contract *ContractCallerSession) ImtData() (struct { - Depth *big.Int - Root *big.Int - NumberOfLeaves *big.Int - UseDefaultZeroes bool + MaxIndex *big.Int + NumberOfLeaves *big.Int }, error) { return _Contract.Contract.ImtData(&_Contract.CallOpts) } @@ -576,6 +566,37 @@ func (_Contract *ContractCallerSession) Members(arg0 *big.Int) (*big.Int, error) return _Contract.Contract.Members(&_Contract.CallOpts, arg0) } +// MerkleProofElements is a free data retrieval call binding the contract method 0x74e942fa. +// +// Solidity: function merkleProofElements(uint40 index) view returns(uint256[]) +func (_Contract *ContractCaller) MerkleProofElements(opts *bind.CallOpts, index *big.Int) ([]*big.Int, error) { + var out []interface{} + err := _Contract.contract.Call(opts, &out, "merkleProofElements", index) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +// MerkleProofElements is a free data retrieval call binding the contract method 0x74e942fa. +// +// Solidity: function merkleProofElements(uint40 index) view returns(uint256[]) +func (_Contract *ContractSession) MerkleProofElements(index *big.Int) ([]*big.Int, error) { + return _Contract.Contract.MerkleProofElements(&_Contract.CallOpts, index) +} + +// MerkleProofElements is a free data retrieval call binding the contract method 0x74e942fa. +// +// Solidity: function merkleProofElements(uint40 index) view returns(uint256[]) +func (_Contract *ContractCallerSession) MerkleProofElements(index *big.Int) ([]*big.Int, error) { + return _Contract.Contract.MerkleProofElements(&_Contract.CallOpts, index) +} + // Root is a free data retrieval call binding the contract method 0xebf0c717. // // Solidity: function root() view returns(uint256) diff --git a/go.mod b/go.mod index 94180ef..3891a05 100644 --- a/go.mod +++ b/go.mod @@ -4,22 +4,21 @@ go 1.19 require ( github.com/ethereum/go-ethereum v1.13.5 + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.25.7 - github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd + github.com/waku-org/go-zerokit-rln v0.1.14-0.20240124153136-14960f3aff2a ) require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect @@ -30,22 +29,20 @@ require ( github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b // indirect - github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 // indirect - github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1 // indirect + github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330 // indirect + github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113 // indirect + github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/tools v0.13.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index ebe23d8..64124bf 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDO github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= @@ -80,6 +80,7 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= @@ -99,7 +100,6 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -110,18 +110,18 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd h1:cu7CsUo7BK6ac/v193RIaqAzUcmpa6MNY4xYW9AenQI= -github.com/waku-org/go-zerokit-rln v0.1.14-0.20230916173259-d284a3d8f2fd/go.mod h1:1PdBdPzyTaKt3VnpAHk3zj+r9dXPFOr3IHZP9nFle6E= -github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b h1:KgZVhsLkxsj5gb/FfndSCQu6VYwALrCOgYI3poR95yE= -github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= -github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 h1:Sd7QD/1Yo2o2M1MY49F8Zr4KNBPUEK5cz5HoXQVJbrs= -github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4= -github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1 h1:4HSdWMFMufpRo3ECTX6BrvA+VzKhXZf7mS0rTa5cCWU= -github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20230916171518-2a77c3734dd1/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240124153136-14960f3aff2a h1:QxwhGVNajSoeKElW5rjd3bmu3eF5SHXUmFgOgcy0oXA= +github.com/waku-org/go-zerokit-rln v0.1.14-0.20240124153136-14960f3aff2a/go.mod h1:UerBnX5Lthq5AvM3yOUuMM2YST00LDoCwhaOgFd0CD8= +github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330 h1:TJmn6GQ5HpxdZraZn6DjUqWy8UV+8pB4yWcsWFAngqE= +github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48= +github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113 h1:dPwc4LAWLXb4Pssej/NtGA9A0UMQwi+JafQPdnhjRWM= +github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4= +github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc h1:GUZlr25hXLu/PeASqm8P5dPOyD4CdfvkzyEtXEBLbr8= +github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -133,19 +133,17 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/main.go b/main.go index da54eb6..2c4bfc5 100644 --- a/main.go +++ b/main.go @@ -22,24 +22,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/pkg/errors" ) -// some public endopoints - -// drpc -// https://polygon-zkevm-testnet.drpc.org -// wss://polygon-zkevm-testnet.drpc.org - -// blastapi -// https://polygon-zkevm-testnet.public.blastapi.io -// wss://polygon-zkevm-testnet.public.blastapi.io - -// polygon team -// http://rpc.public.zkevm-test.net -// wss://ws.public.zkevm-test.net - -const Endpoint = "wss://ws.public.zkevm-test.net" -const RlnContractAddress = "0x16aBFfCAB50E8D1ff5c22b118Be5c56F801Dce54" +const Endpoint = "wss://ws.cardona.zkevm-rpc.com" +const RlnContractAddress = "0x16abffcab50e8d1ff5c22b118be5c56f801dce54" // Example data for RLN messages const Message = "Hello World!" @@ -57,6 +44,7 @@ func main() { var proofFile string var amountRegister int var privKey string + var leafIndex uint64 executionClient, err := ethclient.Dial(Endpoint) if err != nil { @@ -76,10 +64,19 @@ func main() { // Examples of usage: // ./main register --priv-key=REPLACE_YOUR_PRIV_KEY // ./main listen + + // Fetched direcly from the contract // ./main onchain-root - // ./main sync-tree --chunk-size=500 - // ./main generate-proof --membership-file=membership_xxx.json - // ./main verify-proof --proof-file=proof_xxx.json + // ./main onchain-merkle-proof --leaf-index=1 + + // Syncronized via events locally + // ./main local-root --chunk-size=500 + // ./main local-merkle-proof --chunk-size=500 --leaf-index=1 + + // RLN Related + // ./main onchain-generate-rln-proof --membership-file=membership_xxx.json + // ./main local-generate-rln-proof --membership-file=membership_xxx.json --chunk-size=500 + // ./main verify-rln-proof --proof-file=proof_xxx.json app := &cli.App{ Commands: []*cli.Command{ { @@ -96,22 +93,51 @@ func main() { }, Name: "register", Action: func(cCtx *cli.Context) error { - Register(cfg, privKey, amountRegister) - return nil + err := Register(cfg, privKey, amountRegister) + return err }, }, { Name: "listen", Action: func(cCtx *cli.Context) error { - Listen(cfg) - return nil + err := Listen(cfg) + return err }, }, { Name: "onchain-root", Action: func(cCtx *cli.Context) error { - OnchainRoot(cfg) - return nil + err := OnchainRoot(cfg) + return err + }, + }, + { + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "leaf-index", + Destination: &leafIndex, + }, + }, + + Name: "onchain-merkle-proof", + Action: func(cCtx *cli.Context) error { + _, err := OnchainMerkleProof(cfg, leafIndex) + return err + }, + }, + { + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "chunk-size", + Value: 500, + Destination: &chunkSize, + }, + }, + + Name: "local-root", + Action: func(cCtx *cli.Context) error { + _, err := SyncTree(cfg, chunkSize) + return err }, }, { @@ -121,12 +147,29 @@ func main() { Value: 500, Destination: &chunkSize, }, + &cli.Uint64Flag{ + Name: "leaf-index", + Destination: &leafIndex, + }, }, - Name: "sync-tree", + Name: "local-merkle-proof", + Action: func(cCtx *cli.Context) error { + err := LocalMerkleProof(cfg, chunkSize, leafIndex) + return err + }, + }, + { + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "membership-file", + Destination: &membershipFile, + }, + }, + Name: "onchain-generate-rln-proof", Action: func(cCtx *cli.Context) error { - SyncTree(cfg, chunkSize) - return nil + err := OnchainGenerateRlnProof(cfg, membershipFile) + return err }, }, { @@ -141,10 +184,10 @@ func main() { Destination: &membershipFile, }, }, - Name: "generate-proof", + Name: "local-generate-rln-proof", Action: func(cCtx *cli.Context) error { - GenerateProof(cfg, chunkSize, membershipFile) - return nil + err := LocalGenerateRlnProof(cfg, chunkSize, membershipFile) + return err }, }, { @@ -154,10 +197,10 @@ func main() { Destination: &proofFile, }, }, - Name: "verify-proof", + Name: "verify-rln-proof", Action: func(cCtx *cli.Context) error { - VerifyProof(cfg, proofFile) - return nil + err := VerifyRlnProof(cfg, proofFile) + return err }, }, }, @@ -171,22 +214,22 @@ func main() { // Register a new membership into the rln contract, and stores its in a json file. Note that the json // is not a keystore, but just a custom serialized struct. Registering requires providing a valid // account with enough funds. -func Register(cfg *Config, privKey string, amount int) { +func Register(cfg *Config, privKey string, amount int) error { log.Info("Configured contract ", RlnContractAddress, " registering ", amount, " memberships") rlnInstance, err := rln.NewRLN() if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when creating RLN instance") } privateKey, err := crypto.HexToECDSA(privKey) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when converting private key") } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { - log.Fatal("error casting public key to ECDSA") + return errors.New("error casting public key to ECDSA") } fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) @@ -194,17 +237,17 @@ func Register(cfg *Config, privKey string, amount int) { log.Info("Preparing tx from address: ", fromAddress.Hex()) nonce, err := cfg.client.PendingNonceAt(context.Background(), fromAddress) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching nonce") } chaindId, err := cfg.client.NetworkID(context.Background()) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching chain id") } auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chaindId) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when creating transactor") } var i uint64 = 0 @@ -220,7 +263,7 @@ func Register(cfg *Config, privKey string, amount int) { m, err := rlnInstance.MembershipKeyGen() if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when generating membership") } mBig := rln.Bytes32ToBigInt(m.IDCommitment) @@ -228,38 +271,40 @@ func Register(cfg *Config, privKey string, amount int) { // Create a tx calling the update rewards root function with the new merkle root tx, err := cfg.contract.Register(auth, mBig) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when sending tx") } log.Info("Tx sent. Nonce: ", auth.Nonce, " Commitment: ", mBig, " TxHash: ", tx.Hash().Hex()) rankingsJson, err := json.Marshal(m) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when marshalling membership") } err = ioutil.WriteFile(fmt.Sprintf("membership_%s.json", mBig.String()), rankingsJson, 0644) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when writing membership to file") } time.Sleep(4 * time.Second) } + + return nil } // Listens for new registrations and logs new root. Note that slashings are not // monitored. -func Listen(cfg *Config) { +func Listen(cfg *Config) error { log.Info("Configured contract ", RlnContractAddress) callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} onchainRoot, err := cfg.contract.Root(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching root") } numLeafs, err := cfg.contract.IdCommitmentIndex(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching num leafs") } log.Info("There are ", numLeafs, " leafs and the root is ", onchainRoot) @@ -267,29 +312,29 @@ func Listen(cfg *Config) { currentBlock, err := cfg.client.BlockNumber(context.Background()) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching block number") } watchOpts := &bind.WatchOpts{Context: context.Background(), Start: ¤tBlock} channel := make(chan *contract.ContractMemberRegistered) sub, err := cfg.contract.WatchMemberRegistered(watchOpts, channel) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when watching events") } for { select { case err := <-sub.Err(): - log.Fatal(err) + return errors.Wrap(err, "error when watching events") case vLog := <-channel: callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} onchainRoot, err := cfg.contract.Root(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching root") } numLeafs, err := cfg.contract.IdCommitmentIndex(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching num leafs") } log.WithFields(log.Fields{ "Block": vLog.Raw.BlockNumber, @@ -302,78 +347,194 @@ func Listen(cfg *Config) { } // Gets the merkle root from the contract and logs it. -func OnchainRoot(cfg *Config) { +func OnchainRoot(cfg *Config) error { log.Info("Configured contract ", RlnContractAddress) callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} onchainRoot, err := cfg.contract.Root(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching root") } numLeafs, err := cfg.contract.IdCommitmentIndex(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching num leafs") } log.Info("Onchain leafs: ", numLeafs) log.Info("Onchain root: ", onchainRoot) + + return nil +} + +func OnchainMerkleProof(cfg *Config, leafIndex uint64) (*rln.MerkleProof, error) { + log.Info("Configured contract ", RlnContractAddress) + callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} + + merkleProofElements, err := cfg.contract.MerkleProofElements(callOpts, big.NewInt(0).SetUint64(leafIndex)) + if err != nil { + return nil, errors.Wrap(err, "error when fetching merkle proof elements") + } + + bytePathElements := make([]rln.MerkleNode, len(merkleProofElements)) + + log.Info("Merkle elements for leaf ", leafIndex) + for i, element := range merkleProofElements { + log.Info("Raw from contract [", i, "] ", element) + bytePathElements[i] = rln.BigIntToBytes32(element) + } + + pathIndexes := make([]uint8, len(merkleProofElements)) + for i := 0; i < len(merkleProofElements); i++ { + index := (leafIndex >> i) & 1 + pathIndexes[i] = uint8(index) + } + + merkleProof := &rln.MerkleProof{ + PathElements: bytePathElements, + PathIndexes: pathIndexes, + } + + log.Info("Full Merkle proof for leaf ", leafIndex, " ", merkleProof) + + return merkleProof, nil +} + +func LocalMerkleProof(cfg *Config, chunkSize uint64, leafIndex uint64) error { + rlnInstance, err := SyncTree(cfg, chunkSize) + if err != nil { + return errors.Wrap(err, "error when syncing tree") + } + + proof, err := rlnInstance.GetMerkleProof(rln.MembershipIndex(leafIndex)) + if err != nil { + return errors.Wrap(err, "error when fetching merkle proof") + } + + log.Info("Merkle elements for leaf ", leafIndex) + for i, element := range proof.PathElements { + // Bytes are reversed so that they match with the contract. + // RLN uses little endian, while the contract uses big endian. + bigInt := new(big.Int).SetBytes(reverseBytes(element[:])) + log.Info("Local proof element [", i, "] ", bigInt) + } + + log.Info("Full Merkle proof for leaf: ", leafIndex, " ", proof) + + return nil } // Generates an rln zk proof to be attached to a message, proving membership // inclusion + respecting rate limits. It requires a valid rln membership that // has been registered in the contract. -func GenerateProof(cfg *Config, chunkSize uint64, rlnFile string) { +func LocalGenerateRlnProof(cfg *Config, chunkSize uint64, rlnFile string) error { idCred := &rln.IdentityCredential{} jsonFile, err := os.Open(rlnFile) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when opening file") } bb, err := io.ReadAll(jsonFile) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when reading file") } err = json.Unmarshal(bb, &idCred) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when unmarshalling file") } log.Info("Loaded commitment: ", rln.Bytes32ToBigInt(idCred.IDCommitment)) - rlnInstance := SyncTree(cfg, chunkSize) + rlnInstance, err := SyncTree(cfg, chunkSize) + if err != nil { + return errors.Wrap(err, "error when syncing tree") + } - found := false - membershipIndex := uint(0) - for leafIdx := uint(0); leafIdx < rlnInstance.LeavesSet(); leafIdx++ { + membershipIndex, err := findMembershipInTree(rlnInstance, idCred) + if err != nil { + return errors.Wrap(err, "error when finding membership in tree") + } - leaf, err := rlnInstance.GetLeaf(leafIdx) - if err != nil { - log.Fatal(err) - } + proof, err := rlnInstance.GenerateProof([]byte(Message), *idCred, rln.MembershipIndex(membershipIndex), rln.Epoch{RlnEpoch}) + if err != nil { + return errors.Wrap(err, "error when generating proof") + } - leafBig := rln.Bytes32ToBigInt(leaf) - mineBig := rln.Bytes32ToBigInt(idCred.IDCommitment) + proofJson, err := json.Marshal(proof) + if err != nil { + return errors.Wrap(err, "error when marshalling proof") + } - if leafBig.Cmp(mineBig) == 0 { - log.Info("Found leaf in tree: ", leafBig) - found = true - membershipIndex = leafIdx - break - } + // Just a hash of the proof, used as filename + hash := sha256.Sum256(proofJson) + + fileName := fmt.Sprintf("proof_%s.json", hex.EncodeToString(hash[:])) + err = ioutil.WriteFile(fileName, proofJson, 0644) + if err != nil { + return errors.Wrap(err, "error when writing to file") } - if !found { - log.Fatal("Could not find leaf in tree") + log.Info("Proof generated succesfully and stored as ", fileName) + + return nil +} + +func OnchainGenerateRlnProof(cfg *Config, rlnFile string) error { + idCred := &rln.IdentityCredential{} + jsonFile, err := os.Open(rlnFile) + if err != nil { + return errors.Wrap(err, "error when opening file") } - proof, err := rlnInstance.GenerateProof([]byte(Message), *idCred, rln.MembershipIndex(membershipIndex), rln.Epoch{RlnEpoch}) + bb, err := io.ReadAll(jsonFile) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when reading file") + } + err = json.Unmarshal(bb, &idCred) + if err != nil { + return errors.Wrap(err, "error when unmarshalling file") + } + + rlnInstance, err := rln.NewRLN() + if err != nil { + return errors.Wrap(err, "error when creating RLN instance") + } + + callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} + + exists, err := cfg.contract.MemberExists(callOpts, rln.Bytes32ToBigInt(idCred.IDCommitment)) + if err != nil { + return errors.Wrap(err, "error when checking if membership exists") + } + + if !exists { + return errors.New("membership does not exist in the contract") + } + + membershipIndex, err := cfg.contract.Members(callOpts, rln.Bytes32ToBigInt(idCred.IDCommitment)) + if err != nil { + return errors.Wrap(err, "error when fetching membership index") + } + log.Info("Membership index found in the contract: ", membershipIndex, " for the provided commitment") + + merkleProof, err := OnchainMerkleProof(cfg, membershipIndex.Uint64()) + if err != nil { + return errors.Wrap(err, "error when fetching merkle proof") + } + + rlnWitness := rln.CreateWitness( + idCred.IDSecretHash, + []byte(Message), + rln.ToEpoch(RlnEpoch), + *merkleProof) + + proof, err := rlnInstance.GenerateRLNProofWithWitness(rlnWitness) + if err != nil { + return errors.Wrap(err, "error when generating proof") } proofJson, err := json.Marshal(proof) if err != nil { - log.Fatal("error when marshalinng proof: ", err) + return errors.Wrap(err, "error when marshalling proof") } // Just a hash of the proof, used as filename @@ -382,76 +543,79 @@ func GenerateProof(cfg *Config, chunkSize uint64, rlnFile string) { fileName := fmt.Sprintf("proof_%s.json", hex.EncodeToString(hash[:])) err = ioutil.WriteFile(fileName, proofJson, 0644) if err != nil { - log.Fatal("error when writing to file: ", err) + return errors.Wrap(err, "error when writing to file") } log.Info("Proof generated succesfully and stored as ", fileName) + + return nil } -func VerifyProof(cfg *Config, proofFile string) { +func VerifyRlnProof(cfg *Config, proofFile string) error { proof := &rln.RateLimitProof{} jsonFile, err := os.Open(proofFile) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when opening file") } bb, err := io.ReadAll(jsonFile) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when reading file") } err = json.Unmarshal(bb, &proof) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when unmarshalling file") } callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} onchainRoot, err := cfg.contract.Root(callOpts) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when fetching root") } log.Info("Onchain root: ", onchainRoot) rlnInstance, err := rln.NewRLN() if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when creating RLN instance") } verified, err := rlnInstance.Verify([]byte(Message), *proof, rln.BigIntToBytes32(onchainRoot)) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when verifying proof") } // When does it fail to verify? // * If the data (message) is different // * If the membership is not part of the tree if !verified { - log.Fatal("Proof not verified") + return errors.New("proof verification failed") } metadata, err := rlnInstance.ExtractMetadata(*proof) if err != nil { - log.Fatal(err) + return errors.Wrap(err, "error when extracting metadata") } + _ = metadata log.Info("Proof verified succesfully") - _ = metadata + return nil } // Creates an rln instance and syncs it with the contract leafs (aka memberships). This // creates a local tree that can be used to generate proofs (required for sending messages). -func SyncTree(cfg *Config, chunkSize uint64) *rln.RLN { +func SyncTree(cfg *Config, chunkSize uint64) (*rln.RLN, error) { rlnInstance, err := rln.NewRLN() if err != nil { - log.Fatal(err) + return nil, errors.Wrap(err, "error when creating RLN instance") } callOpts := &bind.CallOpts{Context: context.Background(), Pending: false} numLeafs, err := cfg.contract.IdCommitmentIndex(callOpts) if err != nil { - log.Fatal(err) + return nil, errors.Wrap(err, "error when fetching num leafs") } for i := uint64(0); i < numLeafs.Uint64(); i += chunkSize { @@ -464,25 +628,65 @@ func SyncTree(cfg *Config, chunkSize uint64) *rln.RLN { log.Info("Fetching from ", start, " to ", end, " out of ", numLeafs, " leafs") leafs, err := cfg.contract.GetCommitments(callOpts, start, end) if err != nil { - log.Fatal(err) + return nil, errors.Wrap(err, "error when fetching commitments") } for _, leaf := range leafs { err := rlnInstance.InsertMember(rln.BigIntToBytes32(leaf)) if err != nil { - log.Fatal(err) + return nil, errors.Wrap(err, "error when inserting member") } } } - log.Info("Local leafs:", rlnInstance.LeavesSet()) + log.Info("Local leafs: ", rlnInstance.LeavesSet()) myRoot, err := rlnInstance.GetMerkleRoot() if err != nil { - log.Fatal(err) + return nil, errors.Wrap(err, "error when fetching merkle root") } - log.Info("Local root:", rln.Bytes32ToBigInt(myRoot)) + log.Info("Local root: ", rln.Bytes32ToBigInt(myRoot)) + + return rlnInstance, nil +} + +func findMembershipInTree(rlnInstance *rln.RLN, idCred *rln.IdentityCredential) (uint, error) { + found := false + membershipIndex := uint(0) + for leafIdx := uint(0); leafIdx < rlnInstance.LeavesSet(); leafIdx++ { + + leaf, err := rlnInstance.GetLeaf(leafIdx) + if err != nil { + return 0, errors.Wrap(err, "error when fetching leaf") + } + + leafBig := rln.Bytes32ToBigInt(leaf) + mineBig := rln.Bytes32ToBigInt(idCred.IDCommitment) + + if leafBig.Cmp(mineBig) == 0 { + log.Info("Found leaf in tree: ", leafBig) + found = true + membershipIndex = leafIdx + break + } + } + + if !found { + return 0, errors.New("membership not found in tree") + } + + return membershipIndex, nil +} + +func reverseBytes(b []byte) []byte { + reversed := make([]byte, len(b)) + copy(reversed, b) + + for i := len(reversed)/2 - 1; i >= 0; i-- { + opp := len(reversed) - 1 - i + reversed[i], reversed[opp] = reversed[opp], reversed[i] + } - return rlnInstance + return reversed }