Lita Docs
  • Introduction
    • Getting Started
  • Quick Start
    • Tutorial: Rust via Docker
    • Installation & System Requirements
    • Valida Compiler Toolchain
      • Rust Usage
      • C Usage
      • WASM Usage
      • Rust API
      • Client-side API
    • Valida zk-VM
  • ADVANCED USAGE
    • zk-VM: Advanced Usage
    • Using the Rust Toolchain
    • Using LLVM libc
  • Architecture
    • Overview
    • Proving System: Plonky3
      • Future Directions
    • Valida zk-VM
      • Technical Design: VM
      • Technical Design: Prover
      • GitHub Link
    • LLVM-Valida Compiler
      • Technical Design
      • GitHub Link
    • Benchmarks
      • Fibonacci (vs. RISC Zero)
      • Fibonacci (vs. SP1)
      • Fibonacci (vs. Jolt)
      • Fibonacci (Rust vs. C)
      • SHA-256 (vs. RISC Zero)
      • SHA-256 (vs. SP1)
      • SHA-256 (vs. Jolt)
  • Core Concepts
    • zk-VM
    • Proofs: Classical, Probabilistic, Succinct, and ZK
    • Evaluating zk-VMs
    • ZK-VM Design Tradeoffs
    • Valida Design Considerations
  • Contributing
    • Overview
    • Early Access Program
Powered by GitBook
On this page
  • Installation and cloning the project template
  • Prerequisites
  • Installation
  • Cloning the template project
  • Validating the installation
  • Looking at the template project
  • Application logic
  • Renaming the project
  • Building, running, proving, and verifying
  1. Quick Start

Tutorial: Rust via Docker

PreviousGetting StartedNextInstallation & System Requirements

Last updated 2 months ago

This is a tutorial on setting up and using the Dockerized Valida zk-VM stack to write a Rust program, compile it to Valida, and provide its execution. The and pages provide the same information about the stack, in a more concise and complete reference-style format. You can safely skip this tutorial if you prefer that style of presentation, since there is no information about the stack which is contained in this tutorial and nowhere else.

Installation and cloning the project template

This section covers the following steps:

  1. Install the toolchain via Docker.

  2. Clone the Rust project template.

  3. Validate that the installation is working correctly.

You can see all of the steps in this section performed in this video demo:

Prerequisites

First make sure that your system meets the requirements for following this tutorial. You will need a system which can run Docker containers. Your user will need to be part of the docker group. You will need at least 10 GB of free space on your filesystem.

Installation

To install the toolchain as a Docker image, run:

docker pull ghcr.io/lita-xyz/llvm-valida-releases/valida-build-container:v0.9.0-alpha

Cloning the template project

To clone the Rust project template, run:

git clone https://github.com/lita-xyz/fibonacci.git

Validating the installation

Follow the steps in this section to ensure that the toolchain is working correctly on your machine. After performing the above steps, cd into the template project:

cd fibonacci

Next, enter the Docker container.

For x86_64 systems:

docker run --platform linux/amd64 --entrypoint=/bin/bash -it --rm -v $(realpath .):/src ghcr.io/lita-xyz/llvm-valida-releases/valida-build-container:v0.9.0-alpha

For ARM64 systems:

docker run --platform linux/arm64 --entrypoint=/bin/bash -it --rm -v $(realpath .):/src ghcr.io/lita-xyz/llvm-valida-releases/valida-build-container:v0.9.0-alpha

From within the Docker container, build the template project:

cargo +valida build

Next, run the template project:

valida run target/valida-unknown-baremetal-gnu/debug/fibonacci log

You should see a prompt that looks like this:

Please enter a number from 0 to 46:

Enter a number between 0 and 46, and press enter. At this point, the program should print the answer and exit. For example:

valida> valida run target/valida-unknown-baremetal-gnu/debug/fibonacci log
Please enter a number from 0 to 46:
5
5
-th fibonacci number is:
5

At this point, you have validated that the toolchain is working on your system. You have also demonstrated how to compile a Rust program to a Valida executable, and run it on the zk-VM.

Looking at the template project

In the previous steps, you cloned the fibonacci template project. This contains a single Rust source file, src/main.rs. This is roughly what it should look like if you open it up in a text editor:

