Prepare WASM Image

Since ZKWASM runs Web Assembly, applications that are suppose to run in ZKWASM have to be compiled into WASM. WebAssembly (WASM) is designed to be a portable compilation target for programming languages, enabling deployment on the web for client and server applications. A wide range of languages can be compiled into Wasm, with varying degrees of support. Some of the most notable languages include:

C/C++: These are the most well-supported languages for compiling to WebAssembly. Tools like Emscripten are used to compile C and C++ code to Wasm.

Rust: Rust has robust support for WebAssembly. It’s often chosen for Wasm projects due to its performance characteristics and safety guarantees.

AssemblyScript: A variant of TypeScript, AssemblyScript is designed to compile to WebAssembly. It’s a good choice for developers familiar with TypeScript or JavaScript.

Go: Go has experimental support for WebAssembly. While not as mature as C/C++ or Rust, it is still a viable option.

Python, Ruby, and PHP: These languages have experimental or early-stage support for WebAssembly. Tools and runtimes are being developed to compile these languages to Wasm, but they may not yet be suitable for production use.

Kotlin/Native: Kotlin can target WebAssembly, allowing Kotlin code to run in the browser.

The ability to compile a language to WebAssembly largely depends on the availability of a suitable compiler or toolchain. The WebAssembly ecosystem is rapidly evolving, and new tools and languages are constantly being added to the list.

Generate WASM from C (or C++) code

Following is a trivial piece of C code "zkmain.c" that compares two inputs (one from the public input and the other from the private input) and requires that these two inputs are equal.

#include <stdint.h>
uint64_t wasm_input(uint32_t);
extern void require(int cond);

static inline uint64_t wasm_public_input()
{
  return wasm_input(1);
}

static inline uint64_t wasm_private_input()
{
  return wasm_input(0);
}

__attribute__((visibility("default")))
int zkmain() {
    uint64_t public = wasm_public_input();
    uint64_t private = wasm_private_input();
    require(public == private);
    return 0;
}

To compile the above piece of C code into WASM, we use the clang-15 as our C compiler and set the target binary to be wasm32

LIBS  = -lkernel32 -luser32 -lgdi32 -lopengl32
CFILES = $(wildcard *.c)

ifeq ($(CLANG),)
CLANG=clang-15
endif

FLAGS = -flto -O3 -nostdlib -fno-builtin -ffreestanding -mexec-model=reactor --target=wasm32 -Wl,--strip-all -Wl,--initial-memory=131072 -Wl,--max-memory=131072 -Wl,--no-entry -Wl,--allow-undefined -Wl,--export-dynamic

all: output.wasm

output.wasm: $(CFILES)
	$(CLANG) -o $@ $(CFILES) $(FLAGS) $(CFLAGS)


clean:
	rm -f *.wasm *.wat

Put the above Makefile in the same folder as zkmain.c and type

make

will give the desired wasm file output.wasm. This file can be then used to run in the ZKWASM VM to generate proofs once inputs are provided. Also, more C to zkWASM program sdk can be find in Delphinus Lab's zkWasm-C repo.

Generate WASM from Rust code

Here we use rust as a reference language, thus all examples are coded and compiled using wasm-bindgen. wasm-bindgen is a tool and library for facilitating high-level interactions between WebAssembly (Wasm) modules and JavaScript. It’s particularly prominent in the Rust ecosystem, but its principles can be applied to other languages that compile to WebAssembly.

To use wasm-bindgen in your rust project, you can simply put it in your Cargo.tom as follows:

[dependencies]
zkwasm-rust-sdk = { git = "https://github.com/DelphinusLab/zkWasm-rust.git" }
wasm-bindgen = "0.2.83"

Suppose that we use zkmain.rs as the main entry point of your application. We can start our application as follows:

use zkwasm_rust_sdk::wasm_output;
  use zkwasm_rust_sdk::wasm_input;
  use zkwasm_rust_sdk::wasm_dbg;
  use zkwasm_rust_sdk::require;

#[wasm_bindgen]
pub fn zkmain() -> {
    let mut hasher = Sha256::new();
    let commands_len = unsafe {wasm_input(0)};
    for _ in 0..commands_len {
        let command = unsafe {wasm_input(0)};
        hasher.update(command.to_le_bytes());
        step(command);
    }
    let msghash = hasher.finalize();
    zkwasm_rust_sdk::dbg!("Hello world application with command hash {:?}\n", msghash);
}

Simlarly the Makefile used to generate WASM from RUST is the following:

build:
	wasm-pack build --release --out-name rust-sdk-test.wasm --out-dir pkg --features wasmbind
	wasm-opt -Oz -o output.wasm pkg/rust-sdk-test.wasm

clean:
	rm -rf pkg output params

File output after WASM code generation.

Once you have compiled your C (C++) and RUST code, the directory should look like the below:

.
├── Makefile
├── output.wasm
└── zkmain.c/zkmain.rs

The file ouput.wasm will be the input image for zkWasm's Setup Phase below.

Last updated