# Rarimo Docs > Learn to use a privacy-first social protocol, which seamlessly combines various identity standards and forms a private but verifiable social graph. This file contains all documentation content in a single document following the llmtxt.org standard. ## Freedom Tool import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Freedom Tool Online voting systems are the most accessible way for people to participate in democracy, especially for those living under oppressive regimes. However, they typically lack transparency and are prone to Sybil and cyber attacks. Rarimo has employed zero-knowledge cryptography to address these challenges and built Freedom Tool, a decentralized, transparent, and privacy-preserving online voting tool on blockchain. ## How it works Here are the main components of the Freedom Tool: - **Mobile apps**: The Freedom Tool provides mobile apps for Android and iOS that voters can use to participate in the voting process. These apps are easy to use and accessible to everyone. - **Biometric passports**: Voters use their biometric passports to prove their identity. The mobile app scans the passport and extracts the relevant information, such as the voter's citizenship and date of birth. - **Zero-knowledge proofs**: The Freedom Tool uses zero-knowledge proofs to verify the validity of the biometric passport without exposing personal data, ensuring that the voting process is private and secure. - **Smart contracts**: The Freedom Tool uses smart contracts to handle the voting process. These contracts verify the zero-knowledge proofs and ensure the voting process is fair and reliable. ## Features - **Privacy-preserving**: The Freedom Tool uses zero-knowledge proofs to verify the validity of biometric passports without exposing personal data. - **Transparent**: On-chain vote records and cryptography ensure the system's integrity. - **Accessible**: The Freedom Tool is designed to be accessible, making it easy for everyone. It is enough to have a smartphone and a biometric passport to participate in voting. - **Secure**: The Freedom Tool relies on proven cryptographic mechanisms and was audited by independent cybersecurity specialists. - **Open-source**: The Freedom Tool is open-source, meaning anyone can inspect the code and contribute to its development. ## Use cases The Freedom Tool and its components can be used for a wide range of use cases, including: - Local elections - Polls - Paid surveys - NGO board elections - Robust anti-sybil solutions based on proof of passport ## Projects using Freedom Tool ### Russia2024 The Russia2024 project, spearheaded by Russian opposition figure Mark Feygin, provides a democratic alternative to the rigged 2024 Russian presidential election. The Russia2024 app aims to establish parallel government structures to promote democracy, freedom, and peace in Russia. It garnered over 15,000 downloads on its first day. ### Iranians Vote Launched by Iranian dissidents, the Iranians Vote project is a blockchain-powered voting app that enables citizens to protest the presidential election safely. It allows users to cast a vote of no confidence in the Islamic Republic of Iran's presidential candidates and its corrupt electoral system. ### United Space The United Space app, initiated by Georgian opposition activists, aims to facilitate tools for liquid democracy. It offers anonymous polls for Georgian citizens, both domestic and abroad, digital versions of government documents, and tokenization of state assets. ## Want to know more? Read the Freedom Tool whitepaper to learn more. --- ## Freedom Tool resources import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Freedom Tool resources >TODO --- ## Freedom Tool: Smart contracts reference import OutLink from "@site/src/components/OutLink"; # Freedom Tool: Smart contracts reference ## `ProposalsState` A facade for voting creation and management by creator. It is responsible for counting votes and adding voters to SMT by nullifier. ### Deployments | Chain | ChainID | Address | |--------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------| | Rarimo | `7368` | `0x9C4b84a940C9D3140a1F40859b3d4367DC8d099a` | ### Interface ```solidity title="/contracts/state/ProposalsState.sol" contract ProposalsState is OwnableUpgradeable, AccessControlUpgradeable, UUPSUpgradeable { uint256 public lastProposalId; uint256 public minFundingAmount; struct ProposalConfig { uint64 startTimestamp; uint64 duration; uint256 multichoice; uint256[] acceptedOptions; string description; address[] votingWhitelist; // must be sorted bytes[] votingWhitelistData; // data per voting whitelist } enum ProposalStatus { None, Waiting, Started, Ended, DoNotShow } struct ProposalInfo { address proposalSMT; ProposalStatus status; ProposalConfig config; uint256[MAXIMUM_CHOICES_PER_OPTION][] votingResults; } function createProposal( ProposalConfig calldata proposalConfig_ ) external payable { // ... } function addFundsToProposal( uint256 proposalId_ ) external payable { // ... } function changeProposalDuration( uint256 proposalId_, uint64 newDuration_ ) external onlyProposalCreator(proposalId_) { // ... } function hideProposal( uint256 proposalId_ ) external onlyProposalCreator(proposalId_) { // ... } function getProposalEventId( uint256 proposalId_ ) external view returns (uint256) { // ... } function getProposalInfo( uint256 proposalId_ ) external view returns (ProposalInfo memory info_) { // ... } } ``` - `createProposal()` — creates a new voting with initial amount (more than `minFundingAmount`) to cover fees per each user; - `addFundsToProposal()` — allows to top up a voting to cover more voting transactions; - `changeProposalDuration()` — allows voting prolongation. Only callable by the proposal creator; - `hideProposal()` — allows to hide proposal (switch status to `DoNotShow`). There is no way to make it visible again; - `getProposalEventId()` — returns unique number which is used to calculate nullifier; - `getProposalInfo()` — allows voting prolongation. Only callable by the proposal creator; See ProposalsState.sol for the full implementation. ## `BioPassportVoting` Responsible for the Biometric Passport query proof verification and pub signal validation. ### Deployments | Chain | ChainID | Address | |--------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------| | Rarimo | `7368` | `0x8Dea8065888A14F66ba9Fb944353d898663863cf` | ### Interface ```solidity title="/contracts/voting/BioPassportVoting.sol" contract BioPassportVoting is BaseVoting { function vote( bytes32 registrationRoot_, uint256 currentDate_, uint256 proposalId_, uint256[] memory vote_, UserData memory userData_, VerifierHelper.ProofPoints memory zkPoints_ ) external override { // ... } } ``` - `vote()` — verifies query proof and calls `vote()` function of `ProposalState` smart contract; See BioPassportVoting.sol for the full implementation. ## `Voting` Responsible for the National ID Card query proof verification and pub signal validation. ### Interface ```solidity title="/contracts/voting/Voting.sol" contract Voting is BaseVoting { function vote( bytes32 registrationRoot_, uint256 currentDate_, uint256 proposalId_, uint256[] memory vote_, UserData memory userData_, VerifierHelper.ProofPoints memory zkPoints_ ) external override { // ... } } ``` - `vote()` — verifies query proof and calls `vote()` function of `ProposalState` smart contract; See Voting.sol for the full implementation. ## `BaseVoting` This abstract contract encapsulates common logic for both `BioPassportVoting` and `Voting`. ### Interface ```solidity title="/contracts/voting/BaseVoting.sol" abstract contract BaseVoting is OwnableUpgradeable, UUPSUpgradeable { uint256 public constant ZERO_DATE = 0x303030303030; struct UserData { uint256 nullifier; uint256 citizenship; uint256 identityCreationTimestamp; } struct ProposalRules { uint256 selector; uint256[] citizenshipWhitelist; uint256 identityCreationTimestampUpperBound; uint256 identityCounterUpperBound; uint256 sex; uint256 birthDateLowerbound; uint256 birthDateUpperbound; uint256 expirationDateLowerBound; } address public registrationSMT; address public proposalsState; address public votingVerifier; } ``` - `registrationSMT` — address of Sparse Merkle Tree with registered identities; See BaseVoting.sol for the full implementation. ## `RegistrationSMTReplicator` Use it instead of `registrationSMT` unless your contracts on Rarimo. ### Interface ```solidity title="/contracts/replication/RegistrationSMTReplicator.sol" contract RegistrationSMTReplicator is MultiOwnable, UUPSUpgradeable { uint256 public constant ROOT_VALIDITY = 1 hours; bytes32 public latestRoot; uint256 public latestTimestamp; function transitionRoot( bytes32 newRoot_, uint256 transitionTimestamp_, bytes calldata proof_ ) external virtual onlyOwner { // ... } function isRootValid(bytes32 root_) external view virtual returns (bool) { // ... } function isRootLatest(bytes32 root_) public view virtual returns (bool) { // ... } } ``` See RegistrationSMTReplicator.sol for the full implementation. --- ## Freedom Tool success stories import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Freedom Tool success stories >TODO --- ## What is Rarimo? import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # What is Rarimo? Rarimo is building a permissionless ZK Registry that redefines how identities, interactions, and social graphs are managed. Unlike traditional identity protocols that depend on centralized issuers or specialized hardware, Rarimo uses client-side proving to deliver a truly censorship-resistant backbone. Instead of striving to build a universal identity, Rarimo embraces a pluralistic approach. This empowers developers to create applications that respect user anonymity while preserving historical interactions and ensuring continuity of identity. ## What is a ZK Registry? The ZK Registry is a ZK Rollup that functions as an on-chain registry for abstract statements. It enables proving the system's state without revealing details about individual statements. By aggregating standardized, provable statements in a permissionless registry, the ZK Registry provides a flexible foundation for many use cases. Developers can implement protocols like identity verification, reputation, and proof-of-attendance with minimal effort, making it easier to build privacy-preserving applications. ## Why a ZK Rollup? Rarimo deploys the ZK Registry to a dedicated ZK Rollup to leverage Ethereum's security and interoperability while maintaining affordability and flexibility. Additionally, ZK Registry updates can be propagated to other Layer 1 networks, ensuring seamless synchronization of root states across multiple ecosystems. ## Tools for building privacy-preserving applications Rarimo provides a suite of tools to help developers build trustless identities and privacy-preserving applications. These tools include: ### ZK Passport for permissionless self-issued identities ZK Passport Registry transforms biometric passports into unique, privacy-preserving digital identities. Unlike most solutions, it combines decentralized infrastructure, reliable uniqueness verification, and privacy. Learn more at [ZK Passport](/zk-passport). ### Bionetta for client-side ZKML Bionetta is a client-side framework for generating private inference proofs using Zero-Knowledge Machine Learning(ZKML). By focusing on public model weights, Bionetta ensures user inputs remain fully private. This approach is ideally suited for privacy-preserving use cases such as biometric verification in Web3. Learn more at [ZKML Bionetta](/zkml-bionetta). ### ZK Graph for provable credibility { // TODO: Remove } The ZK Graph Registry allows users to prove their reputation based on private relationships and interactions, without exposing sensitive details. By leveraging zero-knowledge proofs, users can verify their trustworthiness and standing in various communities while maintaining complete control over their privacy. Learn more at [ZK Graph](/zk-graph). ## Ecosystem Several applications are being built on top of Rarimo: - [**Rarimo App**](/rarimo-app): A self-sovereign mobile wallet for managing digital identity. - [**Freedom Tool**](/freedom-tool): A privacy-preserving voting solution designed for e-democracy. ## Launch your own ZK Registry ZK Registry is entirely open-source and built with extensibility in mind, following [ERC-7812](/zk-registry). If your DApp can benefit from private yet provable data, bring it on! --- ## Guide: Becoming a Passport Ambassador import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: Becoming a Passport Ambassador >TODO --- ## Rarimo App import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Rarimo App Rarimo is a mobile app that converts your biometric passport into an anonymous digital ID. It lets you use Web3 services anonymously by using advanced zero-knowledge (ZK) technology. ## Key Features - **Biometric Document Scanner**: Easily scan your biometric documents using your phone's camera or NFC. This ensures quick and accurate identity verification, saving you time during sign-ups. - **Private Data Vault**: Your identity and social connections are securely stored right on your device. We never share your data with anyone else, giving you complete control over your personal information. - **Zero-Knowledge Proofs**: Share specific parts of your identity without revealing all your personal information. For example, prove you're over 18 without showing your exact birthdate. You can present these proofs through QR codes when using social apps. - **Rewards Program**: Earn rewards for signing up, inviting friends, and completing various tasks within the app. Enjoy incentives that make using Rarimo even more beneficial. ## Get the app Rarimo App is available on the [App Store](https://apps.apple.com/us/app/rarime/id6503300598) and [Google Play](https://play.google.com/store/apps/details?id=com.rarilabs.rarime&hl=en). --- ## Audits import OutLink from "@site/src/components/OutLink"; # Audits The Rarimo Protocol components and smart contracts have undergone independent security auditing. All issues found during these audits have been acknowledged and/or fixed. The following companies conducted the audits: - Halborn - Ambisafe ## Public Reports ### Passport ZK Circuits **Date**: 2024-03-16 **Scope**: https://github.com/rarimo/passport-zk-circuits **Auditor**: Halborn **Results**: _1 low and 1 informational issues solved_ Final Report ### Identity Provider **Date**: 2024-03-16 **Scope**: https://github.com/rarimo/passport-identity-provider **Auditor**: Halborn **Results**: _2 low issues solved_ Final Report ### Voting Contracts **Date**: 2024-03-12 **Scope**: https://github.com/rarimo/voting-contracts **Auditor**: Halborn **Results**: _1 low and 1 informational issues solved, 1 low issue accepted_ Final Report ### iOS and Android Apps **Date**: 2024-03-26 **Scope**: https://github.com/rarimo/FreedomTool and https://github.com/rarimo/FreedomToolAndroid **Auditor**: Halborn **Results**: _1 high issue accepted, 2 low issues informational issues acknowledged, 1 low issue solved_ Final Report --- ## Glossary import OutLink from "@site/src/components/OutLink"; # Glossary ## Active Authentication (AA) A security feature in biometric passports aimed at preventing unauthorized copying of passport data. ## Biometric Passport A passport that includes biometric information, such as a facial photograph, typically includes an RFID chip containing data for biometric verification, personal details, an expiration date, and the issuer's digital signature. ## Bionetta Bionetta is a client-side framework for generating secure and private inference proofs using Zero-Knowledge Machine Learning (ZKML). ## Circom Circuit A Circom circuit is a computational construct used in zero-knowledge proof systems, defining constraints for efficient verification. It enables the generation and verification of proofs, preserving privacy by allowing one party to convince another of a statement's validity without revealing additional information. ## Decentralized Application (DApp) A DApp is an application of one or more smart contracts running on a blockchain and a user interface. ## DG1 Personal Details A data group within a biometric passport includes the passport holder's primary identity information, such as name, date of birth, nationality, and passport number. ## DG15: Active Authentication Public Key The data group within a biometric passport includes the public key used for Active Authentication (AA). ## DG2 Facial Photograph Data group within a biometric passport containing the passport holder's portrait. ## Ethereum Virtual Machine (EVM) A computation engine that is the foundation for EVM-compatible blockchains such as Ethereum and Polygon. See ethereum.org. ## Freedom Tool A decentralized, transparent, and privacy-preserving online voting tool on the blockchain. ## Fuzzy Extractors A cryptographic technique used for account recovery with noisy data, like images, without storing the raw data. ## Groth16 A specific type of zk-SNARK proof system used in Bionetta and ZK Passport for efficient proof generation. ## L1 Layer 1 blockchain, referring to the main Ethereum network in the context of the ZK Registry. ## L2 Layer 2 scaling solution, referring to the Rarimo ZK Rollup network. ## Machine-Readable Zone (MRZ) A portion of a passport that contains key personal data in a format readable by machine scanners. ## Merkle Tree A Merkle tree stores hashes of data in a tree that allows you to verify the contents of the tree. You can use Merkle branches to follow the hashes through the tree to verify that a specific piece of data is in the tree. ## NFC (Near Field Communication) A technology used for short-range communication between devices, often used in passports for wireless data transfer. ## Non-fungible token (NFT) An NFT is a piece of data stored on a blockchain that certifies a digital asset to be unique and not interchangeable with any other asset. NFTs are different from fungible tokens such as BTC and ETH because fungible tokens are equivalent and interchangeable. ## Nullifier A unique value used to prevent double-voting or double-spending in a privacy-preserving way. ## Poseidon Hashing A hash function optimized for zero-knowledge proof systems, used in the ZK Registry and Seedless Recovery. ## Query Proof A zero-knowledge proof that verifies specific information from a biometric passport or other identity document. ## RMO RMO is the Rarimo token. The RMO token is used for governance of the Rarimo project, among other things. Users can stake RMO tokens to become oracles. ## Rarimo App A mobile application that turns a biometric passport into an anonymous digital ID. ## Seedless Recovery An innovative, visual approach to crypto account recovery using physical objects or biometrics. ## Smart contract Smart contracts are programs that run on EVM-compatible blockchains. They can provide public functions and accept transactions, such as selling NFTs or exchanging tokens. Smart contracts provide the logic for NFTs and decentralized applications. ## Sparse Merkle Tree A data structure designed for efficiently storing and verifying large data sets with minimal storage requirements. ## ZK Graph ZK Graph allows users to prove their reputation based on private interactions or relationships using zero-knowledge proofs, ensuring privacy while verifying trustworthiness. ## ZK Likeness ZK Likeness is a ZKML solution powered by the Bionetta framework. It verifies that two faces belong to the same person without revealing any biometric data, enabling privacy-preserving face recognition for various applications. ## ZK Liveness A solution to verify that a user is a live person, used in conjunction with ZK Likeness. ## ZK Passport A solution that allows users to prove their identity on-chain without revealing private data from their passport. ## ZK Registry The ZK Registry is a privacy-focused on-chain registry. It enables proving system states without revealing individual details, supporting applications like identity verification and reputation systems. This design ensures data integrity and user anonymity​. ## ZK Registry Rollup A ZK Registry Rollup is an on-chain registry used to prove system states without exposing individual data, enabling privacy-preserving applications. ## ZKML (Zero-Knowledge Machine Learning) ZKML stands for Zero-Knowledge Machine Learning, which combines zero-knowledge proofs (ZK proofs) with machine learning algorithms. It enables machine learning tasks to be performed on encrypted or private data without revealing the data itself, ensuring privacy and confidentiality. ## Zero-Knowledge Proof (ZKP) A ZKP is a cryptographically secure piece of information that proves that a certain user meets a certain requirement without revealing any specific information about the user. For example, a ZKP can verify that a user is above a certain age without revealing the user's actual age. ## Zero-Knowledge Succinct Non-Interactive Argument of Knowledge (zk-SNARK) A zk-SNARK is the type of ZKP that Rarimo Identity Protocol uses. One characteristic of zk-SNARKs is that they are non-interactive, meaning that the user and verifier interact only once, unlike some other types of ZKPs. --- ## ZK Graph smart contracts reference import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Graph smart contracts reference >TODO --- ## Guide: verifying user social graph privately import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: verifying user social graph privately >TODO --- ## ZK Graph import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Graph ## What is ZK Graph? ZK Graph allows users to prove their social standing without compromising privacy. ## Why ZK Graph? Traditional reputation systems often require users to expose personal data or interactions to verify credibility. ZK Graph solves this by letting users demonstrate trustworthiness without revealing sensitive information. This approach benefits social and professional platforms where privacy is essential, from decentralized marketplaces to community-driven DAOs. ## How does it work? ZK Graph uses zero-knowledge proofs to verify user actions stored in a permissionless registry. Users can selectively disclose verified elements of their social reputation while the specifics of each action remain hidden. This process enables a high degree of privacy, as only the proof, not the underlying actions, is revealed. ## Use cases - **Decentralized Marketplaces**: Prove trustworthiness as a buyer or seller without revealing complete transaction histories. - **Social Platforms**: Demonstrate social standing or contributions without exposing individual interactions. - **DAOs and Governance**: Verify participation and influence in community activities while keeping voting records private. - **On-chain Games**: Prove your character's level without doxxing your wallet. ## Can I use it now? ZK Graph is under development and is currently seeking early adopters. Please get in touch with us on Telegram. --- ## ZK Likeness import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Likeness :::info ZK Likeness is currently in an active development phase, and the implementation is currently private. Contact us on Telegram to get early access to this model. ::: ZK Likeness is a ZKML solution powered by [Bionetta ZKML framework](/zkml-bionetta) designed to verify that two faces are of the same person without revealing any biometric data. This enables privacy-preserving face recognition for various applications. ## ZK Likeness Registry The ZK Likeness Registry is a decentralized solution powered by ZK Liveness and ZK Registry technology, enabling individuals to manage and license their digital likeness. Each record links an extracted key from user biometric data with access policies and optional additional information. To create an entry, users process their biometric data through an extractor, define policies like "PROHIBITED," "FREE USAGE," or "PAYABLE," provide relevant details such as payment addresses for commercial uses and create a ZK proof of likeness authenticating the transaction. Data consumers, such as AI models, can verify the presence of data without accessing the raw data, enhancing privacy. Crucially, the face scan data never leaves the device during likeness creation, and it cannot be reverse-engineered from the registry. This approach provides significant data protection benefits, eliminating the need for companies to handle, transmit, or store sensitive biometric information and reducing compliance risks and operational burdens. ## Use cases ZK Likeness can be used in various scenarios, including: - **Managing use of likeness for AI-generated content**: Verifying that a person has given consent for their likeness to be used in AI-generated content, such as deepfakes or avatars, without revealing their actual face. - **Identity verification**: Confirming a user's identity by comparing a new selfie with a registered face. - **Access control**: Granting access to physical or digital resources based on facial recognition without storing or processing raw biometric data. - **Duplicate account prevention**: Detecting and preventing users from creating multiple accounts using different identities but the same face. --- ## ZK Liveness import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Liveness :::info ZK Liveness is currently in an active development phase, and the implementation is currently private. Contact us on Telegram to get early access to this model. ::: :::info Check out the [ZK Liveness demo](https://zk-liveness-airdrop.rarimo.com/), showcasing a liveness check for an airdrop. ::: ZK Liveness is a ZKML solution powered by [Bionetta ZKML framework](/zkml-bionetta) designed to verify the presence of a live person in online interactions, preventing spoofing attacks. It allows users to prove their liveness without revealing any personal information or biometric data. Currently, ZK Liveness achieves **accuracy of 97%** in detecting liveness, making it a viable solution for low-risk applications. ## Use cases ZK Liveness can be used in various scenarios, including: - **Proof of humanity**: Verifying that a user is a real person, not a bot or an AI. - **Airdrops**: Ensuring that airdrop participants are real, live users. - **Multi-factor authentication**: Adding an extra layer of security by verifying the user's liveness during authentication. - **Online voting**: Ensuring that voters are live participants, preventing vote manipulation through spoofing. --- ## Biometric passports 101 import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Biometric passports 101 Biometric passports are now a standard travel document, issued by 172 nations worldwide in accordance with the ICAO Machine Readable Travel Documents standard. They serve as one of the most widely recognized identity credentials globally. Each biometric passport includes a *Machine-Readable Zone (MRZ)*, which contains an RFID chip. This chip securely stores data for biometric verification, personal information, the passport's expiration date, and the issuing authority’s digital signature. To verify the passport’s validity, the verifier reads the MRZ using an NFC scanner, checks the issuing authority’s digital signature, and confirms the expiration date. --- ## ZK Passport smart contracts reference import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Passport smart contracts reference :::info We're in process of migrating from legacy Circom circuits to Noir Ultra Plonk for passport verification during registration. Until the migration is complete, both are supported resulting in existence of `...Noir` methods ::: A set of contracts facilitating self-issued anonymous identities based on the ZK Passport tech. Currently, there are seven contract sections: 1. State — main `StateKeeper` contract and a set of utility contracts. `StateKeeper` acts as a singleton state instance with which registration contracts interact. `StateKeeper` does the following: * Stores the Sparse Merkle Tree with registered identities; * Stores the Sparse Merkle Tree with registered certificates of ICAO members; * Stores and manages some passport and identity information; * Manages the "Passport \ Identity" bonds. 1. Registration — primary section with `Registration2` contract, that: * Allow users registration (with binding of their identity to the passport); * Allow revocation of passport-identity binding; * Allow re-issuance of previously revoked identity; * Allow registration and revocation of ICAO member's certificates (calls `StateKeeper`); * Call one of the verifier contracts, based on the provided passport type, to verify ZKP; * Manage different dispatchers. 1. Verifiers — auto-generated contracts, the main purpose of which is to verify the ZKP. 1. Dispatchers — acts as a bridge between registration contracts and: * authenticators corresponding to different passport types, if it is a passport dispatcher; * signers, if it is a certificate dispatcher. 1. Signers — responsible for verifying the signature of the ICAO member's certificate. It also stores information required for signature verification (e.g. RSA exponent). 1. Authenticators — perform the passport Active Authentication (AA). 1. Utilities — some utility contracts, such as X509, SHA1, RSA implementations in solidity language and other. ### Deployments | Name | Address | |-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------| | StateKeeper | `0x61aa5b68D811884dA4FEC2De4a7AA0464df166E1` | | RegistrationSMT | `0x479F84502Db545FA8d2275372E0582425204A879` | | CertificatesSMT | `0xA8b350d699632569D5351B20ffC1b31202AcEDD8` | | Registration2 | `0x11BB4B14AA6e4b836580F3DBBa741dD89423B971` | ## `Registration2` ### Interface ```solidity contract Registration2 is Initializable, TSSUpgradeable { StateKeeper public stateKeeper; mapping(bytes32 => address) public certificateDispatchers; mapping(bytes32 => address) public passportDispatchers; mapping(bytes32 => address) public passportVerifiers; function registerCertificate( Certificate memory certificate_, ICAOMember memory icaoMember_, bytes32[] memory icaoMerkleProof_ ) external virtual { /* ... */ } function revokeCertificate(bytes32 certificateKey_) external virtual { stateKeeper.removeCertificate(certificateKey_); } function register( bytes32 certificatesRoot_, uint256 identityKey_, uint256 dgCommit_, Passport memory passport_, VerifierHelper.ProofPoints memory zkPoints_ ) external virtual { /* ... */ } function registerViaNoir( bytes32 certificatesRoot_, uint256 identityKey_, uint256 dgCommit_, Passport memory passport_, bytes memory zkPoints_ ) external virtual { /* ... */ } function revoke(uint256 identityKey_, Passport memory passport_) external virtual { /* ... */ } function reissueIdentity( bytes32 certificatesRoot_, uint256 identityKey_, uint256 dgCommit_, Passport memory passport_, VerifierHelper.ProofPoints memory zkPoints_ ) external virtual { /* ... */ } function reissueIdentityViaNoir( bytes32 certificatesRoot_, uint256 identityKey_, uint256 dgCommit_, Passport memory passport_, bytes memory zkPoints_ ) external virtual { /* ... */ } function updateDependency( MethodId methodId_, bytes calldata data_, bytes calldata proof_ ) external virtual { /* ... */ } } ``` - `stateKeeper` — instance of `StateKeeper` contract that saves all data about registrations, certificates, etc.; - `certificateDispatchers` — mapping from ICAO member's certificate to its dispatcher; - `passportDispatchers` — mapping from passport type to its dispatcher contract; - `passportVerifiers` — mapping from passport type to its verifier contract; - `registerCertificate(...)` — registers an ICAO member's X509 certificate in the certificates SMT (calls `StateKeeper` contract); - `revokeCertificate(...)` — revokes an expired ICAO member's X509 certificate (calls `StateKeeper` contract); - `register(...)` — registers the user (identity) by its passport, technically — issues the identity, calls the dispatcher to verify ZKP, links the identity to its passport by calling `StateKeeper`; - `registerViaNoir(...)` - the same as `register(...)` but uses Noir proof instead of Circom; - `revoke(...)` — revokes the identity and its passport by the user's will, marks the passport (and identity) as revoked; - `reissueIdentity(...)` — re-issues previous passport \ identity bond by migration to a new identity; - `reissueIdentityViaNoir(...)` — the same as `reissueIdentity(...)` but uses Noir proof instead of Circom; - `updateDependency(...)` — adds or removes, depending on the method provided, a dispatcher via Rarimo TSS; For the full implementation, see Registration2.sol at the GitHub. ## `Verifiers` ### Interface ```solidity contract ExampleVerifier { /* some constants */ function verifyProof( uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[4] calldata _pubSignals ) public view returns (bool) { /* ... */ } } ``` All verifiers are generated contracts that share the same interface. However, each verifier is designed to accept proofs generated by specific ZK schemas. Below is a list of the current verifiers: * `Circom verifiers` — the list of acceptable by `Registration2` smart contracts to verify biometric passport; * `Noir verifiers` — the list of acceptable by `Registration2` smart contracts to verify biometric passport; * `PInternalOptVerifier2` — Georgian ID card verifier for `Registration2`; * `PInternalVerifier2` — Georgian ID card verifier for `Registration2`; * `PMNEOptVerifier2` — Montenegro ID card verifier for `Registration2`; - `verifyProof(...)` — verifies passport validity ZKP; For the full implementation, see verifiers for `Registration2` at the GitHub. ## `PECDSASHA1Dispatcher` ### Interface ```solidity contract PECDSASHA1Dispatcher is IPassportDispatcher, Initializable { address public authenticator; function authenticate( bytes memory challenge_, bytes memory passportSignature_, bytes memory passportPublicKey_ ) external view returns (bool) { /* ... */ } } ``` - `authenticator` — address of the ECDSASHA1Authenticator contract for passports; - `authenticate(...)` — authenticates the ECDSASHA1 passport; For the full implementation, see PECDSASHA1Dispatcher.sol at the GitHub. ## `PNOAADispatcher` ### Interface ```solidity contract PNOAADispatcher is IPassportDispatcher, Initializable { function authenticate( bytes memory, bytes memory passportSignature_, bytes memory passportPublicKey_ ) external pure returns (bool) { return passportSignature_.length == 0 && passportPublicKey_.length == 0; } } ``` This is a special dispatcher for passports without Active Authentication. - `authenticate(...)` — authenticates the passport without AA. Just return __true__; For the full implementation, see PNOAADispatcher.sol at the GitHub. ## `PRSASHADispatcher` ### Interface ```solidity contract PRSASHADispatcher is IPassportDispatcher, Initializable { address public authenticator; function authenticate( bytes memory challenge_, bytes memory passportSignature_, bytes memory passportPublicKey_ ) external view returns (bool) { return PRSASHAAuthenticator(authenticator).authenticate( challenge_, passportSignature_, passportPublicKey_ ); } } ``` - `authenticator` — address of the RSASHAAuthenticator contract for passports; - `authenticate(...)` — authenticates the RSASHA passport; For the full implementation, see PRSASHADispatcher.sol at the GitHub. ## `CRSADispatcher` ### Interface ```solidity contract CRSADispatcher is ICertificateDispatcher, Initializable { address public signer; function verifyICAOSignature( bytes memory x509SignedAttributes_, bytes memory icaoMemberSignature_, bytes memory icaoMemberKey_ ) external view override returns (bool) { return ICertificateRSASigner(signer).verifyICAOSignature( x509SignedAttributes_, icaoMemberSignature_, icaoMemberKey_ ); } } ``` As for passports, there are authenticators for certificates, and it's one of them. - `signer` — address of the CRSASigner or CRSAPSSSigner contract for certificates; - `verifyICAOSignature(...)` — verifies RSA signature of ICAO member's certificate; For the full implementation, see CRSADispatcher.sol at the GitHub. ## `CRSAPSSSigner` ### Interface ```solidity contract CRSAPSSSigner is ICertificateRSASigner, Initializable { uint256 public exponent; bool public isSha2; function verifyICAOSignature( bytes memory x509SignedAttributes_, bytes memory icaoMemberSignature_, bytes memory icaoMemberKey_ ) external view override returns (bool) { return RSAPSS.verify( x509SignedAttributes_, icaoMemberSignature_, abi.encodePacked(exponent), icaoMemberKey_, isSha2 ); } ``` - `exponent` — RSAPSS exponent; - `isSha2` — hash function switcher, true - sha2, false - sha512; - `verifyICAOSignature(...)` — verifies ICAO member RSAPSS signature of the X509 certificate SA; For the full implementation, see CRSAPSSSigner.sol at the GitHub. ## `CRSASigner` ### Interface ```solidity contract CRSASigner is ICertificateRSASigner, Initializable { uint256 public exponent; // RSA exponent bool public isSha1; // hash function switcher, true - sha1, false - sha2 function verifyICAOSignature( bytes memory x509SignedAttributes_, bytes memory icaoMemberSignature_, bytes memory icaoMemberKey_ ) external view override returns (bool) { /* ... */ } } ``` - `exponent` — RSA exponent; - `isSha2` — hash function switcher, true - sha2, false - sha512; - `verifyICAOSignature(...)` — verifies ICAO member RSA signature of the X509 certificate SA.; For the full implementation, see CRSASigner.sol at the GitHub. ## `PECDSASHA1Authenticator` ### Interface ```solidity contract PECDSASHA1Authenticator { /* brainpool256r1 parameters */ function authenticate( bytes memory challenge, uint256 r, uint256 s, uint256 x, uint256 y ) external pure returns (bool) { /* ... */ } } ``` - `authenticate(...)` — checks active authentication of a passport (ECDSA signature); For the full implementation, see PECDSASHA1Authenticator.sol at the GitHub. ## `PRSASHAAuthenticator` ### Interface ```solidity contract PRSASHAAuthenticator is Initializable { uint256 public exponent; // RSA exponent function authenticate( bytes memory challenge_, bytes memory s_, bytes memory n_ ) external view returns (bool) { /* ... */ } } ``` - `exponent` — RSA exponent; - `authenticate(...)` — checks active authentication of a passport (RSA signature); For the full implementation, see PRSASHAAuthenticator.sol at the GitHub. ## `TD3QueryProofVerifier` ### Interface ```solidity contract TD3QueryProofVerifier { function verifyProof( uint256[2] calldata _pA, uint256[2][2] calldata _pB, uint256[2] calldata _pC, uint256[] calldata _pubSignals ) external view returns (bool) { /* ... */ } } ``` - Verifies TD3-query zero-knowledge proofs produced by the Query circuit (e.g., age, uniqueness, country masks) without revealing raw passport data. - Accepts Groth16 proof points and the public signals array (23 items for Query circuit) and returns `true` if valid. - Intended for SDK-level integrations where on-chain verification of query proofs is needed. For the full implementation, see TD3QueryProofVerifier.sol at the GitHub. ## `PublicSignalsBuilder` ### Interface ```solidity library PublicSignalsBuilder { uint256 public constant PROOF_SIGNALS_COUNT = 23; function newPublicSignalsBuilder( uint256 selector_, uint256 nullifier_ ) internal pure returns (uint256 dataPointer_); function withName(uint256 dataPointer_, uint256 name_) internal pure; function withNameResidual(uint256 dataPointer_, uint256 nameResidual_) internal pure; function withNationality(uint256 dataPointer_, uint256 nationality_) internal pure; function withCitizenship(uint256 dataPointer_, uint256 citizenship_) internal pure; function withSex(uint256 dataPointer_, uint256 sex_) internal pure; function withEventIdAndData(uint256 dataPointer_, uint256 eventId_, uint256 eventData_) internal pure; function withIdStateRoot(uint256 dataPointer_, bytes32 idStateRoot_) internal view; function withSelector(uint256 dataPointer_, uint256 selector_) internal pure; function withCurrentDate(uint256 dataPointer_, uint256 currentDate_, uint256 timeBound_) internal view; function withTimestampLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure; function withIdentityCounterLowerbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure; function withBirthDateLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure; function withExpirationDateLowerboundAndUpperbound(uint256 dataPointer_, uint256 lb_, uint256 ub_) internal pure; function withCitizenshipMask(uint256 dataPointer_, uint256 citizenshipMask_) internal pure; function buildAsUintArray(uint256 dataPointer_) internal pure returns (uint256[] memory); function buildAsBytesArray(uint256 dataPointer_) internal pure returns (bytes32[] memory); } ``` - Helper library to construct the Query circuit public signals in the exact index layout expected by verifiers (see Query circuit public signals spec). - Validates constraints via `AQueryProofExecutor` storage; `withIdStateRoot(...)` checks `IPoseidonSMT(registrationSMT).isRootValid(...)` before writing the root at index 11. - Date handling: `withCurrentDate(...)` parses `yyMMdd` to a timestamp and enforces a ±timeBound window using `Date2Time` utilities. - Produces either `uint256[]` or `bytes32[]` views of the same in-memory array for downstream verifier calls. For the full implementation, see PublicSignalsBuilder.sol at the GitHub. ## `RegistrationSMTReplicator` ### Interface ```solidity contract RegistrationSMTReplicator is IPoseidonSMT { uint256 public constant ROOT_VALIDITY = 1 hours; string public constant REGISTRATION_ROOT_PREFIX = "Rarimo root"; address public sourceSMT; bytes32 public latestRoot; uint256 public latestTimestamp; event RootTransitioned(bytes32 newRoot, uint256 transitionTimestamp); function __RegistrationSMTReplicator_init( address[] memory oracles_, address sourceSMT_ ) external; function addOracles(address[] memory oracles_) external; function removeOracles(address[] memory oracles_) external; function setSourceSMT(address newSourceSMT_) external; function transitionRoot(bytes32 newRoot_, uint256 transitionTimestamp_) external; function transitionRootWithSignature( bytes32 newRoot_, uint256 transitionTimestamp_, bytes memory signature_ ) external; function isRootValid(bytes32 root_) external view returns (bool); function isRootLatest(bytes32 root_) public view returns (bool); function isOracle(address oracle_) public view returns (bool); function getOracles() external view returns (address[] memory); } ``` - Deployed on target chains to replicate the Registration SMT root when the canonical `StateKeeper` is not directly available. - Root updates can be pushed by: - Authorized oracles via `transitionRoot(...)`. - Anyone with an oracle-signed message via `transitionRootWithSignature(...)` using `keccak256(abi.encodePacked(REGISTRATION_ROOT_PREFIX, sourceSMT, address(this), newRoot, timestamp))` recovered with ECDSA. - `isRootValid(root)` returns true if the root is the latest or if it was observed within `ROOT_VALIDITY` (1 hour), enabling lightweight freshness checks by verifiers depending on `IPoseidonSMT`. - Owners manage the oracle set and can update `sourceSMT`; contract follows UUPS upgradeability and multi-owner controls. For the full implementation, see RegistrationSMTReplicator.sol at the GitHub. --- ## Guide: Using ZK Passport for off-chain verification import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: Using ZK Passport for off-chain verification Before getting started with implementing off-chain verification, let's quickly review all the steps of the process on the diagram below. The basic verification flow works like this: 1. User installs the Rarimo App and scans their identity document 2. User scans a QR-code in your DApp 3. User gets prompted to generate a proof of the requested attribute in Rarimo App 4. The app submits the proof to the callback URL (specified in the QR code) for verification 5. Your DApp backend fetches the verification status and verified data from a REST API You can check out this flow in the sandbox: ZK Passport Demo ## [Optional]: Spin up your own verificator backend By default, the library uses a public instance of `verificator-svc` hosted by Rarimo contributors: https://api.app.rarime.com/ However, you can also host your own instance of `verificator-svc` to have full control over the verification process and data. Follow the instructions in the [Hosting your own verificator](/zk-passport/guide-setting-up-verificator-svc) guide to set up your own instance. ## Step #1: Install `@rarimo/zk-passport-react` package To install the package, run the following command in your project directory: ```bash npm2yarn npm install @rarimo/zk-passport-react ``` If you don't use React, you can use the `@rarimo/zk-passport` package instead. It provides a low-level API for requesting and verification of ZK Passport proofs. ```bash npm2yarn npm install @rarimo/zk-passport ``` ## Step #2: Import the library and render the QR code The following JS snippet uses the library to render a QR code with a verification request and handles the results: ```jsx import ZkPassportQrCode from '@rarimo/zk-passport-react' // this is a public instance of verificator-svc(https://github.com/rarimo/verificator-svc). Replace it with the URL of your instance const apiUrl = 'https://api.app.rarime.com' // `requestid` is a public unique ID associated with a user within your system. It will be used later to check the user status const requestId = 'account-1' // `event_id` is an arbitrary number that denotes the application scope of the proof const eventId = 1337 const verificationOpts: RequestVerificationLinkOpts = { uniqueness: true, // check if the user has already passed the verification nationalityCheck: true, // include user's citizenship in the output(proof public signals) // you can include other options based on your verification requirements eventId: eventId, // scope of the proof } return ( console.log(status)} onSuccess={proof => console.log(proof)} onError={error => console.error(error)} /> ) ``` ## Step #3: Fetch the verification status and data on the backend You can fetch the verification status for a particular `id` from this verificator endpoint: https://rarimo.github.io/verificator-svc/#tag/User-verification/operation/getUserStatus Here is a JS snippet for checking verification status: ```ts const backendUrl = 'https://api.app.rarime.com/' const userId = 'some-user-id' // the `id` from step #3 const response = await fetch(`${backendUrl}/integrations/verificator-svc/private/verification-status/${userId}`) const { data } = await response.json() // responseUserId === userId const responseUserId = data.id // Verification status enum: // - "not_verified" - user is not verified // - "verified" - user is verified // - "failed_verification" - user verification failed // - "uniqueness_check_failed" - user uniqueness check failed const verificationStatus = data.attributes.status ``` If the user has status `verified`, you can fetch the verified attribute data like this: ```ts const response = await fetch(`${backendUrl}/integrations/verificator-svc/private/user/${userId}`) const { data } = await response.json() // Depending on what attributes you requested in your verification options // For example, if you requested nationalityCheck: // ISO 3166 alpha-3 country code: https://www.iban.com/country-codes // Note: German passports may have a single-letter "D" code const citizenshipISOCode = data.attributes.nationality // e.g. "UKR" // Other attributes might be available depending on your verification request ``` ## Example use cases Here are some common verification scenarios you can implement: 1. **Citizenship verification**: Verify a user's nationality without seeing their passport 2. **Age verification**: Check if a user is above a certain age without knowing their birth date 3. **Identity uniqueness**: Ensure each physical person can only create one account ## Conclusion In this guide, we set up an off-chain verification system using ZK Passport and the Rarimo App, enabling secure proof of user attributes without exposing private data. This approach allows you to create privacy-preserving verification flows for various use cases while maintaining compliance with data protection regulations. --- ## Guide: Setting up on-chain user verification with ZK Passport import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: Setting up on-chain user verification with ZK Passport This tutorial walks you through the process of setting up on-chain verification of ZK Passport proofs. By the end of this tutorial, you will have: 1. Ensured that you have all the necessary components in place to verify ZK Passport proofs on your chain. 2. Deployed the `TD3QueryProofVerifier` contract on your chain to handle proof verification. 3. Integrated verification logic into your own contract to validate proof public signals. { //TODO: // - guide to customizing the query params } ## Prerequisites - If you are using an EVM-compatible chain other than Rarimo's L2, you'll need to set up ZK Passport Registry state replication as described in [Setting up Cross-Chain Verification](/zk-passport/guide-setting-up-cross-chain-replication). - Access to the `verificator-svc` service for retrieving proof parameters. You can use the public instance of `verificator-svc` (https://api.app.rarime.com/) for testing, but it's recommended to deploy your own instance for production use, as described in [Setting up `verificator-svc`](/zk-passport/guide-setting-up-verificator-svc). ## 1. Setting up the smart contract for on-chain verification ### 1.1 Add the Rarimo Passport Contracts library to your project The contracts are available in the @rarimo/passport-contracts npm package: ```bash npm2yarn npm install @rarimo/passport-contracts ``` ### 1.2 Deploy the TD3QueryProofVerifier smart contract The `TD3QueryProofVerifier ` contract is responsible for verifying ZK proofs on-chain. This contract often comes with a precompiled ZK circuit. Import the `TD3QueryProofVerifier ` contract from the Rarimo Passport Contracts library: ```solidity import {TD3QueryProofVerifier } from "@rarimo/passport-contracts/sdk/verifier/TD3QueryProofVerifier.sol"; ``` Then deploy it to your chain and record the address of the newly deployed `TD3QueryProofVerifier `. You will use this address in your other contracts to verify proofs. ### 1.3 Integrate proof verification into your smart contract Once your chain is replicating ZK Registry state and you have a deployed `TD3QueryProofVerifier ` contract, you can add ZK proof checks to your own contract logic. Below is a skeleton of the DApp that relies on the Query Proof Verification to perform some actions (token mint, grant a role, etc.). ```solidity pragma solidity ^0.8.28; import {IPoseidonSMT} from "@rarimo/passport-contracts/interfaces/state/IPoseidonSMT.sol"; import {AQueryProofExecutor} from "@rarimo/passport-contracts/sdk/AQueryProofExecutor.sol"; import {PublicSignalsBuilder} from "@rarimo/passport-contracts/sdk/lib/PublicSignalsBuilder.sol"; contract MyPassportContract is AQueryProofExecutor { struct UserData { uint256 nullifier; uint256 identityCreationTimestamp; } mapping(uint256 => bool) public usedNullifiers; constructor(address registrationSMT_, address verifier_) { __AQueryProofExecutor_init(registrationSMT_, verifier_); } // Called before proof verification function _beforeVerify(bytes32, uint256, bytes memory userPayload_) public override { (address user, UserData memory userData) = abi.decode( userPayload_, (address, UserData) ); require(!usedNullifiers[userData.nullifier], "Nullifier already used"); usedNullifiers[userData.nullifier] = true; } // Called after successful proof verification function _afterVerify(bytes32, uint256, bytes memory userPayload_) public override { (address user, UserData memory userData) = abi.decode( userPayload_, (address, UserData) ); // Grant access, mint tokens, or perform other actions // Example: grantAccess(user); } // Builds the public signals for verification function _buildPublicSignals( bytes32, uint256 currentDate_, bytes memory userPayload_ ) public override returns (uint256 dataPointer_) { (address user, UserData memory userData) = abi.decode( userPayload_, (address, UserData) ); // Query proof verification logic here return dataPointer_; } } ``` The critical parts are the three required override methods: ```solidity // Called before proof verification function _beforeVerify(bytes32 registrationRoot_, uint256 currentDate_, bytes memory userPayload_) public override { /* ... */ } // Called after successful proof verification function _afterVerify(bytes32 registrationRoot_, uint256 currentDate_, bytes memory userPayload_) public override { /* ... */ } // Builds the public signals for verification function _buildPublicSignals(bytes32 registrationRoot_, uint256 currentDate_, bytes memory userPayload_) public override returns (uint256 dataPointer_) { /* ... */ } ``` Above, your contract inherits from `AQueryProofExecutor`, which handles the ZK proof validation internally. Below, you can see detailed structure for the `_buildPublicSignals` function. Which is the core of the SDK, where you setup the constraints for identity verification. ```solidity function _buildPublicSignals( bytes32, uint256 currentDate_, bytes memory userPayload_ ) public override returns (uint256 dataPointer_) { (address user, UserData memory userData) = abi.decode( userPayload_, (address, UserData) ); // highlight-start uint256 identityCreationTimestampUpperBound = getIdentityCreationTimestampUpperBound(); uint256 identityCounterUpperBound = type(uint32).max; if (userData.identityCreationTimestamp > 0) { identityCreationTimestampUpperBound = userData.identityCreationTimestamp; identityCounterUpperBound = 1; } dataPointer_ = PublicSignalsBuilder.newPublicSignalsBuilder(SELECTOR, userData.nullifier); dataPointer_.withEventIdAndData(getEventId(user), getEventData()); dataPointer_.withCurrentDate(currentDate_, 1 days); dataPointer_.withTimestampLowerboundAndUpperbound(0, identityCreationTimestampUpperBound); dataPointer_.withBirthDateLowerboundAndUpperbound( PublicSignalsBuilder.ZERO_DATE, BIRTHDAY_UPPERBOUND ); dataPointer_.withIdentityCounterLowerbound(0, identityCounterUpperBound); dataPointer_.withExpirationDateLowerboundAndUpperbound( currentDate_, PublicSignalsBuilder.ZERO_DATE ); // highlight-end return dataPointer_; } // Helper functions function getIdentityCreationTimestampUpperBound() public view returns (uint256) { return accessStartTimestamp - IPoseidonSMT(getRegistrationSMT()).ROOT_VALIDITY(); } function getEventId(address user) public view returns (uint256) { // Implementation depends on your application return uint256(keccak256(abi.encodePacked(block.chainid, address(this), user))); } function getEventData() public view returns (uint256) { // Implementation depends on your application return uint256(uint248(uint256(keccak256(abi.encodePacked(/* some data*/))))); } ``` The client application calls the `function execute(registrationRoot, currentDate, userPayload, zkPoints_)` to perform the actions and verify the Query Proof. { // TODO: reference to the public signals builder when it's ready } To learn more about the verification parameters(public signals) and how to build them, check out PublicSignalsBuilder.sol. ## 2. Adding verification to your DApp front-end The @rarimo/zk-passport-react package provides a QR code that users can scan to initiate the verification process. This component handles the collection of user data, proof generation, and on-chain verification. ### 2.1 Install the ZK Passport React package First, install the SDK and the `viem` library in your React project: ```npm2yarn npm install viem@^2.31.0 @rarimo/zk-passport-react ``` ### 2.2 Add the QR code component to your DApp To render the ZK Passport QR code in your DApp, you can use the `ZkPassportQrCode` component from the @rarimo/zk-passport-react package: ```jsx import ZkPassportQrCode from '@rarimo/zk-passport-react' import { mainnet } from 'viem/chains' // Import the chain you are using const requestId = 'account-1' const apiUrl = 'https://api.app.rarime.com' // or your own instance of `verificator-svc` const contractAddress = '' // Address of the deployed TD3QueryProofVerifier contract from Step 1.2 const receiverAddress = '' // User's wallet address const chain = mainnet return ( console.log(status)} onSuccess={proof => console.log(proof)} onError={error => console.error(error)} /> ) ``` To learn more about the ZK Passport React component, check out the ZK Passport React component documentation. ## Reference implementation Here is a simple age-restricted airdrop DApp that allows users to claim tokens if they meet the age requirement: - **ClaimableToken.sol**: smart contract that implements the ZK Passport proof verification. - **on-chain-verification-react**: front-end that uses the ZK Passport React component to verify user age and uniqueness. Live demo is available at StackBlitz. ## Conclusion With these steps: 1. **Set up** the ZK Passport Registry state replication on your chain if needed. 1. **Deploy** the `TD3QueryProofVerifier` contract to facilitate ZK proof verification. 1. **Add** proof verification into your DApp's smart contract by inheriting from `AQueryProofExecutor` and implementing the required methods. 1. **Integrate** proof verification into your DApp's front-end using the ZK Passport React component. We've set up on-chain verification of ZK Passport proofs. This allows you to verify user identities without revealing sensitive data, ensuring privacy and security in your application. --- ## Guide: Setting up cross-chain replication for ZK Passport import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: Setting up cross-chain replication for ZK Passport :::info Native state transfer to Ethereum L1 is currently under construction. ::: This guide walks you through the process of setting up cross-chain replication for ZK Passport Registry state. By the end of this tutorial, you will have: 1. Deployed the **RegistrationSMTReplicator** contract on the target chain. 2. Set up your relayer or front-end submission flow. ## Step 1: Deploy the RegistrationSMTReplicator The **RegistrationSMTReplicator** handles the replication of state from the ZK Registry onto your chain. This ensures your chain is kept up to date with changes to passport registrations. 1. **Install the Rarimo Passport Contracts** package: ```bash npm2yarn npm install @rarimo/passport-contracts ``` 1. **Generate a key pair** for the relayer that will submit state updates to the `RegistrationSMTReplicator` contract. You can use the `ethers` library to generate a new key pair: ```typescript import { ethers } from 'ethers'; const wallet = ethers.Wallet.createRandom(); console.log('Address:', wallet.address); console.log('Private Key:', wallet.privateKey); ``` Make sure to securely store the private key, as it will be used by the relayer to sign state updates. 1. **Deploy** the `RegistrationSMTReplicator` to your chain and initialize it: ```typescript import { RegistrationSMTReplicator } from '@rarimo/passport-contracts'; const replicator = await RegistrationSMTReplicator.deploy({ // Specify the chain and network parameters here }); const oracle = '0x...'; // Replace with your relayer address from the previous step const sourceSMT = '0x479F84502Db545FA8d2275372E0582425204A879'; // This is the address of the original ZK Passport Registry on Rarimo L2. await replicator.__RegistrationSMTReplicator_init( [oracle], // list of oracle public keys that can submit roots directly sourceSMT // contract address of the original registry on Rarimo L2 ); ``` ## Step 2: Set up the relayer with front-end submission to replicate the states To keep the state of the ZK Registry in sync with your chain, you need to set up a relayer with front-end submission. This will ensure that any changes in the ZK Registry are reflected in the `RegistrationSMTReplicator` contract. You have to set up backend service and make your front-end app update the root: 1. **Relayer**: Run a relayer service that listens for events from the ZK Registry. - Follow the instructions in proof-verification-relayer README. - Make sure you set the right replicator contract address and private key of corresponding `allowedUpdaterAddress` in the config. - Make sure endpoint is available for your frontend app. - Make sure that the relayer's corresponding key is set as an oracle in the replicator contract. You can check this using the `isOracle(address)` method on the `RegistrationSMTReplicator` contract. 2. **Front-end**: Send the state update transactions on-demand from your front-end: - After obtaining the proof from verificator-svc you know the required `root` (it is a 11th pub signal). - Now you can get signed state by root ```typescript async function getSignedRootState(root: string) { const API_BASE_URL = 'https://api.example.com' const requestUrl = new URL(`${API_BASE_URL}/integrations/proof-verification-relayer/v2/state`) requestUrl.searchParams.set('filter[root]', root) const res = await fetch(requestUrl.toString()) const { data } = await res.json() return { // Signature of root state signed by relayer private key. signature: data.attributes.signature, // Time when the event was caught, a.k.a state transition timestamp timestamp: data.attributes.timestamp, } } ``` - You can use the `transitionRootWithSignature(root, timestamp, signature)` method on the `RegistrationSMTReplicator` contract to submit state updates. --- ## Guide: Setting up your own off-chain verificator for ZK Passport import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Guide: Setting up your own off-chain verificator for ZK Passport `verificator-svc` is a service that verifies ZK Passport proofs off-chain. It provides a convenient REST API for requesting parameters for the QR code, checking the user verification status, etc. You need to trust the instance of `verificator-svc`, so the best option is to host your own instance. ## Prerequisites - Docker - PostgreSQL 10+ ## Step 1: Create a config fields Here is a sample config file for `verificator-svc`. Set the appropriate `db` connection string, `verifier.event_id`, and `callback.url` values for your deployment: ```yaml title="config.yaml" log: level: debug disable_sentry: true db: # Your PostgreSQL connection string goes here url: "postgres://..." verifier: # Pick an arbitrary sting as a default value for the event_id. event_id: "my_app_event_id" verification_key_path: "./proof_keys/passport.json" allowed_identity_timestamp: 1741972260 multiproof: false preserve_user_id_case: false erc_1155: "0x0000000000000000000000000000000000000000" callback: # URL of the current instance of verifier-svc url: "http://mybackend.net:8000" listener: addr: :8000 cop: disabled: true endpoint: "http://..." upstream: "http://..." service_name: verificator-svc service_port: 80 signature_verification: pub_key: "04e29323ad356ab524fa5dbe3e490244e741b4d445ac7d2ee5f321556b3fda616bb9d2f2216fc27e099ab3019103cca872679e130629b2b90ea16cedb2b2136371" poseidonsmt_root_verifier: rpc: "https://l2.rarimo.com" contract: "0x479F84502Db545FA8d2275372E0582425204A879" request_timeout: 10s auth: enabled: false addr: http://rarime-auth ``` { // TODO: explain other params e.g. `erc_1155`, `signature_verification` } ## Step 2: Run the service Run the service in docker with the following command: ```bash docker build -t github.com/rarimo/verificator-svc . docker run -e KV_VIPER_FILE=/config.yaml github.com/rarimo/verificator-svc ``` { // TODO: add sample output // You should see the following output: // ```bash // ``` } ## API reference `verificator-svc` API reference can be found here: https://rarimo.github.io/verificator-svc/ ## Running from source and contributing Refer to the README on github. --- ## Supported countries import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Supported countries >TODO --- ## ZK Passport import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Passport Rarimo has developed a novel solution that turns a regular biometric passport into a flexible digital identity credential for Web3. Importantly, this is done without compromising the security of your sensitive data by using zero-knowledge proofs. ZK Passport can be used to prove reliably: - *Uniqueness* - *Humanity* - *Citizenship* - *Age* - *Any other data contained in the passport* ## How it works :::info Check out the [ZK Passport sandbox](https://app.rarime.com/proof-requests-demo) to get familiar with the flow. ::: Using ZK Passport from a user perspective is simple and seamless: 1. **Set Up**: Install the Rarimo App on your phone. Use the app to scan your passport with the phone's camera and NFC chip during a one-time setup. 1. **Proof Request**: When a DApp requests passport proof, scan the QR code provided by the DApp using the Rarimo App and approve the proof generation request. 1. **Proceed**: After the proof is processed, continue using the DApp without interruption. ## Light vs full passport verification To start using identity proofs, a user's passport needs to be registered in the ZK Registry's on-chain Merkle tree. This process eliminates the need to create expensive passport validity proofs for each transaction. Instead, users can rely on simpler proofs of inclusion in the Merkle tree. :::info DApps can verify the ZK Passport proofs both on-chain and off-chain. ::: For the initial registration, Rarimo provides two passport verification modes to balance decentralization with the computation load on users' mobile devices: - **Full Verification**: In this fully decentralized mode, the client generates a ZK proof to confirm two things: the passport's validity (by verifying the issuer's signature) and its integrity (by checking the hash). However, this process is limited by current smartphone hardware, which struggles to process certain digital signatures used in passports efficiently. - **Light Verification**: This mode reduces decentralization slightly to ease the computational load on the user's mobile device while maintaining privacy. The client generates proof of passport integrity (hash verification) while a trusted Rarimo verifier checks the passport's validity (issuer signature). This method works seamlessly with most passport types. To balance decentralization and compatibility, the Rarimo App defaults to the full verification mode and switches to light verification when necessary. This is done automatically and seamlessly for the user. ## Dictionary attacks and the privacy network effect Passport issuers or other third parties may try to identify users who participate in specific social apps by executing a "dictionary" attack that consists of hashing all known passports and checking them against the passport registry. By using a global Merkle tree of passport hashes shared by all users and social apps, Rarimo creates a *privacy network effect* where the system's security is enhanced with the growth of the user base. The dilution of hashes in the shared Merkle tree makes it progressively more challenging to deduce a connection between specific passports and social apps. The bigger the network gets, the more plausible deniability the users get. ## ZK circuits The ZK Passport circuits can be found in the following repositories: - passport-zk-circuits - passport-zk-circuits-noir Originally, the circuits were created using Circom, but the project is now transitioning to Noir. Noir eliminates the need for a per-circuit trusted setup, significantly reducing download sizes and enhancing overall security. This makes Noir the preferred framework for future ZK Passport development. ## Challenges and limitations The first implementation of ZK Passport comes with some limitations: - Some types of passports are not supported in the full verification mode yet due to limitations of mobile phone hardware. - No biometric checks are included, meaning someone may scan a stolen or borrowed passport. ZKML solutions may alleviate this in the future. - There's no way to prevent a holder of multiple passports from onboarding multiple times. - Only passports with Active Authentication support recovery at the moment. Alternative recovery methods are being researched. --- ## Resources import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; ## Step #1: Ask the user to install Rarimo App and scan their identity document Links for installation: - Rarimo for iOS | AppStore - Rarimo for Android | Google Play --- ## Use cases import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Use cases Here are some of the primary use cases for the ZK Passport technology. ## Geolocking ## Charity airdrops ## Age verification ## Proof of personhood DApp developers can use the passport-derived profiles to: - Restrict access for risky countries - Set minimum age requirements - Create provable social graph attestations - Prevent Sybil attacks - Verify personhood - Any other use cases with selective disclosure of the passport data. --- ## Rarimo ZK Rollup network info import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Rarimo ZK Rollup network info ## Chain metadata **Chain ID**: `7368` **Chain list**: eip155-7368.json **Native currency**: ETH **Block time**: ~15 seconds **Finality**: ~6 hours ## Links - **L2 Explorer**: https://scan.rarimo.com/ - **L2 RPC**: https://l2.rarimo.com - **L2 WSS RPC**: `wss://wss.l2.rarimo.com` - **Bridge**: https://l2bridge.rarimo.com/ ## L1 address book ### Smart contracts - **RollupProxy**: `0x2F3c205613d9451f88E19E011ED23775afe00C41` - **PlonkVerifier**: `0x0C1f51ccb366177D2C09a6f446A55C2C8C86F074` ### Infrastructure accounts - **Deployer**: `0xBbEF10cEA9eEFD021069a1fC4A348c00e799b01b` - **RollupSecurityCouncil**: `0xBbEF10cEA9eEFD021069a1fC4A348c00e799b01b` - **FinalizationOperator**: `0x3Cbe03D5BFc55b47e332B9Ff71feBa0F0e2133Dd` - **BlobSubmissionOperator**: `0x558AbE76A8258b55F14e5e16C92b6f6b087dEa0e` - **Postman**: `0xC974Ab7d1022a8532931f3078045b94EdBdC8d6d` ## L2 address book ### Smart contracts - **L2MessageServiceProxy**: `0x348D5a055aE9BdE2ac2d7EebceFF55C5b2A4F6CC` ### Infrastructure accounts - **Deployer**: `0x1b56ad29F2C447Ef04Dd2dC0922834E297648C6F` - **MessageServiceSecurityCouncil**: `0x1b56ad29F2C447Ef04Dd2dC0922834E297648C6F` - **MessageAnchoringOperator**: - `0x78f5cBeDBd43F9b2A16eD9a55539313B4ADc514b` - `0x1b56ad29F2C447Ef04Dd2dC0922834E297648C6F` - **Postman**: - `0x403C76C3C19D256B0467Dfd83dD333d1f896Dc64` - `0x1b56ad29F2C447Ef04Dd2dC0922834E297648C6F` --- ## ZK Registry import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # ZK Registry ## What is the ZK Registry? The ZK Registry is a permissionless registry designed to securely commit and prove private data on-chain without revealing it. By leveraging ZK Rollup technology, the ZK Registry centralizes identity-related verifications, making them verifiable across multiple applications while preserving user privacy. ## Why deploy on a ZK Rollup? Deploying the ZK Registry on a ZK Rollup rather than on a separate chain ensures enhanced security, flexibility, and interoperability by inheriting Ethereum’s security model. A ZK Rollup reduces data segregation, supporting a unified registry of passports, credentials, social graphs, etc, that can be accessed and verified across chains, thus minimizing privacy risks. Additionally, using a ZK Rollup optimizes scalability and simplifies the integration of new verification algorithms. ## How does the ZK Registry work? The ZK Registry functions through a modular architecture consisting of two primary components: - **EvidenceRegistry**: A central contract where statements are registered, as described in ERC-7812: ZK Identity Registry. It interacts with an on-chain database, **EvidenceDB**, which stores hashed proofs of user data. - **Registrar**: The component that manages specific use cases, such as identity verification or proof of attendance, by structuring data and coordinating with **EvidenceRegistry**. Each registered statement is stored in a Merkle tree, ensuring users can prove data inclusion without revealing sensitive details. The use of Sparse Merkle Trees (SMTs) and Poseidon hashing enables efficient zero-knowledge proof compatibility. On top of this base structure, several types of commitment trees can be built: 1. **Statement Trees (ST)**: Accommodate basic statements without hierarchy or time constraints. - **Adjustable Statement Trees (ADST)**: Allow commitments if they meet predefined rules, requiring partial knowledge proof. - **Arbitrary Statement Trees (ARST)**: No validation rules; acts as an open platform for any commitment. 1. **Credential Trees (CT)**: Support hierarchical, one-to-many relationships between issuers and credential holders, with optional multisig support. 1. **Time Trees (TT)**: Linked to specific time ranges, allowing no new commitments after a set period. These can be based on ST or CT structures. ## Use cases - **Identity Verification**: Prove identity without requiring disclosure of personal details. - **Reputation Systems**: Enable users to verify their reputation based on past interactions or contributions, preserving privacy. - **Proof of Attendance**: Allow users to validate event participation without exposing location or identity. - **Verifiable Credentials**: Allows users to issue private but provable digital credentials, like certificates. ## Scaling to other chains To maintain accurate, verifiable records, the ZK Registry synchronizes its state root with Ethereum and potentially other chains: - **L2 to L1 Sync**: Ethereum-based smart contracts store the current state root of the registry. As Rarimo posts updates, it also publishes the latest root, ensuring other chains relying on Ethereum for verification have consistent data. - **Cross-Chain Syncing**: For alternative L1s, the state root is propagated from Ethereum via cross-chain messaging. Future implementations may explore alternative solutions to reduce reliance on external messaging systems. --- ## Using ZK Registry Rollup import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Using ZK Registry Rollup >TODO --- ## Bionetta: the ultimate client-side ZKML framework import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; # Bionetta: the ultimate client-side ZKML framework :::info Bionetta is currently in an active development phase, and the implementation is currently private. Contact us on Telegram to get early access to the framework. ::: Bionetta is a client-side framework designed to generate secure and private inference proofs using Zero-Knowledge Machine Learning (ZKML). Unlike other frameworks, Bionetta assumes the AI model and its weights are public, keeping user inputs strictly private. Utilizing Groth16-based ZK-SNARKs, Bionetta efficiently executes neural network inferences, typically completing proofs directly on smartphones in under two minutes. Core principles of Bionetta: - **Provability**: Anyone can independently verify that the model executed correctly. - **Zero-Knowledge**: Verification does not reveal sensitive user data, such as biometric information. - **Client-Side Proving**: Proof generation occurs entirely on the user's device, minimizing data leaks and eliminating reliance on external trusted providers. This approach makes Bionetta ideal for biometrics-based identity proofs. For example, users can scan their faces using mobile devices to demonstrate membership within a specific group, without exposing any raw biometric data. ## Key features and capabilities ### Developer-friendly architecture Bionetta prioritizes simplicity and developer experience, offering: - TensorFlow integration: An SDK built on TensorFlow enables easy training of neural networks compatible with the Bionetta system. It automatically generates R1CS circuits in Circom, simplifying the workflow from model training to ZK proof generation. - Upcoming PyTorch integration: Work is underway to add PyTorch support, further expanding developer flexibility. - Optimized neural network layers: Build your own neural network using the pre-designed ZK-friendly layers, optimized specifically to reduce the model's circuit size, such as ReLU, LeakyReLU, and HardSwish. ### Efficient inference proving Bionetta leverages custom neural network architectures optimized specifically for Rank-1 Constraint Systems (R1CS), significantly reducing the computational constraints required. This tailored approach enables practical, efficient inference verification on-chain, without sacrificing performance or resource efficiency. ### High-performance witness generation Bionetta includes a high-performance Rust-based witness generator, dramatically speeding up witness computation for neural network inference. Even relatively large neural networks can be verified rapidly, enabling feasible real-time biometric verification on consumer devices. ### Optimized floating-point arithmetization Floating-point operations are essential in model inference but challenging to implement efficiently within zero-knowledge circuits. Bionetta employs a specialized quantization scheme that effectively encodes real numbers into finite fields (such as BN254), minimizing accuracy loss during quantization and arithmetic operations. This precise approach is critical for accurate neural network inference. ## Architecture Creating a ZKML model with Bionetta consists of three key steps: - **Step 1: Neural network preparation** - Developer trains or loads a neural network using frameworks such as TensorFlow or PyTorch(coming soon). - Model structure must be compatible with Bionetta-supported layers. - **Step 2: Circuit generation** - Bionetta quantizes neural network weights. - Generates zero-knowledge circuits (in *Circom* or *Noir*(coming soon)). - Produces R1CS specification and witness format. - Builds optimized witness generator and generates proving keys using *Groth16* and *UltraGroth* protocols for efficient zero-knowledge proof construction. - **Step 3: Client-side proof generation** - User generates a witness using the custom witness generator. - Creates zero-knowledge proof. - Proof can be efficiently verified on-chain through autogenerated smart contracts. ## Use cases ### Physical objects as keys With Bionetta, physical objects (e.g., books, figurines, or even cats) can serve as secure keys. Users prove their possession of the object through image proximity proofs, confirming their identity without exposing any detailed information about the object itself. ### Liveness checks Bionetta can securely verify the liveness of users in biometric authentication processes. ZK proofs confirm that users are physically present without revealing raw biometric data, enhancing security against spoofing or replay attacks. ### Face registries Bionetta supports private face registries, allowing users to prove their membership within specific groups or communities anonymously. Participants can demonstrate their identity securely without exposing biometric details. ## Challenges & limitations ### Groth16 proving system Requires a trusted setup and large verification keys; any circuit change (e.g., updating the model) mandates repeating that ceremony. ### Public-weights assumption Optimized only for models whose architecture and weights are public, making it unsuitable for proprietary or private fine-tuned weights. ### Limited model portability Off-the-shelf neural networks, such as MobileNet, can't be adopted directly. Their dense activations would blow up the constraint count and become impractical for consumer hardware. ## Additional resources - Bionetta: Ultimate Client-Side ZKML. Technical Overview - Benchmarking Bionetta ## Conclusion Bionetta represents a significant advancement in the ZKML (zero-knowledge machine learning) field, providing a robust, developer-friendly framework for integrating efficient client-side inference proving into blockchain and privacy-preserving applications. --- ## Creating a ZKML model import OutLink from "@site/src/components/OutLink"; import IdealImage from '@site/src/components/IdealImage'; { // TODO: // - include a link to the MNIST demo } # Creating a ZKML model with Bionetta In this tutorial, you'll learn how to build a simple ZKML model that recognizes handwritten digits. This model enables users to prove they've presented a specific handwritten digit (e.g., "7") without revealing the original image. We'll train the model with TensorFlow using the MNIST dataset and then compile it into a ZKML-compatible format using the Bionetta framework. ## Prerequisites :::info Bionetta is currently in an active development phase, and the implementation is currently private. Contact us on Telegram to get early access to the framework. ::: To get started, clone the bionetta repository that contains the Bionetta framework and the example MNIST model: ```bash git clone git@github.com:rarimo/bionetta.git ``` Then, install the required dependencies: - Node.js - Python 3.11 - TensorFlow 2.12 - Rust 1.84+ - Circom 2.1.7 - Snarkjs Refer to `install.sh` script for the complete list of dependencies and installation instructions. ## Recipe to build your own ZKML model Running Bionetta Framework is as easy as writing an init script in Python with the following steps: 1. Preparing the dataset 2. Building a TensorFlow model with the layers supported by Bionetta 3. Training the model 4. Compiling ZK circuits using Bionetta with just one single extra line: `model.compile_circuits(*)` Then you should run this script and wait till the end of the process. After running the script everything would be ready to build your own ZKML model. ## Step 1. Training the model We will start from the only part that could need the user's efforts: model training and writing an init script mentioned above. Since you are here, we assume that you are familiar with the basics of neural network training. Each neural network training is based on some previously prepared dataset. Because each dataset has unique properties, this step must be performed by the user. For example, we give you a piece of code below, which parses the MNIST dataset: ```python (X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data() X_train = X_train / 255.0 X_test = X_test / 255.0 X_train = np.expand_dims(X_train, axis=-1) # Add channel dimension X_test = np.expand_dims(X_test, axis=-1) # Add channel dimension y_train = tf.keras.utils.to_categorical(y_train, 10) y_test = tf.keras.utils.to_categorical(y_test, 10) ``` After that you should create model by using layers supported by Bionetta Framework. This model then should be put into the init script as Tensorflow model. Here we present you a small example of such neural network: ```python model = tf.keras.models.Sequential( [ tf.keras.layers.Input(shape=(28, 28, 1)), tfb.layers.EDLight2DConv( 7, 16, hidden_units=16, kernel_output_size=3, activation=tfb.layers.ShiftReLU(3), ), tf.keras.layers.Conv2D(32, 3, activation=None, padding="same"), tfb.layers.SEHeavyBlock(6, hidden_units=32, activation=tfb.layers.ShiftReLU(2)), tf.keras.layers.Conv2D(64, 3, strides=2, activation=None, padding="same"), tfb.layers.EDLight2DConv( 2, 128, hidden_units=16, kernel_output_size=3, activation=tfb.layers.ShiftReLU(3), ), tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dense(128, activation=None), tf.keras.layers.Dense(10, activation=None), ] ) ``` After dataset preparation and constructing model architecture you can describe how your training should go. Obviously if you already have trained weights you can put them to the weights directory and skip the training part of SDK. Our repository also contains such example of using Bionetta Framework. ```python hyperparams = tfb.hyperparameters.TrainingHyperparameters({ 'epochs': 1, 'validation_split': 0.9 }) DEBUG_BATCH_SIZE = 4 random_idx = np.random.randint(len(X_train), size=DEBUG_BATCH_SIZE) X_debug = X_train[random_idx] y_debug = y_train[random_idx] callbacks = [ tfb.callbacks.BionettaSubmodelCheckpoint( model, "./examples/bionetta_mnist_test_model/training" ), tfb.callbacks.ExamplePredictionsCallback(X_debug, y_debug), ] model.save_circuit_weights('./bionetta-mnist-temp', compress=False) model.compile(loss='mse', optimizer='adam') model.fit(X_train, y_train, epochs=hyperparams.epochs, validation_data=(X_test, y_test), validation_split=hyperparams.validation_split, callbacks=callbacks) ``` The last part of your actions will be to add code for compiling ZKML: ```python test_input = X_test[np.random.randint(len(X_test))] model.compile_circuits( path='./examples/bionetta_mnist_circuits', test_input=test_input, powers_of_tau_path='./examples/powersOfTau_20.ptau' ) ``` To train the model and execute the Bionetta framework, run this script and wait till the end of the process: ```bash python3 -m examples.bionetta_mnist ``` After running the script, you will see the following output: ## Step 2. Proving and verifying inference After running Bionetta, you will find the following files in the `examples/bionetta_mnist_circuits` directory: - `.zkey` file keeps data received by running trusted setup for your circuit. It holds all the pre computations needed to construct Groth16 proof. In future such file is used to create proof for your witness. - `bionetta-witness-generator` executable and library for C. - `.wtns` file keeps witness for your circuit. It is a file that contains all the information about the input to your neural network and the output of the neural network. - `verification_key.json` file with the verification key for your circuit. - `verifier.sol` Solidity verification contract ### Proving To generate a proof for the inference, you need to run the following command: ```bash snarkjs groth16 prove \ examples/bionetta_mnist_circuits/sequential_circom/sequential.zkey \ examples/bionetta_mnist_circuits/sequential_circom/sequential.wtns \ proof.json public.json ``` This command will use a random image from the test set as input to the neural network. The `proof.json` file will contain the proof, and the `public.json` file will contain the public inputs to the circuit. Also `.wtns` and `.zkey` file are compatible with Rapidsnark, so it is also possible to use it for proof generation. I would personally say that it is even preferable, because Rapidsnark generates proofs much faster than snarkjs command. ### Verification To verify the proof generated in the previous step, run the following command: ```bash snarkjs groth16 verify \ examples/bionetta_mnist_circuits/sequential_circom/verification_key.json \ public.json proof.json ``` If everything was done correctly you will receive the following result: ```bash [INFO] snarkJS: OK! ``` ### Proving and Verifying in Python Alternatively, you can generate proofs and verify them directly from your Python script using Bionetta's built-in methods. To generate a proof for a given input and then verify it: ```python proof_dir = './examples/simple_mnist_circuits/proof' model.prove( input=test_input, target_dir=proof_dir ) model.verify(proof_dir=proof_dir) ``` This will create the proof files in the specified `proof_dir` and then attempt to verify them. ### Conclusion In this tutorial, we have shown you how to create a simple ZKML model using the Bionetta framework. We covered the steps of preparing the dataset, building and training the model, compiling ZK circuits, and generating and verifying proofs. With Bionetta, you can easily create privacy-preserving models that leverage the power of zero-knowledge proofs.