pub fn main() {
    println!("Please enter a number from 0 to 46:");
    // Read a line from stdin and parse it as an u8.
    let n = loop {
        match valida_rs::io::read_line::<u8>() {
            Ok(num) => break num,
            Err(e) => {
                println!(&format!("Error reading input: {}. Please try again:", e));
            }
        }
    };
    // n that is larger than 46 will overflow the u32 type.
    if n > 46 {
        println!("Error: n is too large. Please enter a number no more than 46.");
        return;
    }
    let mut a: u32 = 0;
    let mut b: u32 = 1;
    let mut sum: u32;
    for _ in 1..n {
        sum = a + b;
        a = b;
        b = sum;
    }

    println!(&n.to_string());
    println!("-th fibonacci number is:");
    println!(&b.to_string());
}

This program computes an element of the Fibonacci sequence based on user input.

Let's delete the application-specific logic from this file, in order to come up with a generic starting point for the project:

pub fn main() {
    // Your application code goes here
}

Application logic

Let's create a program which proves knowledge of the prime factorization of a 32-bit unsigned integer.

This tutorial demonstrates that multi-file Rust projects are supported by the Valida toolchain. We create a new file, call it src/lib.rs, which we fill with the following contents:

pub fn read_number() -> u32 {
    loop {
        match valida_rs::io::read_line::<u32>() {
            Ok(num) => break num,
            Err(e) => {
                println!(&format!("Error reading input: {}. Please try again:", e));
            }
        }
    }
}

pub fn check_prime_factorization(x: u32, ys: &[u32]) -> bool {
    let mut z = 1;
    for y in ys {
        if !is_prime(*y) {
            return false;
        }
        z *= *y;
    }
    z == x
}

pub fn is_prime(x: u32) -> bool {
    for y in 2..x {
        if x % y == 0 {
            return false;
        }
    }
    true
}

This module defines three functions: read_number, check_prime_factorization, and is_prime. Here is what each of these functions does:

  • The read_number function attempts to read an unsigned 32-bit integer from the input tape. If it fails to do so, it outputs an error message and again attempts to read an unsigned integer, continuing until it succeeds.

  • The check_prime_factorization function checks that ys is a prime factorization of x, outputting true if this is the case and false otherwise.

  • The is_prime function outputs true if x is a prime number, and false otherwise.

Next, we'll edit the src/main.rs file so that it looks like this:

use prime_factorization::{read_number, check_prime_factorization};

pub fn main() {
    println!("Please enter a 32-bit number:");
    // Read a line from stdin and parse it as a u32.
    let x = read_number();

    println!("Please enter the number of prime factors (with multiplicity):");
    let n = read_number();

    let mut ys = vec![];
    for _i in 0..n {
        println!("Please enter the next prime factor:");
        ys.push(read_number());
    }

    if check_prime_factorization(x, ys.as_ref()) {
        println!("Verified prime factorization of:");
        println!(&x.to_string());
    } else {
        println!("Failed to verify prime factorization");
    }
}

This modifies the template src/main.rs file by adding imports from src/lib.rs and populating the main function. The new main function reads a 32-bit number x, followed by its number of prime factors, and then each of its prime factors. It then checks whether the claimed prime factors are in fact the prime factors of x, providing output which indicates whether or not this is the case.

Renaming the project

Let's rename the project so that it is no longer called fibonacci. First open the Cargo.toml file in the root folder of the project. You should see something like this:

[package]
name = "fibonacci"
version = "0.1.0"
edition = "2021"

[dependencies]
valida-rs = { git = "https://github.com/lita-xyz/valida-rs.git", branch = "no-deps" }

Edit the line name = "fibonacci" to read instead name = "prime_factorization".

Building, running, proving, and verifying

To build the program to run on Valida, simply run:

cargo +valida build --release

To run the program:

valida run --fast ./target/valida-unknown-baremetal-gnu/release/prime_factorization log

You will get a series of prompts. Here is an example interaction with this program:

Please enter a 32-bit number:
4
Please enter the number of prime factors (with multiplicity):
2
Please enter the next prime factor:
2
Please enter the next prime factor:
2
Verified prime factorization of:
4

At this point, the output of the program will have been written to log. To prove an execution of the program, run:

valida prove ./target/valida-unknown-baremetal-gnu/release/prime_factorization proof

You will again get a series of prompts, which you should respond to in the same way. The result should be that a proof of that execution is written to proof. You can verify this proof as follows:

valida verify ./target/valida-unknown-baremetal-gnu/release/prime_factorization proof log
Valida Compiler Toolchain
Valida zk-VM