#![cfg_attr(zisk_guest, no_std)]
#![cfg_attr(zisk_guest, feature(linkage))]
#![allow(unexpected_cfgs)]
#![allow(unused_imports)]
#[cfg(zisk_guest)]
use core::arch::asm;
#[cfg(zisk_guest)]
mod dma;
#[cfg(zisk_guest)]
mod evm;
#[cfg(zisk_guest)]
mod fcall;
#[cfg(zisk_guest)]
mod alloc;
#[cfg(zisk_guest)]
extern crate alloc as alloc_crate;
#[cfg(zisk_guest)]
pub(crate) use alloc_crate as alloc_extern;
mod profile;
#[cfg(zisk_guest)]
pub use fcall::*;
pub mod io;
pub use profile::*;
pub mod syscalls;
pub mod zisklib;
pub mod ziskos_definitions;
#[cfg(all(not(zisk_guest), any(zisk_hints, zisk_hints_debug), feature = "user-hints"))]
pub mod hints;
#[cfg(all(not(zisk_guest), zisk_hints))]
extern "C" {
fn hint_input_data(input_data_ptr: *const u8, input_data_len: usize);
}
#[cfg(all(not(zisk_guest), zisk_hints_debug))]
extern "C" {
fn hint_log_c(msg: *const std::os::raw::c_char);
}
#[cfg(zisk_hints_debug)]
pub fn hint_log<S: AsRef<str>>(msg: S) {
#[cfg(not(zisk_guest))]
{
use std::ffi::CString;
if let Ok(c) = CString::new(msg.as_ref()) {
unsafe { hint_log_c(c.as_ptr()) };
}
}
#[cfg(zisk_guest)]
{
println!("{}", msg.as_ref());
}
}
#[cfg_attr(all(not(feature = "hints"), not(zisk_staticlib)), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_zkvm_init")]
pub extern "C" fn zkvm_init() {
#[cfg(not(zisk_guest))]
{
read_input_reset();
crate::zisklib::zkvm_io::reset();
}
#[cfg(all(not(zisk_guest), zisk_hints, feature = "user-hints"))]
{
let path =
std::env::var("ZISK_HINTS_OUTPUT").map(std::path::PathBuf::from).unwrap_or_else(|_| {
let dir = std::path::PathBuf::from("./tmp");
std::fs::create_dir_all(&dir).expect("failed to create tmp dir");
dir.join("hints.bin")
});
crate::hints::init_hints_file(path, None).expect("hints init failed");
}
}
#[cfg_attr(all(not(feature = "hints"), not(zisk_staticlib)), no_mangle)]
#[cfg_attr(feature = "hints", export_name = "hints_zkvm_deinit")]
pub extern "C" fn zkvm_deinit() {
#[cfg(all(not(zisk_guest), zisk_hints, feature = "user-hints"))]
{
crate::hints::close_hints().expect("hints close failed");
}
#[cfg(all(zisk_guest, zisk_staticlib, feature = "alloc-stats"))]
unsafe {
crate::alloc::print_max_used_sys_alloc();
}
}
#[cfg(all(not(zisk_guest), zisk_hints, feature = "user-hints"))]
pub fn zkvm_init_socket(
socket_path: std::path::PathBuf,
debug_file: Option<std::path::PathBuf>,
write_flush_threshold: Option<usize>,
ready: Option<tokio::sync::oneshot::Sender<()>>,
) -> anyhow::Result<()> {
read_input_reset();
crate::zisklib::zkvm_io::reset();
crate::hints::init_hints_socket(socket_path, debug_file, write_flush_threshold, ready)
}
pub trait ZiskTermination {
fn to_exit_code(self) -> i32;
}
impl ZiskTermination for () {
#[inline]
fn to_exit_code(self) -> i32 {
0
}
}
impl ZiskTermination for i32 {
#[inline]
fn to_exit_code(self) -> i32 {
self
}
}
impl<T: ZiskTermination, E> ZiskTermination for Result<T, E> {
#[inline]
fn to_exit_code(self) -> i32 {
match self {
Ok(value) => value.to_exit_code(),
Err(_) => 1,
}
}
}
#[macro_export]
macro_rules! entrypoint {
($path:path) => {
fn __zisk_entry() -> i32 {
$crate::ZiskTermination::to_exit_code($path())
}
mod zkvm_generated_main {
#[no_mangle]
extern "C" fn main() -> i32 {
$crate::zkvm_init();
let code = super::__zisk_entry();
$crate::zkvm_deinit();
code
}
}
};
}
#[allow(unused_imports)]
use crate::ziskos_definitions::ziskos_config::*;
#[cfg(zisk_guest)]
pub(crate) const INPUT_INITIAL_OFFSET: usize = 8;
#[cfg(not(zisk_guest))]
pub(crate) const INPUT_INITIAL_OFFSET: usize = 0;
pub(crate) static mut INPUT_POS: usize = INPUT_INITIAL_OFFSET;
pub fn read_input_reset() {
unsafe { INPUT_POS = INPUT_INITIAL_OFFSET };
}
#[cfg(not(zisk_guest))]
static NATIVE_INPUT: std::sync::Mutex<Option<Vec<u8>>> = std::sync::Mutex::new(None);
#[cfg(not(zisk_guest))]
pub fn set_native_input(data: Vec<u8>) {
*NATIVE_INPUT.lock().unwrap() = Some(data);
}
#[cfg(zisk_guest)]
pub(crate) fn read_slice_zerocopy<'a>() -> &'a [u8] {
let input_pos = unsafe { INPUT_POS };
let addr = (INPUT_ADDR as usize) + input_pos;
crate::zisklib::fcall_input_ready(&((addr + 7) as u64));
let len = unsafe {
let bytes = core::slice::from_raw_parts(addr as *const u8, 8);
u64::from_le_bytes(bytes.try_into().unwrap()) as usize
};
let data_addr = addr + 8;
let aligned_len = (len + 7) & !0x7;
crate::zisklib::fcall_input_ready(&((data_addr + aligned_len - 1) as u64));
unsafe { INPUT_POS = input_pos + 8 + aligned_len };
let data_slice = unsafe { core::slice::from_raw_parts(data_addr as *const u8, len) };
#[cfg(zisk_hints_debug)]
{
let start_bytes = &data_slice[..data_slice.len().min(64)];
let ellipsis = if data_slice.len() > 64 { "..." } else { "" };
hint_log(format!(
"hint_input_data (input_data: {:x?}{} , input_data_len: {}",
start_bytes,
ellipsis,
data_slice.len()
));
}
data_slice
}
#[cfg(not(zisk_guest))]
pub(crate) fn read_input() -> Vec<u8> {
let input_pos = unsafe { INPUT_POS };
let data = if let Some(buf) = NATIVE_INPUT.lock().unwrap().as_ref() {
let len_bytes: [u8; 8] = buf
.get(input_pos..input_pos + 8)
.expect("Failed to read length prefix from native input")
.try_into()
.unwrap();
let len = u64::from_le_bytes(len_bytes) as usize;
let data = buf
.get(input_pos + 8..input_pos + 8 + len)
.expect("Failed to read data from native input")
.to_vec();
let aligned_len = (len + 7) & !0x7;
unsafe { INPUT_POS = input_pos + 8 + aligned_len };
data
} else {
use std::{
fs::File,
io::{Read, Seek, SeekFrom},
};
let path =
std::env::var("ZISK_INPUT_FILE").unwrap_or_else(|_| "build/input.bin".to_string());
let mut file = File::open(&path)
.unwrap_or_else(|e| panic!("Error opening input file at {}: {}", path, e));
file.seek(SeekFrom::Start(input_pos as u64)).expect("Failed to seek in input file");
let mut len_bytes = [0u8; 8];
file.read_exact(&mut len_bytes).expect("Failed to read length prefix from input file");
let len = u64::from_le_bytes(len_bytes) as usize;
let mut data = vec![0u8; len];
file.read_exact(&mut data).expect("Failed to read data from input file");
let aligned_len = (len + 7) & !0x7;
unsafe { INPUT_POS = input_pos + 8 + aligned_len };
data
};
#[cfg(zisk_hints)]
unsafe {
hint_input_data(data.as_ptr(), data.len());
}
#[cfg(zisk_hints_debug)]
{
let start_bytes = &data[..data.len().min(64)];
let ellipsis = if data.len() > 64 { "..." } else { "" };
hint_log(format!(
"hint_input_data (input_data: {:x?}{} , input_data_len: {})",
start_bytes,
ellipsis,
data.len()
));
}
data
}
#[cfg(zisk_guest)]
pub(crate) fn set_output(id: usize, value: u32) {
use core::arch::asm;
let addr_v: *mut u32;
let arch_id_zisk: usize;
unsafe {
asm!(
"csrr {0}, marchid",
out(reg) arch_id_zisk,
)
};
assert!(id < 64, "Maximum number of public outputs: 64");
if arch_id_zisk == ARCH_ID_ZISK as usize {
addr_v = (OUTPUT_ADDR + 4 * (id as u64)) as *mut u32;
} else {
addr_v = (0x1000_0000 + 4 * (id as u64)) as *mut u32;
}
unsafe { core::ptr::write_volatile(addr_v, value) };
}
#[cfg(not(zisk_guest))]
pub(crate) fn set_output(id: usize, value: u32) {
println!("public {id}: {value:#010x}");
}
#[cfg(zisk_guest)]
pub mod ziskos {
use crate::ziskos_definitions::ziskos_config::*;
use core::arch::asm;
#[no_mangle]
#[link_section = ".text.init"]
unsafe extern "C" fn _start() -> ! {
asm!(
".option push",
".option norelax",
"la gp, _global_pointer",
".option pop",
"la sp, _init_stack_top",
"call {_zisk_main}",
"csrr t0, marchid",
"li t1, 0xFFFEEEE",
"beq t0, t1, 1f",
"li t0, 0x100000",
"beqz a0, 3f",
"slli t1, a0, 16",
"li t2, 0x3333",
"or t1, t1, t2",
"sw t1, 0(t0)",
"j 2f",
"3:",
"li t1, 0x5555",
"sw t1, 0(t0)",
"j 2f",
"1: li a7, 93",
"ecall",
"2: j 2b",
_zisk_main = sym _zisk_main, options(noreturn) );
pub fn zkvm_getrandom(s: &mut [u8]) -> Result<(), getrandom::Error> {
unsafe {
sys_rand(s.as_mut_ptr(), s.len());
}
Ok(())
}
getrandom::register_custom_getrandom!(zkvm_getrandom);
}
#[no_mangle]
unsafe extern "C" fn _zisk_main() -> i32 {
{
extern "C" {
fn main() -> i32;
}
#[cfg(any(
feature = "zisk-embedded-alloc",
feature = "zisk-embedded-dlmalloc-alloc",
feature = "zisk-embedded-talc-alloc",
feature = "zisk-embedded-tlfs-alloc"
))]
crate::alloc::embedded::init();
#[cfg(all(
not(feature = "zisk-embedded-alloc"),
not(feature = "zisk-embedded-dlmalloc-alloc"),
not(feature = "zisk-embedded-talc-alloc"),
not(feature = "zisk-embedded-tlfs-alloc")
))]
crate::alloc::init_sys_alloc();
run_init_array();
let code = main();
run_exit_handlers();
code
}
}
type CtorFn = extern "C" fn();
extern "C" {
static __init_array_start: [CtorFn; 0];
static __init_array_end: [CtorFn; 0];
static __fini_array_start: [CtorFn; 0];
static __fini_array_end: [CtorFn; 0];
}
unsafe fn run_init_array() {
let mut p = core::ptr::addr_of!(__init_array_start) as *const CtorFn;
let end = core::ptr::addr_of!(__init_array_end) as *const CtorFn;
while p < end {
(core::ptr::read(p))();
p = p.add(1);
}
}
unsafe fn run_fini_array() {
let start = core::ptr::addr_of!(__fini_array_start) as *const CtorFn;
let mut p = core::ptr::addr_of!(__fini_array_end) as *const CtorFn;
while p > start {
p = p.sub(1);
(core::ptr::read(p))();
}
}
#[no_mangle]
#[linkage = "weak"]
pub static __dso_handle: u8 = 0;
const MAX_ATEXIT: usize = 64;
static mut ATEXIT_FNS: [(Option<extern "C" fn(*mut u8)>, *mut u8); MAX_ATEXIT] =
[(None, core::ptr::null_mut()); MAX_ATEXIT];
static mut ATEXIT_LEN: usize = 0;
#[no_mangle]
#[linkage = "weak"]
pub unsafe extern "C" fn __cxa_atexit(
func: extern "C" fn(*mut u8),
arg: *mut u8,
_dso: *mut u8,
) -> i32 {
if ATEXIT_LEN >= MAX_ATEXIT {
return -1;
}
let base =
core::ptr::addr_of_mut!(ATEXIT_FNS) as *mut (Option<extern "C" fn(*mut u8)>, *mut u8);
core::ptr::write(base.add(ATEXIT_LEN), (Some(func), arg));
ATEXIT_LEN += 1;
0
}
#[no_mangle]
#[linkage = "weak"]
pub unsafe extern "C" fn __cxa_finalize(_dso: *mut u8) {
let base =
core::ptr::addr_of!(ATEXIT_FNS) as *const (Option<extern "C" fn(*mut u8)>, *mut u8);
while ATEXIT_LEN > 0 {
ATEXIT_LEN -= 1;
let (func, arg) = core::ptr::read(base.add(ATEXIT_LEN));
if let Some(func) = func {
func(arg);
}
}
}
unsafe fn run_exit_handlers() {
__cxa_finalize(core::ptr::null_mut());
run_fini_array();
}
#[no_mangle]
pub extern "C" fn sys_write(_fd: u32, write_ptr: *const u8, nbytes: usize) {
let arch_id_zisk: usize;
let mut addr: *mut u8 = 0x1000_0000 as *mut u8;
unsafe {
asm!(
"csrr {0}, marchid",
out(reg) arch_id_zisk,
)
};
if arch_id_zisk == ARCH_ID_ZISK as usize {
addr = UART_ADDR as *mut u8;
}
for i in 0..nbytes {
unsafe {
core::ptr::write_volatile(addr, *write_ptr.add(i));
}
}
}
use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
static mut RNG: Option<SmallRng> = None;
static mut SYS_RAND_WARNING: bool = false;
#[allow(static_mut_refs)]
#[no_mangle]
unsafe extern "C" fn sys_rand(recv_buf: *mut u8, words: usize) {
if !SYS_RAND_WARNING {
SYS_RAND_WARNING = true;
let msg = b"WARNING: Using insecure random number generator.\n";
sys_write(1, msg.as_ptr(), msg.len());
}
let rng = RNG.get_or_insert_with(|| SmallRng::seed_from_u64(0x123456789abcdef0));
for i in 0..words {
let element = recv_buf.add(i);
*element = rng.gen();
}
}
#[no_mangle]
extern "C" fn sys_getenv() {
}
#[no_mangle]
extern "C" fn sys_alloc_words() {
}
#[no_mangle]
extern "C" fn sys_argc() {
unimplemented!("sys_argc");
}
#[no_mangle]
extern "C" fn sys_argv() {
unimplemented!("sys_argv");
}
pub extern "C" fn sys_write_hex(val: usize, ln: bool) {
let mut buf = [0u8; 19]; buf[0] = b'0';
buf[1] = b'x';
let mut v = val;
for i in (2..18).rev() {
buf[i] = b"0123456789abcdef"[v & 0xF];
v >>= 4;
}
if ln {
buf[18] = b'\n';
sys_write(1, buf.as_ptr(), buf.len());
} else {
sys_write(1, buf.as_ptr(), buf.len() - 1);
}
}
pub extern "C" fn sys_write_u64(val: u64, ln: bool) {
let mut buf = [0u8; 21]; let mut v = val;
let mut end = 20usize;
if v == 0 {
buf[19] = b'0';
end = 19;
} else {
while v > 0 {
end -= 1;
buf[end] = b'0' + (v % 10) as u8;
v /= 10;
}
}
if ln {
buf[20] = b'\n';
sys_write(1, buf[end..].as_ptr(), 21 - end);
} else {
sys_write(1, buf[end..20].as_ptr(), 20 - end);
}
}
core::arch::global_asm!(include_str!("dma/memcpy.s"));
core::arch::global_asm!(include_str!("dma/memmove.s"));
core::arch::global_asm!(include_str!("dma/memcmp.s"));
core::arch::global_asm!(include_str!("dma/memset.s"));
}