In some applications, it is crucial to ensure that a third party has performed a computation correctly and to make use of the result of that computation. To achieve this, the third party must first interact with Aligned and obtain the AlignedVerificationData, a receipt indicating that the proof of the computation was verified correctly. The application should then receive both the AlignedVerificationData and the result of the computation. After confirming that the proof was verified by Aligned, it must check that the posted result matches the one committed in the AlignedVerificationData.
This guide demonstrates how to validate a Risc0 proof using the Aligned SDK. The Risc0 program in this example is a Fibonacci sequence calculator. It generates a public input that corresponds to the last two Fibonacci numbers of the sequence, taken modulo 7919. Our goal is to validate, within a smart contract, that the public input commitments match these two numbers.
In this case, the Fibonacci number to be calculated is 500 and the last two numbers of the sequence modulo 7919 are 1268 and 1926.
This guide assumes you are in the examples/validating-public-input directory.
Generate your ZK Proof
[!IMPORTANT] To generate the proof ensure you have docker installed and the docker daemon running. This is necessary to ensure deterministic builds of the binary we want to generate a proof of. If not used, builds may differ depending on the system you are running on. To know more about this, check this link from RiscZero docs.
To submit proofs to Aligned and get them verified, first you need to generate those proofs. Every proving system has its own way of generating proofs.
Program Identifier Validation: The contract first validates if the provided commitment of the program identifier matches the expected one.
require( fibonacciProgramIdCommitment == programIdCommitment,"Program ID doesn't match");
Public Input Validation: The contract then checks that the commitment of the public inputs matches the keccak 256 hash of the actual public inputs.
require( pubInputCommitment ==keccak256(abi.encodePacked(pubInputBytes)),"Fibonacci numbers don't match with public input");
Static Call to AlignedServiceManager: The contract makes a static call to the AlignedServiceManager contract to check if the proof was verified in Aligned. It then extracts the last two Fibonacci numbers from the pubInputBytes and emits an event.
Bytes to two uint32 conversion: A helper function to convert a byte array into two uint32 numbers, used for extracting the last two Fibonacci numbers from the pubInputBytes.
functionbytesToTwoUint32(bytesmemory data) publicpurereturns (uint32,uint32) {require(data.length >=8,"Input bytes must be at least 8 bytes long");uint32 first =uint32(uint8(data[0])) | (uint32(uint8(data[1])) <<8) | (uint32(uint8(data[2])) <<16) | (uint32(uint8(data[3])) <<24);uint32 second =uint32(uint8(data[4])) | (uint32(uint8(data[5])) <<8) | (uint32(uint8(data[6])) <<16) | (uint32(uint8(data[7])) <<24);return (first, second);}
To deploy the contract, first you will need to set up the .env file in the contracts folder with the following variables:
RPC_URL=<rpc_url> #You can use publicnode RPC: https://ethereum-holesky-rpc.publicnode.com
PRIVATE_KEY=<private_key>
ALIGNED_SERVICE_MANAGER_ADDRESS=<service_manager_address> #0x58F280BeBE9B34c9939C3C39e0890C81f163B623 for Holesky
PAYMENT_SERVICE_ADDRESS=<payment_service_address> #0x815aeCA64a974297942D2Bbf034ABEe22a38A003 for Holesky
Then, run make deploy_fibonacci_validator.
To call the function in the contract with cast run:
The proof submission and verification can be done either with the SDK or by using the Aligned CLI.
To submit the proof generated in this example, run make submit_fibonacci_proof. This will output the AlignedVerificationData needed to send to the verifyBatchInclusion method of the contract in the batch_inclusion_data directory inside aligned-integration.