Validating public input
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 interact with Aligned, using the Aligned SDK, to 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 Risc0 and SP1 proofs using the Aligned SDK. The program in this example is a Fibonacci sequence calculator. It generates a public input that corresponds to the number of fibonacci being calculated and 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 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.
Requirements
The program
The Fibonacci program to be proven is essentially a Rust program with a few additional functions from the risc0
and sp1
libraries. These extra functions allow for the submission of public inputs and enable the generation of a proof.
Risc0
For risc0
, the Fibonacci program can be found in examples/validating-public-input/risc_zero/fibonacci_proof_generator/methods/guest/src/main.rs
, and it's known as the guest code. This code compiles into a binary file that is later used to correctly generate the proof. The host code, located in examples/validating-public-input/risc_zero/fibonacci_proof_generator/host/src/main.rs
, is responsible for executing the program with the given input and generating a receipt that contains both the proof and all output data from the process.
For more details about risc0
and the interaction between guest and host code, as well as how the various parts work, you can refer to the official documentation here.
SP1
For SP1
, the Fibonacci program is located in examples/validating-public-input/sp1/fibonacci/program/src/main.rs
, and it functions similarly to the one written for risc0
. It follows a similar structure and process to generate a proof. The code responsible for executing and proving the program can be found in examples/validating-public-input/sp1/fibonacci/script/src/main.rs
. Both components work in tandem, much like in the risc0
framework.
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 or this from SP1.
To submit proofs to Aligned and get them verified, you first need to generate those proofs. Every proving system has its own method for generating proofs.
Examples on how to generate proofs can be found in the generating proofs guide.
To generate the proof required for this example, run the following commands:
For Risc0:
make generate_risc0_fibonacci_proof
For SP1:
make generate_sp1_fibonacci_proof
Once completed, you will see output that includes the program ID, the public inputs (which are the initial number of steps in the sequence and the last two Fibonacci numbers of the sequence), and the verification result, like so:
The command generates three different files, which will be used for later validation:
An
.elf
file containing the compiled program.A
.proof
file containing the proof bytes for the program.A
.pub
file containing the serialized public input bytes committed by the program.
Submit and verify the proof to Aligned
For more details on submitting proofs and setting up a local wallet keystore, refer to the submitting proofs guide.
The proof submission and verification process can be done either using the SDK or the Aligned CLI. In this case, we’ll use the Aligned SDK to better illustrate how the entire process works.
To submit the Risc0 proof generated in this example, run:
Alternatively, you can submit the one generated with SP1 by running:
This command will execute the Rust code that handles the proof submission with the appropriate verifier. You can find this code in the file examples/validating-public-input/aligned-integration/src/main.rs
. It acts as the integration layer between the proof-generating program and the proof submission process to Aligned.
The data necessary to send to aligned follows this structure:
It could take some time but once this proof is submitted and executed within Aligned, you should see an output similar to:
The file logged in <JSON_FILE_NAME>
will contain the AlignedVerificationData
. This data is essential for sending the transaction to the verifyBatchInclusion
method of the smart contract, which verifies the inclusion of your proof in Aligned and checks the correctness of the compiled program and public inputs.
Each generated proof gets its own file name, so ensure to save the filename or remember it for future steps, as it will be required later. You can check the generated data files in aligned-layer/examples/validating-public-input/aligned-integration/batch_inclusion_data
Validating the public inputs
To check if a proof was verified in Aligned, you need to make a call to the AlignedServiceManager
contract from within your smart contract.
We previously reviewed the structure of a Verifier contract when building our first application; you can find that information here if you'd like to revisit it. Now, we need to implement a check to ensure that the public inputs match the expected values. To accomplish this, we have added a new parameter to our verifyBatchInclusion
function in the smart contract, which will receive the bytes of the public inputs directly from the .pub
file generated during compilation.
Now the function should look like this for both sp1 and risc0 proofs.
Since the format of the generated byts is the same for both of the verifiers, we can later decode the inputs if we want to do something with them, in this case we emit an event:
We have already implemented the contract with these features; you can check it in contracts/src/FibonacciValidator.sol
.
To test it, you'll need to deploy the contract. First, create a new .env
file following the format of .env.example
, ensuring to add the private_key
you wish to use for deployment. Make sure you have a sufficient balance on the Holesky testnet. For all other values, you can use the default settings provided in the comments.
Once your .env
file is set up, you can deploy the contract using the following command:
This command will log the address of the deployed contract like so:
Make sure to save this address, as you'll need it for the next step.
Now, to call our verifier contract and check the inclusion of the proof along with the validation of the public inputs, use the following command based on the verifier you used:
For Risc0:
For SP1:
In these commands:
<FIBONACCI_VALIDATOR_ADDRESS>
is the address of the validator you deployed in the previous step.<DATA_FILE_NAME>
is the name of the file where the aligned data for this proof was saved (including the.json
extension)
When you run this command, it will gather all necessary information from the file containing the aligned data and send a transaction to the Fibonacci validator using the cast send
tool, like so:
If the output of this transaction indicates success
, then we can confirm that our proof has been successfully included in Aligned. Additionally, this outcome verifies that the public inputs we generated match the expected values from the proof generation process.
Last updated