Skip to content

Commit

Permalink
wip - fixed replay on risc0
Browse files Browse the repository at this point in the history
  • Loading branch information
mpernambuco committed Sep 8, 2024
1 parent 95afe40 commit ada4761
Show file tree
Hide file tree
Showing 19 changed files with 325 additions and 368 deletions.
88 changes: 31 additions & 57 deletions rust/verify_steps/host/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,82 +1,56 @@
use std::fs::{self, File};
use std::fs::{File};
use std::io::Read;
use methods::{
TESTE1_ELF, TESTE1_ID
};
use risc0_zkvm::{
sha::{Digest, Impl, Sha256},
default_prover,
ExecutorEnv };

pub struct PageInfo {
pub address: u64,
pub data: [u8; 4986],
fn load_page_data(file_path: &str) -> (Vec<u8>, u32) {
let mut file = File::open(file_path).expect("Could not open file");
let mut buffer = [0; 4];
file.read(&mut buffer).expect("Could not read file contents");
let page_count = u32::from_le_bytes(buffer);
let mut data = vec![0; (page_count * (8+4096)) as usize];
file.read(&mut data).expect("Could not read file contents");
(data, page_count)
}

fn load_pages(dir_path: &str) -> Vec<PageInfo> {
let mut files = fs::read_dir(dir_path)
.expect("Could not read directory")
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
if path.is_file() {
Some(path)
} else {
None
}
})
.collect::<Vec<_>>();

files.sort_by_key(|path| {
u64::from_str_radix(path.file_name().unwrap().to_str().unwrap(), 16).unwrap()
});
let mut pages = Vec::new();
for file_path in files {
let filename = file_path.file_name().unwrap().to_str().unwrap();
let address = u64::from_str_radix(filename, 16).unwrap();
let mut file = File::open(&file_path).expect("Could not open file");
let mut data = [0 as u8; 4986];
// read file contents and push a new page info
file.read(&mut data).expect("Could not read file contents");
pages.push(PageInfo {
address,
data,
});
}
pages
}

