Notes on Risc-V ZKVMs with Uma Roy

A Rust program is compiled to ELF, using traditional compiler (as ELF is not only used for ZKVM, but also for embedded system as well, this step is pretty much standard)

instruction explanation:

ADD, value1 stored in address xD, added this value by 5 and store the value in x29

Understanding RISC-V Emulator Execution Process

This post explains how a RISC-V emulator or runtime executes a program, specifically one in the ELF (Executable and Linkable Format) file format, which contains machine instructions.

1. Execution Process

When the ELF file is ready to run, the emulator or runtime starts executing each instruction in sequence.

  • The program counter (PC) is used to track the next instruction to execute. The PC is simply a numeric value that points to the current instruction in the ELF file.

2. Program Counter (PC)

The PC initializes at a start address, guiding the runtime on where to begin in the ELF’s instruction list.

  • Each instruction is fetched based on the PC, executed, and then the PC is updated to the next instruction.

3. Instruction Fetch and Execute Loop

The runtime follows a loop where it fetches the instruction indicated by the PC, executes it, and then moves to the next one.

  • This loop continues until a halt condition or exit code signals the program’s end.

4. Instruction Flow

The instruction flow depends on the type of instruction being executed:

  • For simple instructions like ADD, the PC increments to the next instruction sequentially.
  • For branching instructions like JAL (Jump and Link), commonly used in loops or function calls, the PC changes to a specific address instead of incrementing. This allows the program to “jump” to a different part of the code, supporting control flow structures like loops, conditionals, and function calls.

5. Program Termination

The execution stops when a halt instruction is encountered, or the program signals termination with an exit code.

In summary, the emulator uses a program counter to navigate and execute instructions in a loop, updating the counter based on the type of instruction (sequential or branching) until the program completes.

Lookup

Succint prove RISCV extension where there is multiplication operation.

every operation can have its own lookup table (receiver), like addition, multiplication. If not using lookup, an operation like addition will needs more witness column to do some intermediate computation, and these extra columns are too expensive to pay just for addition operation alone, they won’t be used by other operation.

Using lookup table, each operation is a STARK table, and itself has an STARK proof alone, then there will be many tables for different operations, use recursive to aggregate the proofs.

 

 

 

Memory

Older method:Merkleized memory

each address is a leave for the merkle tree, commit to the root of the merkle tree. every time access the memory, one needs to have a proof for a open of merkel tree.

Newer method: offline memory checking

when a user access a particular address, a new record is added on the lookup argument with multiplicity 1, and a previous record is subtracted

+ (addr, value, timestamp) - (addr, prev_value, pre_timestamp)

check accm_mem=0

question: how to cancel the last one?

Pre-compile

Downside of zkvm compare to hand written specialised circuit, is the overhead of vm, memory reading loading …

one way of improvement is:

pre-compiles/accelerator/co-processor —> a specialised circuit that more efficient standardised circuit.

Ecall: “keccak” pass a pointer to the array region where we want to do keccak

keccak table:

so instead of using 10k cycles to do keccak narrow table in cpu table, using keccak table with wide columns like 3000, no need to deal with memory.

you don’t need to emulated the computation of cpu, but just ask cpu to witness the computation.

in the frontend, user needs to use sp1 crate for keccak.

Chunks/sherds

for computation that is very long, the execution trace is divided to many smaller chunks/sherds, and these chunks can be computed in parallel, then use STARK recursion to generate a single proof.

recursion proof is difficult, using zkVM to write a STARK verifier is too much overhead, so a specialised recursion zkVM is developed to do this recursion, it has its own instruction set (some other teams develop circuit)

the last stark proof is wrapped to a SNARK, to be verified with ethereum

groth16 or plonkish kzg

Leave a Reply

Your email address will not be published. Required fields are marked *