Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ceno_zkvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde_json.workspace = true

base64 = "0.22"
ceno_emul = { path = "../ceno_emul" }
ceno_host = { path = "../ceno_host" }
ff_ext = { path = "../ff_ext" }
mpcs = { path = "../mpcs" }
multilinear_extensions = { version = "0", path = "../multilinear_extensions" }
Expand Down
48 changes: 43 additions & 5 deletions ceno_zkvm/src/bin/e2e.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ceno_emul::{IterAddresses, Program, WORD_SIZE, Word};
use ceno_host::CenoStdin;
use ceno_zkvm::{
e2e::{Checkpoint, Preset, run_e2e_with_checkpoint, setup_platform},
with_panic_hook,
Expand Down Expand Up @@ -48,8 +49,16 @@ struct Args {
/// Hints: prover-private unconstrained input.
/// This is a raw file mapped as a memory segment.
/// Zero-padded to the right to the next power-of-two size.
#[arg(long)]
hints: Option<String>,
#[arg(long, conflicts_with = "structured_hints")]
raw_hints: Option<String>,

/// Hints: prover-private unconstrained input.
/// This is a file containing decimal representations of
/// a value N, followed by N u32 integers.
/// The N integers are processed so they can be read
/// directly by guest programs.
#[arg(long, conflicts_with = "raw_hints")]
structured_hints: Option<String>,

/// Stack size in bytes.
#[arg(long, default_value = "32k", value_parser = parse_size)]
Expand Down Expand Up @@ -120,8 +129,19 @@ fn main() {
args.heap_size
);

tracing::info!("Loading hints file: {:?}", args.hints);
let hints = memory_from_file(&args.hints);
let (hints, filename) = if args.raw_hints.is_some() {
(read_raw_hints(&args.raw_hints), args.raw_hints)
} else if args.structured_hints.is_some() {
(
read_structured_hints(&args.structured_hints),
args.structured_hints,
)
} else {
(vec![], None)
};

tracing::info!("Loading hints file: {:?}", filename);

assert!(
hints.len() <= platform.hints.iter_addresses().len(),
"hints must fit in {} bytes",
Expand Down Expand Up @@ -187,7 +207,7 @@ fn main() {
}
};
}
fn memory_from_file(path: &Option<String>) -> Vec<u32> {
fn read_raw_hints(path: &Option<String>) -> Vec<u32> {
path.as_ref()
.map(|path| {
let mut buf = fs::read(path).expect("could not read file");
Expand All @@ -198,3 +218,21 @@ fn memory_from_file(path: &Option<String>) -> Vec<u32> {
})
.unwrap_or_default()
}

/// Reads a sequence of u32s and formats it as guest input
fn read_structured_hints(path: &Option<String>) -> Vec<u32> {
let structured_hints =
fs::read_to_string(path.as_ref().unwrap()).expect("could not read structured hints file");

let mut parts = structured_hints.split_whitespace();
let n: usize = parts.next().unwrap().parse().expect("could not parse N");
let values: Vec<u32> = parts
.take(n)
.map(|part| part.parse().expect("could not parse hint"))
.collect();

// Serialize the read values into the suitable format
let mut input = CenoStdin::default();
input.write(&values).unwrap();
(&input).into()
}
61 changes: 5 additions & 56 deletions ceno_zkvm/src/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@ use crate::{
tables::{MemFinalRecord, MemInitRecord, ProgramTableCircuit, ProgramTableConfig},
};
use ceno_emul::{
ByteAddr, CENO_PLATFORM, EmuContext, InsnKind, IterAddresses, Platform, Program, StepRecord,
Tracer, VMState, WORD_SIZE, WordAddr,
CENO_PLATFORM, EmuContext, InsnKind, IterAddresses, Platform, Program, StepRecord, Tracer,
VMState, WORD_SIZE, WordAddr,
};
use clap::ValueEnum;
use ff_ext::ExtensionField;
use itertools::{Itertools, MinMaxResult, chain};
use itertools::{Itertools, chain};
use mpcs::PolynomialCommitmentScheme;
use std::{
collections::{BTreeSet, HashMap, HashSet},
iter::zip,
sync::Arc,
};
use std::{collections::BTreeSet, iter::zip, sync::Arc};
use transcript::BasicTranscript as Transcript;

pub struct FullMemState<Record> {
Expand Down Expand Up @@ -129,7 +125,7 @@ fn emulate_program(
}
})
.collect_vec();
debug_memory_ranges(&vm, &mem_final);
// debug_memory_ranges(&vm, &mem_final);

// Find the final public IO cycles.
let io_final = io_init
Expand Down Expand Up @@ -548,50 +544,3 @@ pub fn run_e2e_verify<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>>(
None => tracing::error!("Unfinished execution. max_steps={:?}.", max_steps),
}
}

fn debug_memory_ranges(vm: &VMState, mem_final: &[MemFinalRecord]) {
let accessed_addrs = vm
.tracer()
.final_accesses()
.iter()
.filter(|(_, &cycle)| (cycle != 0))
.map(|(&addr, _)| addr.baddr())
.filter(|addr| vm.platform().can_read(addr.0))
.collect_vec();

let handled_addrs = mem_final
.iter()
.filter(|rec| rec.cycle != 0)
.map(|rec| ByteAddr(rec.addr))
.collect::<HashSet<_>>();

tracing::debug!(
"Memory range (accessed): {:?}",
format_segments(vm.platform(), accessed_addrs.iter().copied())
);
tracing::debug!(
"Memory range (handled): {:?}",
format_segments(vm.platform(), handled_addrs.iter().copied())
);

for addr in &accessed_addrs {
assert!(handled_addrs.contains(addr), "unhandled addr: {:?}", addr);
}
}

fn format_segments(
platform: &Platform,
addrs: impl Iterator<Item = ByteAddr>,
) -> HashMap<String, MinMaxResult<ByteAddr>> {
addrs
.into_grouping_map_by(|addr| format_segment(platform, addr.0))
.minmax()
}

fn format_segment(platform: &Platform, addr: u32) -> String {
format!(
"{}{}",
if platform.can_read(addr) { "R" } else { "-" },
if platform.can_write(addr) { "W" } else { "-" },
)
}