ZKInterface ZKP Verification
ZKInterface is a precompile inside the Horizen chain that allows zero-knowledge proof verification using different proof types.
The method could be invoked in the EVM invoking the following method:
function verifyProof(ProofType proofType, bytes calldata vk, bytes calldata proof, bytes calldata pubs) pure external returns (bool, uint8);
Where proofType assumes one of the following values:
- GROTH16
- ULTRAPLONK
- RISC0
- FFLONK
Since in Solidity the enum values are also numeric 8-bit (uint8) values, the method also follows the following interface:
function verifyProof(uint8 proofType, bytes calldata vk, bytes calldata proof, bytes calldata pubs) pure external returns (bool, uint8);
In this case, the proofType assumes one of the following values:
- 0 (for Groth16 proof type)
- 1 (for Ultraplonk proof type)
- 2 (for Risc0 proof type)
- 3 for (for Fflonk proof type)
Return type
The method returns a tuple (bool, uint8)
, composed by the following values:
- Boolean value: true if the verification was successful, false otherwise.
- Numeric value: result code or error code according to the following table:
Code | Result |
---|---|
0 | Verification successful |
1 | Inputs are correct data, but the proof is not verified |
2 | pubs (public signals) arguments is invalid |
3 | vk (verification key) argument is invalid |
4 | proof argument is invalid |
Encoding parameters
All the parameters should be provided as a bytes object, that represent the encoding of the corresponding object in Rust according to the SCALE enconding. For more information, check official SCALE codec documentation and parity_scale_codec Rust pallet documentation
An exception is provided for the Groth16 proof type, since it's the only one (between the supported ones) which publicInput value has the Vec<Vec<u8>>
type (array of an array). In this case, the encoding is the following:
-
Inner arrays are encoded according to the SCALE encoding standard.
-
Outer array is obtained chaining the inner arrays' encoded values.
You can find some examples of valid encoded inputs for each algorithm inside the test
subfolder in the precompile
source code folder.
Proof verifications
The precompile executes the proof verification in a synchronous way with a pure (stateless read-only) method on the Solidity interface. This verification is executed in the following way:
- The input arguments are decoded from the bytes format to the Rust object
- The decoded inputs are passed to the
zkverify
verifier for the given proof type, invoking the corresponding method imported as a Rust library - According to the result of the
zkverify
code invocation, the corresponding Result Code is returned - The Result Code is then converted into the correct Solidity output (see Return type section of this document)
For more information about the verification code, consult the zkVerify sources:
Proof type | Docs | Github |
---|---|---|
Groth16 | Link | Link |
Ultraplonk | Link | Link |
Risc0 | Link | Link |
Fflonk | Link | Link |
Source Code
The precompile
source code folder contains the following files:
lib.rs
: access point for the precompile, it receives the arguments and passes them to thehandlers.rs
verification method, that converts the response into the Solidity's interface requested one.handlers.rs
: contains the implementation for all the verification proof typesutils.rs
: contains the utility as theResultCode
enum and the error conversion methods.tests.rs
: contains the positive and negative unit teststest
folder: contains some examples of hex-encoded inputs for the different proof types, used for unit testsZKInterface.sol
: Solidity interface for the verification precompile.