// Page files generated by the following command:
// mkdir -p /tmp/loxa3
// cartesi-machine.lua --max-mcycle=0 --log-steps=1,/tmp/loxa3
// run this command to run the 15 cycles used by this test
// cartesi-machine.lua --max-mcycle=0 --log-steps=15,/tmp
fn main() {
let pages = load_pages("/tmp/loxa3");
// Initialize tracing. In order to view logs, run `RUST_LOG=info cargo run`
let step_count: u64 = 1; // number of steps to replay
let (data_before, page_count_before) = load_page_data("/tmp/pages-before");
let hash_before = Impl::hash_bytes(&data_before);
println!("host: hash before: {:?}", hash_before);
let (data_after, page_count_after) = load_page_data("/tmp/pages-after");
assert_eq!(page_count_before, page_count_after);
let hash_after = Impl::hash_bytes(&data_after);
println!("host: hash after: {:?}", hash_after);

tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.init();

let mut builder = ExecutorEnv::builder();
// stupid way to send pages to the guest. Will improve later.
builder.write(&pages.len()).unwrap();
let mut temp: [u8; 32] = [0; 32];
for page in pages {
builder.write(&page.address).unwrap();
println!("writing page: {:x}", page.address);
for i in (0..4096).step_by(32) {
temp.copy_from_slice(&page.data[i..(i + 32)]);
builder.write(&temp).unwrap();
}
let data_size: u32 = data_before.len() as u32;
builder.write(&step_count).unwrap();
builder.write(&page_count_before).unwrap();
for i in (0..data_size).step_by(1) {
builder.write(&data_before[i as usize]).unwrap();
}

let env = builder.build().unwrap();

let prover = default_prover();
let receipt = prover
.prove(env, TESTE1_ELF)
.unwrap();

let output:u64 = receipt.journal.decode().unwrap();
println!("result: {:x}", output);

let hash_after_guest:Digest = receipt.journal.decode().unwrap();
println!("host: hash after guest: {:?}", hash_after_guest);
assert_eq!(hash_after_guest, *hash_after);
receipt
.verify(TESTE1_ID)
.unwrap();
Expand Down
15 changes: 12 additions & 3 deletions rust/verify_steps/methods/guest/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
// fn main() {
// cc::Build::new()
// .object("../../../../zkarch/zkarch-replay-steps.o")
// .compile("loxa");
// }

fn main() {
const OBJ_PATH: &str = "../../../../zkarch/zkarch-replay-steps.o";

println!("cargo:rerun-if-changed={}", OBJ_PATH);
cc::Build::new()
.object("../../../../zkarch/zkarch-replay-steps.o")
.compile("loxa");
}
.object(OBJ_PATH)
.compile("cartesi_zkarch_replay_steps");
}
65 changes: 25 additions & 40 deletions rust/verify_steps/methods/guest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,40 @@
//#![no_std] // std support is experimental

use risc0_zkvm::guest::env;
use std::ffi::{CStr};
risc0_zkvm::guest::entry!(main);
use std::ptr;
use std::os::raw::{c_char, c_ulonglong};
use std::os::raw::{c_char, c_ulong, c_ulonglong};
use risc0_zkvm::sha::{Impl, Sha256};

#[repr(C)]
pub struct PageInfo {
pub address: c_ulonglong,
pub data: [c_char; 4096],
pub next: *mut PageInfo,
extern "C" {
pub fn zkarch_replay_steps(steps: c_ulonglong, data: *const c_char, size: c_ulong) -> c_ulonglong;
}

extern "C" {
pub fn zkarch_replay_steps(steps: c_ulonglong, pages: *mut PageInfo) -> c_ulonglong;
#[no_mangle]
pub extern "C" fn print_from_c(text: *const c_char) {
let str = unsafe { CStr::from_ptr(text).to_string_lossy().into_owned() };
println!("print_from_c: {}", str);
}

#[no_mangle]
pub extern "C" fn abort_from_c() {
panic!("abort called from C");
}

fn main() {
// linked list of pages
let mut _head : *mut PageInfo = ptr::null_mut();
let mut _current : *mut PageInfo = ptr::null_mut();
// read page count
let _page_count : u64 = env::read();
// stupid way of reading pages. Will improve later.
for _ in 0.._page_count {
let _asdress : u64 = env::read();
let mut _page = PageInfo {
address: _asdress,
data: [0; 4096],
next: ptr::null_mut(),
};
// even stupider way of reading page data. Will improve later.
for _i in (0..4096).step_by(32) {
env::read_slice(&mut _page.data[_i..(_i + 32)]);
}
if _head.is_null() {
_head = Box::into_raw(Box::new(_page));
_current = _head;
} else {
let _new_page = Box::into_raw(Box::new(_page));
unsafe {
(*_current).next = _new_page;
_current = _new_page;
}
}
let steps : u64 = env::read();
let page_count : u32 = env::read();
let data_size : u32 = page_count * (4096 + 8);
let mut data: Vec<u8> = vec![0; data_size as usize];
for i in (0..data_size).step_by(1) {
data[i as usize] = env::read();
}

let mctcle_end: u64;
unsafe {
mctcle_end = zkarch_replay_steps(1, _head);
mctcle_end = zkarch_replay_steps(steps, data.as_ptr() as *const c_char, page_count);
}

env::commit(&_page_count);
println!("guest: mctcle_end: {:?}", mctcle_end);
let hash = Impl::hash_bytes(&data);
println!("guest: hash after {:?}", hash);
env::commit(hash);
}
8 changes: 4 additions & 4 deletions src/i-state-access.h
Original file line number Diff line number Diff line change
Expand Up @@ -681,13 +681,13 @@ class i_state_access { // CRTP
}

template <typename T, typename U>
T aliased_unaligned_read(const void *host_ptr, uint64_t paddr) {
return derived().template do_aliased_unaligned_read<T, U>(host_ptr, paddr);
T aliased_unaligned_read(const void *host_ptr) {
return derived().template do_aliased_unaligned_read<T, U>(host_ptr);
}

template <typename T>
inline T aliased_aligned_read(const void *host_ptr, uint64_t paddr) {
return derived().template do_aliased_aligned_read<T>(host_ptr, paddr);
inline T aliased_aligned_read(const void *host_ptr) {
return derived().template do_aliased_aligned_read<T>(host_ptr);
}

/// \brief Writes a word to memory.
Expand Down
6 changes: 3 additions & 3 deletions src/interpret.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5495,7 +5495,7 @@ static FORCE_INLINE fetch_status fetch_insn(STATE_ACCESS &a, uint64_t &pc, uint3
// If pc is pointing to the very last 2 bytes of a page, it's crossing a page boundary.
if (unlikely(((~pc & PAGE_OFFSET_MASK) >> 1) == 0)) {
// Here we are crossing page boundary, this is unlikely (1 in 2048 possible cases)
insn = a.template aliased_aligned_read<uint16_t>(hptr, pc);
insn = aliased_aligned_read<uint16_t>(hptr);
// If not a compressed instruction, we must read 2 additional bytes from the next page.
if (unlikely((insn & 3) == 3)) {
// We have to perform a new address translation to read the next 2 bytes since we changed pages.
Expand All @@ -5507,7 +5507,7 @@ static FORCE_INLINE fetch_status fetch_insn(STATE_ACCESS &a, uint64_t &pc, uint3
fetch_vaddr_page = vaddr & ~PAGE_OFFSET_MASK;
fetch_vh_offset = cast_ptr_to_addr<uint64_t>(hptr) - vaddr;
// Produce the final 4-byte instruction
insn |= a.template aliased_aligned_read<uint16_t>(hptr, pc) << 16;
insn |= aliased_aligned_read<uint16_t>(hptr) << 16;
}
return fetch_status::success;
}
Expand All @@ -5516,7 +5516,7 @@ static FORCE_INLINE fetch_status fetch_insn(STATE_ACCESS &a, uint64_t &pc, uint3
// therefore we must perform a misaligned 4 byte read on a 2 byte aligned pointer.
// In case pc holds a compressed instruction, insn will store 2 additional bytes,
// but this is fine because later the instruction decoder will discard them.
insn = a.template aliased_unaligned_read<uint32_t, uint16_t>(hptr, pc);
insn = aliased_unaligned_read<uint32_t, uint16_t>(hptr);
return fetch_status::success;
}

Expand Down
94 changes: 59 additions & 35 deletions src/machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#include <cstring>
#include <iomanip>
#include <iostream>
#include <filesystem>
#include <cstdlib>
#include <uarch-solidity-compat.h>

#include "clint-factory.h"
#include "dtb.h"
Expand Down Expand Up @@ -1967,45 +1967,69 @@ void machine::log_steps(uint64_t mcycle_end, const std::string &directory) {
}
record_multi_step_state_access a(*this, directory);
interpret(a, mcycle_end);
a.finish();
}

namespace fs = std::filesystem;

void machine::replay_steps(uint64_t steps, const std::string &directory) {
printf("---> replay steps=%llx, directory=%s\n", steps, directory.c_str());
fs::path dir(directory);
if (!fs::exists(dir) || !fs::is_directory(dir)) {
throw std::invalid_argument{"directory does not exist"};
}
page_info *head = nullptr;
page_info *tail = nullptr;
for (const auto& entry : fs::directory_iterator(directory)) {
if (fs::is_regular_file(entry.status())) {
auto name = entry.path().filename().string();
page_info *page = new page_info;
page->next = nullptr;
page->address = std::strtoull(name.c_str(), nullptr, 16);
auto fp = unique_fopen(entry.path().c_str(), "rb");
if (!fp) {
throw std::runtime_error("Could not open page file for reading");
}
if (fread(&page->data, 1, PMA_PAGE_SIZE, fp.get()) != PMA_PAGE_SIZE) {
throw std::runtime_error("Could not read page data");
}
if (!head) {
head = page;
tail = page;
} else {
tail->next = page;
tail = page;
}
std::cout << entry.path().filename() << std::endl;
}
void machine::replay_steps(uint64_t mcycle_end, const std::string &directory) {
printf("---> replay mcycle_end=%lld, directory=%s\n", mcycle_end, directory.c_str());
auto fp_before = unique_fopen((directory + "/" + "pages-before").c_str(), "rb");
if (!fp_before) {
throw std::runtime_error("Could not open pages-before file for writing");
}
auto fp_after = unique_fopen((directory + "/" + "pages-after").c_str(), "rb");
if (!fp_after) {
throw std::runtime_error("Could not open pages-after file for writing");
}
uint32_t page_count_before = 0;
uint32_t page_count_after = 0;
if (fread(&page_count_before, 1, sizeof(page_count_before), fp_before.get()) != sizeof(page_count_before)) {
throw std::runtime_error("Could not read page count before");
}
if (fread(&page_count_after, 1, sizeof(page_count_after), fp_after.get()) != sizeof(page_count_after)) {
throw std::runtime_error("Could not read page count after");
}
if (page_count_before != page_count_after) {
throw std::runtime_error("Page count mismatch");
}
replay_multi_step_state_access a(head);
auto page_infos_before = unique_calloc<page_info>(page_count_before);
if (!page_infos_before) {
throw std::runtime_error("Could not allocate page infos before");
}
if (fread(page_infos_before.get(), sizeof(page_info), page_count_before, fp_before.get()) != page_count_before) {
throw std::runtime_error("Could not read page infos before");
}
auto page_infos_after = unique_calloc<page_info>(page_count_after);
if (!page_infos_after) {
throw std::runtime_error("Could not allocate page infos after");
}
if (fread(page_infos_after.get(), sizeof(page_info), page_count_after, fp_after.get()) != page_count_after) {
throw std::runtime_error("Could not read page infos after");
}

replay_multi_step_state_access a(page_infos_before.get(), page_count_before);
auto current_mcycle = a.read_mcycle();
uint64_t mcycle_end = current_mcycle + steps;
printf("mcycle = %lld, mcycle_end = %lld\n", current_mcycle, mcycle_end);
interpret(a, mcycle_end);
a.finish();
// // print compare pages
bool ok = true;
printf("Comparing pages....\n");
for(uint32_t i=0; i<page_count_before; i++) {
page_info *before = &page_infos_before.get()[i];
page_info *after = &page_infos_after.get()[i];
printf("%016llx ", before->address);
if (memcmp(before->data, after->data, PMA_PAGE_SIZE) != 0) {
ok = false;
printf("FAIL @ %d\n", i);
} else {
printf("OK\n");
}
}
if (ok) {
printf("All pages match\n");
} else {
printf("Some pages do not match\n");
}
}

access_log machine::log_send_cmio_response(uint16_t reason, const unsigned char *data, size_t length,
Expand Down
3 changes: 3 additions & 0 deletions src/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,8 @@ uint64_t os_get_concurrency() {
#endif
}


#ifndef ZKARCHITECTURE
bool os_parallel_for(uint64_t n, const std::function<bool(uint64_t j, const parallel_for_mutex &mutex)> &task) {
#ifdef HAVE_THREADS
if (n > 1) {
Expand Down Expand Up @@ -716,6 +718,7 @@ bool os_parallel_for(uint64_t n, const std::function<bool(uint64_t j, const para
}
return succeeded;
}
#endif

bool os_select_fds(const os_select_before_callback &before_cb, const os_select_after_callback &after_cb,
uint64_t *timeout_us) {
Expand Down
Loading

0 comments on commit ada4761

Please sign in to comment.