Write zkVM guest programs in C to integrate legacy codebases or achieve low-level control over execution.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt
Use this file to discover all available pages before exploring further.
What You’ll Learn
- Writing guest programs in C
- Using the C platform API
- Compiling C code for RISC-V zkVM
- Reading inputs and committing outputs in C
This example is experimental and hasn’t been extensively tested. Use Rust for production applications when possible.
Overview
While most zkVM programs are written in Rust, you can also use C for:- Legacy Integration: Port existing C codebases
- Low-Level Control: Direct memory management
- Performance: Hand-optimized C code
- Team Expertise: Leverage C programming skills
How It Works
#include "platform.h"
#include <assert.h>
#include <stdint.h>
union u32_cast {
uint32_t value;
uint8_t buffer[4];
};
int main() {
// Initialize SHA-256 hasher for commits
sha256_state* hasher = init_sha256();
// Read two u32 values from host (little-endian)
union u32_cast a;
union u32_cast b;
assert(env_read(a.buffer, 4) == 4);
assert(env_read(b.buffer, 4) == 4);
// Multiply
a.value *= b.value;
// Commit result to journal
env_commit(hasher, a.buffer, sizeof(a.buffer));
// Exit successfully
env_exit(hasher, 0);
return 0;
}
#ifndef PLATFORM_H
#define PLATFORM_H
#include <stddef.h>
#include <stdint.h>
// SHA-256 state for journal commits
typedef struct sha256_state sha256_state;
// Initialize SHA-256 hasher
sha256_state* init_sha256(void);
// Read from host
size_t env_read(uint8_t* buf, size_t len);
// Commit to journal
void env_commit(sha256_state* hasher, const uint8_t* data, size_t len);
// Exit the guest program
void env_exit(sha256_state* hasher, int exit_code);
// Logging
void env_log(const char* msg);
#endif
# Build the C guest program
build:
@echo "Building C guest program..."
cargo build --release
# Execute in zkVM (fast, no proof)
execute: build
@echo "Executing in zkVM..."
cargo run --release --bin execute
# Generate proof (slow)
prove: build
@echo "Generating proof..."
cargo run --release --bin prove
# Run tests
test:
cargo test --release
clean:
cargo clean
.PHONY: build execute prove test clean
Running the Example
What Gets Proven?
The receipt proves:- Correct Execution: The C program ran successfully
- Computation Result: The product is in the journal
- Input Processing: Two numbers were read and multiplied
- Deterministic Output: Result matches the committed value
C Platform API Reference
Reading Inputs
Read data from the host:buf: Buffer to read intolen: Number of bytes to read
Committing Outputs
Commit data to the journal:hasher: SHA-256 state frominit_sha256()data: Data to commitlen: Length of data
Exiting
Exit the guest program:hasher: SHA-256 state frominit_sha256()exit_code: Exit code (0 for success)
Logging
Log messages during execution:Logs are only visible during execution, not in the proof.
Memory Management
The zkVM provides a standard memory layout:Dynamic Allocation
Use standard C allocation:Stack Usage
Be mindful of stack size:Porting C Code
Compatible Features
Works in zkVM:- Standard library (
stdio.h,stdlib.h,string.h, etc.) - Math functions (
math.h) - Integer operations
- Pointer arithmetic
- Dynamic allocation
- Function calls
- Recursion
- System calls (
open,socket, etc.) - Threading (
pthread) - True randomness (
rand()needs seeding) - File I/O (use
env_readinstead) - Time functions (
time(),clock())
Porting Example
Original code:Use Cases
Legacy Cryptography
Port existing crypto implementations:Signal Processing
Port DSP algorithms:Embedded Systems
Verify embedded algorithms:Performance Considerations
Cycle Counts
C operations have predictable cycle costs:| Operation | Cycles (approx) |
|---|---|
| Addition | 1 |
| Multiplication | 1 |
| Division | 30-40 |
| Function call | 2-3 |
| Memory load | 1 |
| Memory store | 1 |
Optimization Tips
Use integer math:Debugging
Printf Debugging
Useenv_log for debugging:
Assertions
Use assertions liberally:Comparison: C vs Rust
| Feature | C | Rust |
|---|---|---|
| Memory safety | Manual | Automatic |
| Learning curve | Moderate | Steep |
| Legacy code | Easy to port | Needs rewrite |
| Performance | Excellent | Excellent |
| Safety guarantees | None | Strong |
| Community support | Mature | Growing |
| Recommended for | Legacy ports | New projects |
Use Rust for new projects. Use C only when porting existing code or when team expertise requires it.
Limitations
Current limitations:- Experimental: Not as tested as Rust guests
- No stdlib: Limited standard library support
- Manual memory: No automatic safety
- Build complexity: Requires custom toolchain
- Debugging: Harder to debug than Rust
Next Steps
- Try writing a Rust guest for comparison
- Port existing C libraries
- Explore Rust examples
- Learn about zkVM architecture