Zk Proof: Part 2- Verifier, Validator, and Hooks

 

blog

Introduction


A zero-knowledge proof (ZKP) verifier is a mechanism that allows one party (the prover) to prove to another party (the verifier) that they possess certain information without revealing the actual data itself. 


The verifier checks that the proof provided by the prover is valid, meaning that it meets specific predefined criteria and that the prover knows the information without revealing it.


The proof provided by the prover is a mathematical object that depends on the information and the predefined criteria, usually created using a specific algorithm called zero-knowledge proof construction. The ZKP verifier can be in the form of a smart contract, software, or hardware device.


It can be used in various scenarios, such as in privacy-sensitive applications, where the verifier needs to confirm that a user has access to specific data without actually seeing the data, or in financial systems where a user wants to prove that they have the assets they claim to have without revealing the assets themselves.


Related: Zk-Proofs: Part 1 - Claim Issuer


What is the main difference between verifiers and validators?


In a zero-knowledge proof (ZKP) system, the validator, and the verifier play slightly different roles.


A validator is responsible for ensuring that the proof provided by the prover is valid, meaning that it conforms to specific predefined criteria. The validator is a set of rules or algorithms that checks the proof against these criteria. The validator can be a smart contract, a software library, or a hardware device.


On the other hand, a verifier is a party that receives the proof from the prover and checks that it is valid. The verifier does this by sending the evidence to the validator, preventing the proof against the predefined criteria, and returning a result to the verifier. The verifier can be a smart contract, a software application, or a person.


In summary, a validator is a mechanism that checks that a proof is valid according to specific predefined criteria. In contrast, a verifier is a party that receives and verifies the proof by sending it to the validator.


Zero Knowledge Airdrop


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import "./lib/GenesisUtils.sol";

import "./interfaces/ICircuitValidator.sol";

import "./verifiers/ZKPVerifier.sol";

contract ERC20Verifier is ERC20, ZKPVerifier {

uint64 public constant TRANSFER_REQUEST_ID = 1;


Mapping to store the addresses of that already claimed the contract.


mapping(uint256 => address) public idToAddress;

mapping(address => uint256) public addressToId;

uint256 public TOKEN_AMOUNT_FOR_AIRDROP_PER_ID =

5 * 10**uint256(decimals());

constructor(string memory name_, string memory symbol_)

ERC20(name_, symbol_)

{}

...

}


This Solidity smart contract implements an ERC20 token and a zero-knowledge proof (ZKP) verifier. The agreement's purpose is to allow users to claim an airdrop of the token by providing a valid ZKP, which is verified by the contract.


The contract includes several hooks that run specific checks before and after the ZKP is submitted. The statements involve verifying that the address submitted the proof is the same as the address calling the "Proof Submit" function, ensuring that each address can only claim the airdrop once, and verifying that the ZKP record is stored in the contract. The contract also restricts token transfers to addresses that have successfully claimed the airdrop.


Libraries


ZKPVerifier likely provides functions and variables necessary for verifying zero-knowledge proofs, such as storing a record of which users have submitted proofs and checking the validity of proofs before they are processed.


We will discuss its functionalities in the next part of the blog.


The ICircuitValidator interface is imported into the code to define the expected behavior and functions of the circuit validator. In the code, the circuit validator is used to verify zero-knowledge proofs submitted by users.


Using an interface, the code can be abstracted from the specific implementation of the circuit validator, allowing for different implementations to be used in other contract deployments. The performance of the circuit validator must meet the requirements defined in the ICircuitValidator interface to be compatible with the contract.


Hooks


function _beforeProofSubmit(

uint64, /* requestId */

uint256[] memory inputs,

ICircuitValidator validator

) internal view override {

// check that the challenge input of the proof is equal to the msg.sender

address addr = GenesisUtils.int256ToAddress(

inputs[validator.getChallengeInputIndex()]

);

require(

_msgSender() == addr,

"address in proof is not a sender address"

);

}

_beforeProofSubmit is a hook in the code that runs a check before the "Proof Submit" function is executed. It verifies that the address that submitted the proof on the validator is the same as the address calling the "Proof Submit" function on the contract. If the check fails, the process will stop execution and return an error message. This hook aims to ensure the integrity and security of the contract by ensuring that the address submitting the proof is indeed the address calling the function.


function _afterProofSubmit(

uint64 requestId,

uint256[] memory inputs,

ICircuitValidator validator

) internal override {

require(

requestId == TRANSFER_REQUEST_ID && addressToId[_msgSender()] == 0,

"proof can not be submitted more than once"

);

uint256 id = inputs[validator.getChallengeInputIndex()];

// execute the airdrop

if (idToAddress[id] == address(0)) {

super._mint(_msgSender(), TOKEN_AMOUNT_FOR_AIRDROP_PER_ID);

addressToId[_msgSender()] = id;

idToAddress[id] = _msgSender();

}

}


The afterProofSubmit hook in the code is executed after a zero-knowledge proof has been submitted to the ERC20Verifier contract. The hook performs checks and operations to determine if the submitted proof should be accepted and processed.


The afterProofSubmit hook checks that:

  • The requestId of the submitted proof is equal to TRANSFER_REQUEST_ID

  • The address that submitted the proof has not claimed the airdrop previously (by checking if the address is mapped to a unique ID in the addressToId mapping)

  • If these checks pass, the hook performs the actual execution of the airdrop by calling the _mint function (inherited from the ERC20 contract) to transfer the TOKEN_AMOUNT_FOR_AIRDROP_PER_ID to the address that submitted the proof. Additionally, the hook updates the addressToId and idToAddress mappings to store the mapping between the address and the unique ID provided in the proof.

    Comments

    Popular posts from this blog

    Building a Cryptocurrency Exchange Platform: Key Considerations & Best Practices

    Blockchain Security: Safeguarding the Decentralized Future

    Build Dynamic Websites With Jamstack Web Development