Q: What is a zero-knowledge proof?
A: A zero-knowledge proof (or ZKP) is a protocol allowing "one party (the prover) [to] prove to another party (the verifier) that a given statement is true [without] conveying any additional information". In the specific case of the RISC Zero ZKP system, the prover can run an agreed-upon function F, passing it secret input and generating both a public output and a 'receipt' of F’s correct execution. The prover can send this receipt to the verifier, who can then check it, and presuming it checks correctly, the verifier can be very sure that prover ran the function correctly and that it produced a specific output. See [our explainer on the RISC Zero ZKP system](/docs/explainers/proof-system/proof-system-sequence-diagram) for more details.
Q: How did you make your RISC-V circuit?
A: The RISC-V circuit is found in step.cpp.inc and is generated by the make-circuit program. It consists of:
- Code to emulate RISC-V, including decoding RISC-V instructions and constructing the execution trace.
- Code to evaluate the constraint polynomials that check the execution trace.
- Auxiliary data to support structures such as ‘taps’.
Because the data structures supporting all three of these need to match very carefully, we created a ‘circuit compiler’ program that generates code for all three of these systems.
Q: What exactly is the image ID?
A: The image ID uses hashing to relate the receipt to the code that produced the receipt. Specifically, the image ID is the SHA-2 hash of the image of the initial zkVM memory state.
Q: How can we use the image ID to determine if program code is altered before execution?
A: The image ID can be determined from the compiled ELF source code. Someone wishing to confirm that a receipt corresponds to Rust source code can compile that code targeting the RISC Zero zkVM and verify that the image ID resulting from this compilation matches the image ID in the receipt.
Code Project Help
Q: What do I do with the proof receipt once I’ve created it?
A: The receipt can be serialized and sent over the network to the verifier. The verifier does not need to have access to the host code, but they do need the image ID of the expected program. The image ID is a required parameter for the receipt.verify() function and is used to confirm that the expected code was executed.
In our code examples, the proof receipt is generated and verified within the same program, but the most common use case is one in which the verification happens on another system.
Q: What types of programs does the zkVM support in Rust?
Guest / Host Interactions
Q: If the guest zkVM lives on the host machine, can’t the host still tamper with the compiled code?
A: Like other zk-STARKs, RISC Zero’s implementation makes it cryptographically infeasable to generate an invalid receipt:
- If the binary is modified, then the receipt’s seal will not match the image ID of the expected binary.
- If the execution is modified, then the execution trace will be invalid.
- If the output is modified, then the journal’s hash will not match the hash recorded in the receipt.
Q: When can information be shared with the guest zkVM? How do you prevent buffer overflows?
A: Data can be sent during program execution from the host to the guest via a memory map. The host-writeable memory is write-once, meaning that adjacent memory regions cannot be overwritten and executed.
Q: How do I know which computations should be performed in the guest zkVM, and which can be offloaded to the host?
A: If you don't need to perform a computation securely, if others don't rely on it, and if it doesn't produce outputs that others rely on, it can probably be performed outside of the zkVM.
However, consider that code run in the RISC Zero zkVM can be shown to behave as expected even on a host that is entirely untrusted. To get the most value out of this guarantee, we recommend dividing the computational labor with an untrusted host in mind. That is, other parties should not need to trust the host's output or operations in order to benefit from the work done in the zkVM.
Data Processing & Performance
Q: If I want the guest to process large volumes of data during execution, I might be constrained by space limitations. What are my options?
A: If data is loaded from the host to restrict guest program size, the most significant limitation on zkVM data processing is a constraint on instruction cycles. Loading data into the guest costs instruction cycles, as does data processing.
There are workarounds for data limitations if the data is only included to ensure that its integrity becomes part of the proof of computation. If the data can be processed externally and simply needs to be verifiably unchanged, consider processing data externally and sending the guest a Merkle proof or (if no processing is needed) generating a SHA of a large dataset.
In the future, we plan to lift these processing limitations using continuations and recursion.
Q: What is the current recommended size of a program running on the zkVM?
A: Although the theoretical maximum size is 128 MB, we recommend that programs be kept no larger than ~1 MB to run on RISC Zero’s current zkVM implementation.
Q: I’d like to speed up the processing done inside the zkVM. What are my options?
A: For cryptographic operations, it is possible to build ‘accelerator’ circuits such as our implementation of SHA26. Fast cryptography is sufficient to support many ‘DeFi’ applications. For many other applications, it is possible to perform most computation on the host (outside the zkVM) and then verify the results in the zkVM.