Core Concepts
Guest and Host

Guest and Host

Guest

The Guest program is responsible for executing the ELF binary which is proven inside of the zkVM, this involves:

  • Reading inputs provided by the host
  • Writing private outputs to the host
  • Committing public outputs to the journal.

If there is an error when running a guest program, the proof should not be generated

Example Guest Programs

package main
 
import (
	"bytes"
	"crypto/sha256"
	"log"
	"github.com/zkMIPS/zkm/go-runtime/zkm_runtime"
)
 
type DataId uint32
 
// use iota to create enum
const (
	TYPE1 DataId = iota
	TYPE2
	TYPE3
)
 
type Data struct {
	Input1  [10]byte
	Input2  uint8
	Input3  int8
	Input4  uint16
	Input5  int16
	Input6  uint32
	Input7  int32
	Input8  uint64
	Input9  int64
	Input10 []byte
	Input11 DataId
	Input12 string
}
 
func main() {
	a := zkm_runtime.Read[Data]()
 
	data := []byte(a.Input12)
	hash := sha256.Sum256(data)
 
	assertEqual(hash[:], a.Input10)
 
	zkm_runtime.Commit[Data](a)
}
 
func assertEqual(a []byte, b []byte) {
	if !bytes.Equal(a, b) {
		log.Fatal("%x != %x", a, b)
	}
}

Host

The Host program is responsible for setting up the environment for the guest program and evaluating the results from the guest execution, this involves:

  • Supplying the inputs to the guest program
  • Evaluating the private outputs provided by the guest
  • Evaluating the public journal output provided by the guest

Example Host Program

fn prove_sha2_go() {
    // 1. split ELF into segs
    let elf_path = env::var("ELF_PATH").expect("ELF file is missing");
    let seg_path = env::var("SEG_OUTPUT").expect("Segment output path is missing");
    let seg_size = env::var("SEG_SIZE").unwrap_or("0".to_string());
    let seg_size = seg_size.parse::<_>().unwrap_or(0);
 
    let mut state = load_elf_with_patch(&elf_path, vec![]);
    // load input
    let args = env::var("ARGS").unwrap_or("data-to-hash".to_string());
    // assume the first arg is the hash output(which is a public input), and the second is the input.
    let args: Vec<&str> = args.split_whitespace().collect();
    assert_eq!(args.len(), 2);
 
    let mut data = Data::new();
 
    // Fill in the input data
    data.input10 = hex::decode(args[0]).unwrap();
    data.input12 = args[1].to_string();
 
    state.add_input_stream(&data);
    log::info!(
        "enum {} {} {}",
        DataId::TYPE1 as u8,
        DataId::TYPE2 as u8,
        DataId::TYPE3 as u8
    );
    log::info!("public input: {:X?}", data);
 
    let (total_steps, mut state) = split_prog_into_segs(state, &seg_path, "", seg_size);
 
    let value = state.read_public_values::<Data>();
    log::info!("public value: {:X?}", value);
 
    let mut seg_num = 1usize;
    if seg_size != 0 {
        seg_num = (total_steps + seg_size - 1) / seg_size;
    }
 
    if seg_num == 1 {
        let seg_file = format!("{seg_path}/{}", 0);
        prove_single_seg_common(&seg_file, "", "", "", total_steps)
    } else {
        prove_multi_seg_common(&seg_path, "", "", "", seg_size, seg_num, 0).unwrap()
    }
}