From 25834582d128f09b746c8f8c5053ae01b8de92bf Mon Sep 17 00:00:00 2001 From: MauroFab Date: Fri, 23 Oct 2026 13:11:35 -0300 Subject: [PATCH 1/2] Correct comments and test data left stale by the COPY unification - Name COPY (not COMMIT) among the ECALL receivers and as the Commit-bus sender. - Count five accelerators, not six, and rename the test that says six. - logs.rs: the row width follows from the count alone; it no longer reads src % 8 / dst % 8. - CLI accel_counts_sizes_dma_calls: derive rows with copy_trace_rows instead of the old terminal-row schedule. - Drop the doubled #[cfg(test)], the DMA/DMA_SET history line, the stale drift-test doc, the allocator and dma_memset_min comments. --- bin/cli/src/main.rs | 8 +++++--- executor/programs/rust/dma_memset_min/src/main.rs | 5 +++-- executor/src/vm/logs.rs | 10 +++++----- executor/src/vm/memory.rs | 8 ++++---- executor/tests/rust.rs | 2 +- prover/src/lib.rs | 2 +- prover/src/tables/copy.rs | 2 +- prover/src/tables/cpu.rs | 2 +- prover/src/tables/hint.rs | 2 +- prover/src/tables/keccak.rs | 2 +- prover/src/tables/types.rs | 4 ++-- prover/src/tests/count_table_lengths_drift_tests.rs | 9 ++++----- prover/src/tests/mod.rs | 1 - prover/src/tests/prove_elfs_tests.rs | 2 +- prover/src/tests/skip_empty_tables_tests.rs | 8 ++++---- syscalls/src/allocator.rs | 2 +- 16 files changed, 35 insertions(+), 34 deletions(-) diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index ace6a1108..7b9fa046c 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -1237,8 +1237,10 @@ mod tests { #[test] fn accel_counts_sizes_dma_calls() { let mut counts = AccelCounts::default(); - // (bytes, rows) as the executor derives them at each ecall. - for (bytes, rows) in [(256, 33), (256, 33), (8, 2), (3, 4), (0, 1)] { + // Rows as the executor derives them at each ecall. + for bytes in [256, 256, 8, 3, 0] { + let rows = + executor::vm::instruction::execution::copy_trace_rows(0x1000, 0x2000, bytes, false); counts.tally(Accelerator::Dma, bytes, rows); } @@ -1246,7 +1248,7 @@ mod tests { assert_eq!(counts.dma_bytes, 523); // The rows are the executor's, derived where src, dst and count are all known; // the report only sums them. - assert_eq!(counts.dma_rows, 73); + assert_eq!(counts.dma_rows, 32 + 32 + 1 + 3 + 1); // The other accelerators must leave the DMA size lines alone. let mut others = AccelCounts::default(); diff --git a/executor/programs/rust/dma_memset_min/src/main.rs b/executor/programs/rust/dma_memset_min/src/main.rs index 1705064b6..ef8150e54 100644 --- a/executor/programs/rust/dma_memset_min/src/main.rs +++ b/executor/programs/rust/dma_memset_min/src/main.rs @@ -5,8 +5,9 @@ unsafe extern "C" { } pub fn main() { - // 43 bytes = five eight-byte rows plus a three-byte tail, so one call yields - // a first row, wide intermediate rows, tail rows and a terminal row. + // 43 bytes: the stub seeds the first 8 with stores and hands the chip the other + // 35, which it moves as four eight-byte rows and three one-byte rows. The chain + // ends on the row that moves the last byte; there is no terminal row. let mut buffer = [0u8; 43]; let count = core::hint::black_box(buffer.len()); diff --git a/executor/src/vm/logs.rs b/executor/src/vm/logs.rs index d21e3eefe..65ee268c0 100644 --- a/executor/src/vm/logs.rs +++ b/executor/src/vm/logs.rs @@ -16,11 +16,11 @@ /// DMA memcpy and DMA memset: byte count. 0 for every other syscall, Keccak /// included. /// -/// The row count is carried rather than recomputed downstream because it is not a -/// function of the byte count: a row is eight bytes or one, and the schedule reads -/// `src % 8` and `dst % 8` to decide. The executor is the only place that holds -/// `src`, `dst` and `count` at once, so it derives the count there and the CLI's -/// accelerator report just sums it. +/// The row count is carried rather than recomputed downstream so that trace sizing +/// and the CLI's accelerator report share one formula, `copy_trace_rows`, which the +/// executor evaluates where `src`, `dst` and `count` are all known. Today a row's +/// width follows from the remaining count alone (eight bytes, or one for the last +/// seven), so the CLI report just sums the carried counts. #[derive(Debug, Clone)] pub struct Log { /// PC before instruction execution (use this to look up the instruction) diff --git a/executor/src/vm/memory.rs b/executor/src/vm/memory.rs index e1a269a01..ef06e1e0b 100644 --- a/executor/src/vm/memory.rs +++ b/executor/src/vm/memory.rs @@ -39,7 +39,7 @@ impl BuildHasher for U64BuildHasher { pub type U64HashMap = HashMap; /// Total cap on public output bytes across all `commit_public_output` calls. -/// The COMMIT AIR concatenates calls via the running `x254` index, so this +/// The COPY chip's `write` rows concatenate calls via the running `x254` index, so this /// is enforced as a running-total budget rather than a per-call limit. pub const MAX_PUBLIC_OUTPUT_TOTAL_SIZE: u64 = 1024 * 1024; /// Maximum size of the private input memory region (in bytes). 512 MiB so a @@ -60,7 +60,7 @@ pub const PRIVATE_INPUT_LENGTH_PREFIX_BYTES: usize = size_of::(); pub struct Memory { cells: U64HashMap<[u8; 4]>, /// Bytes committed to public output via `commit_public_output`. The - /// COMMIT AIR doesn't write to a fixed memory region (it streams bytes + /// COPY chip's `write` rows don't write to a fixed memory region (it streams bytes /// onto the Commit bus by `index`), so this buffer is purely the /// executor's view used by `read_return_value` and CLI display. public_output: Vec, @@ -202,8 +202,8 @@ impl Memory { } /// Append `length` bytes from guest memory starting at `address` to the - /// public output. The COMMIT AIR concatenates calls via the running - /// `x254` index, and the trace builder accumulates `commit_ops` into + /// public output. The COPY chip's `write` rows concatenate calls via the + /// running `x254` index, and the trace builder collects their bytes into /// `VmProof.public_output`; this method maintains the executor's view /// of the same byte stream so `read_return_value` matches. pub fn commit_public_output(&mut self, address: u64, length: u64) -> Result<(), MemoryError> { diff --git a/executor/tests/rust.rs b/executor/tests/rust.rs index c2e8fa51e..3e39e317a 100644 --- a/executor/tests/rust.rs +++ b/executor/tests/rust.rs @@ -274,7 +274,7 @@ fn test_commit() { fn test_ef_io_demo_concatenates_writes() { // Demo guest reads its private input via EF `read_input`, then emits it // back as the public output via TWO `write_output` calls (split in halves). - // The COMMIT AIR concatenates the two calls; the executor's + // The COPY chip's `write` rows concatenate the two calls; the executor's // `commit_public_output` appends in the same order. let input: Vec = b"hello world!".to_vec(); run_program_and_check_public_output( diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 2d8113f5a..c1b0cec51 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -93,7 +93,7 @@ pub struct RuntimePageRange { pub const FIXED_TABLE_COUNT: usize = 6; /// How many sub-proofs each counted table contributes. Chunked chips report -/// their chunk count; the six accelerators are not chunked and report 0 or 1 +/// their chunk count; the five accelerators are not chunked and report 0 or 1 /// (see `validate`). The verifier needs this to reconstruct matching AIRs. #[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub struct TableCounts { diff --git a/prover/src/tables/copy.rs b/prover/src/tables/copy.rs index b2b8aeb21..98caeb1a8 100644 --- a/prover/src/tables/copy.rs +++ b/prover/src/tables/copy.rs @@ -1,7 +1,7 @@ //! COPY table — one streaming copy primitive for `memcpy`/`memmove`, `memset` //! and the `write` syscall. //! -//! Replaces the COMMIT table, and the DMA/DMA_SET pair that preceded it. A row copies `1` or `8` +//! Replaces the COMMIT table. A row copies `1` or `8` //! bytes from `src` to `dst` and chains through [`BusId::CopyNext`] until the row //! where `count == step`. //! diff --git a/prover/src/tables/cpu.rs b/prover/src/tables/cpu.rs index cefa4c125..1f4b33562 100644 --- a/prover/src/tables/cpu.rs +++ b/prover/src/tables/cpu.rs @@ -976,7 +976,7 @@ pub fn bus_interactions() -> Vec { } // ------------------------------------------------------------------------- - // ECALL: system-call bus (HALT/COMMIT/KECCAK receive). mult = ECALL. + // ECALL: system-call bus (HALT/COPY/KECCAK/ECSM/HINT receive). mult = ECALL. // ECALL[timestamp, rv1]. // ------------------------------------------------------------------------- interactions.push(BusInteraction::sender( diff --git a/prover/src/tables/hint.rs b/prover/src/tables/hint.rs index 1fb604bd6..aff37a4d8 100644 --- a/prover/src/tables/hint.rs +++ b/prover/src/tables/hint.rs @@ -33,7 +33,7 @@ //! sound and avoids the mixed-timestamp bookkeeping of a partial-buffer read. //! //! `mu` is constrained to a bit (`IS_BIT`, the table's only algebraic constraint) — -//! the same guard every other multiplicity-column table carries (ECSM/ECDAS/COMMIT/ +//! the same guard every other multiplicity-column table carries (ECSM/ECDAS/COPY/ //! STORE/MEMW_R). The `Ecall` bus alone does not establish it: its tuple carries the //! timestamp, a free column, so the LogUp identity pins only the *sum* of `mu` over //! the rows sharing a `(ts, syscall)` tuple to the CPU's send — it does not rule out diff --git a/prover/src/tables/keccak.rs b/prover/src/tables/keccak.rs index 7b84cbd48..1d47f0d6e 100644 --- a/prover/src/tables/keccak.rs +++ b/prover/src/tables/keccak.rs @@ -164,7 +164,7 @@ pub fn bus_interactions() -> Vec { // 1. ECALL receiver (shared bus, per spec keccak:c:output) // Payload: [ts_lo, ts_hi, syscall_lo32, syscall_hi32] in DWordWL [lo, hi] - // ordering, matching the CPU ECALL sender shared with HALT/COMMIT. + // ordering, matching the CPU ECALL sender shared with HALT/COPY/ECSM/HINT. interactions.push(BusInteraction::receiver( BusId::Ecall, Multiplicity::Column(cols::MU), diff --git a/prover/src/tables/types.rs b/prover/src/tables/types.rs index 64c56f23a..a5b0547f3 100644 --- a/prover/src/tables/types.rs +++ b/prover/src/tables/types.rs @@ -307,11 +307,11 @@ pub enum BusId { // ========================================================================= /// Instruction decode lookup Decode = 18, - /// System call handling (CPU → HALT/COMMIT for all ECALLs) + /// System call handling (CPU → HALT/COPY/KECCAK/ECSM/HINT for all ECALLs) Ecall = 19, // ID 20 is reserved for the removed CommitNextByte bus: COMMIT's per-byte // recursion moved to COPY, which chains over [`BusId::CopyNext`]. - /// COMMIT output bus: verifier computes the receiver contribution externally + /// Commit output bus (sent by COPY's `write` rows): verifier computes the receiver contribution externally /// from `VmProof.public_output` using the shared LogUp challenges Commit = 21, /// Keccak core ↔ round chip: (timestamp, round, state[200 bytes]) diff --git a/prover/src/tests/count_table_lengths_drift_tests.rs b/prover/src/tests/count_table_lengths_drift_tests.rs index d01ff3447..f8c4630a2 100644 --- a/prover/src/tests/count_table_lengths_drift_tests.rs +++ b/prover/src/tests/count_table_lengths_drift_tests.rs @@ -101,11 +101,10 @@ fn count_table_lengths_matches_traces() { } /// Runs one Rust DMA guest and asserts the sizing pass matches the built traces. -/// Each ecall has two hand-maintained replays — `collect_dma_*_ops` for -/// generation and `replay_dma_*_for_sizing` for counting — and they must agree, -/// so the fixtures cover a single chunk plus the multi-chunk / overlapping / -/// near-`MAX_DATA_ROWS` cases of `dma_memcpy_cases`, and the same schedule -/// driven through the memset table. +/// Sizing replays each ecall through `replay_copy_for_sizing`, which delegates to +/// the same `collect_copy_ops` trace generation uses; the fixtures cover a single +/// chunk plus the multi-chunk and overlapping cases of `dma_memcpy_cases`, and the +/// same schedule driven through memset. fn assert_dma_fixture_counts(elf_name: &str, syscall_number: u64) { let workspace_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() diff --git a/prover/src/tests/mod.rs b/prover/src/tests/mod.rs index a9ca30653..cd746aa5a 100644 --- a/prover/src/tests/mod.rs +++ b/prover/src/tests/mod.rs @@ -11,7 +11,6 @@ pub mod branch_constraints_tests; #[cfg(test)] pub mod bytewise_tests; #[cfg(test)] -#[cfg(test)] pub mod compute_commit_bus_offset_tests; #[cfg(test)] pub mod constraint_emit_tests; diff --git a/prover/src/tests/prove_elfs_tests.rs b/prover/src/tests/prove_elfs_tests.rs index 634d25330..a44304575 100644 --- a/prover/src/tests/prove_elfs_tests.rs +++ b/prover/src/tests/prove_elfs_tests.rs @@ -3692,7 +3692,7 @@ fn test_prove_private_input_different_values() { /// End-to-end: EF zkVM IO interface — demo guest reads its private input via /// `read_input` and emits it back through TWO `write_output` calls. The -/// COMMIT AIR's running `x254` index concatenates them; the resulting proof's +/// running `x254` index of COPY's `write` rows concatenates them; the resulting proof's /// `public_output` must equal the original input. #[test] fn test_prove_ef_io_demo_concatenates() { diff --git a/prover/src/tests/skip_empty_tables_tests.rs b/prover/src/tests/skip_empty_tables_tests.rs index 97f03ec46..f9b38fa64 100644 --- a/prover/src/tests/skip_empty_tables_tests.rs +++ b/prover/src/tests/skip_empty_tables_tests.rs @@ -535,7 +535,7 @@ fn validate_still_requires_cpu_and_the_register_file() { } /// The counts ride in the proof, so they are the prover's to choose, and the -/// sub-proof cross-check constrains only their *sum*. The six accelerators are +/// sub-proof cross-check constrains only their *sum*. The five accelerators are /// not chunked — `generate_optional` emits one table or none — so any count /// above 1 describes a table set no prover can build, and the sum alone does not /// catch it: lower another count by the same amount and the total still matches. @@ -577,7 +577,7 @@ fn an_accelerator_count_above_one_is_rejected() { ); } - // The chunked chips keep taking any count: this is a cap on the six, not a + // The chunked chips keep taking any count: this is a cap on the five, not a // cap on chunking. let mut many_mul = honest.clone(); many_mul.mul = 7; @@ -615,9 +615,9 @@ fn counts_that_wrap_have_no_total() { /// The accelerators are the ones a run most often never reaches, and each cost a /// four-row sub-proof regardless. A program with no keccak, no EC and no hint -/// ecall now carries none of the six. +/// ecall now carries none of the five. #[test] -fn a_run_without_accelerators_omits_all_six() { +fn a_run_without_accelerators_omits_all_five() { let (elf, logs, _instructions) = run_asm_elf("xori"); let mut traces = Traces::from_elf_and_logs_minimal(&elf, &logs, &Default::default(), &[]).unwrap(); diff --git a/syscalls/src/allocator.rs b/syscalls/src/allocator.rs index ccaa8ab0a..417dcee2b 100644 --- a/syscalls/src/allocator.rs +++ b/syscalls/src/allocator.rs @@ -2,7 +2,7 @@ use riscv as _; const MAX_MEMORY_SIZE: usize = 0xC000_0000; -/// The accelerated `memset` depends on this. +/// Kept for the accelerated `memset`, though its soundness no longer depends on it. /// /// COPY pins `dst = src + 8` with one 64-bit `ADD`, so a range crossing the 2^32 limb /// boundary is perfectly provable and neither the chip nor the executor refuses it From 59f6d5b7db9a9a6ee07ab3ab79954728111954c7 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Fri, 23 Oct 2026 13:31:36 -0300 Subject: [PATCH 2/2] Call COPY's Commit-syscall rows commit rows in the comments --- executor/src/vm/memory.rs | 6 +++--- executor/tests/rust.rs | 2 +- prover/src/tables/types.rs | 2 +- prover/src/tests/prove_elfs_tests.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/executor/src/vm/memory.rs b/executor/src/vm/memory.rs index ef06e1e0b..b5e6d72cf 100644 --- a/executor/src/vm/memory.rs +++ b/executor/src/vm/memory.rs @@ -39,7 +39,7 @@ impl BuildHasher for U64BuildHasher { pub type U64HashMap = HashMap; /// Total cap on public output bytes across all `commit_public_output` calls. -/// The COPY chip's `write` rows concatenate calls via the running `x254` index, so this +/// The COPY chip's commit rows concatenate calls via the running `x254` index, so this /// is enforced as a running-total budget rather than a per-call limit. pub const MAX_PUBLIC_OUTPUT_TOTAL_SIZE: u64 = 1024 * 1024; /// Maximum size of the private input memory region (in bytes). 512 MiB so a @@ -60,7 +60,7 @@ pub const PRIVATE_INPUT_LENGTH_PREFIX_BYTES: usize = size_of::(); pub struct Memory { cells: U64HashMap<[u8; 4]>, /// Bytes committed to public output via `commit_public_output`. The - /// COPY chip's `write` rows don't write to a fixed memory region (it streams bytes + /// COPY chip's commit rows don't write to a fixed memory region (it streams bytes /// onto the Commit bus by `index`), so this buffer is purely the /// executor's view used by `read_return_value` and CLI display. public_output: Vec, @@ -202,7 +202,7 @@ impl Memory { } /// Append `length` bytes from guest memory starting at `address` to the - /// public output. The COPY chip's `write` rows concatenate calls via the + /// public output. The COPY chip's commit rows concatenate calls via the /// running `x254` index, and the trace builder collects their bytes into /// `VmProof.public_output`; this method maintains the executor's view /// of the same byte stream so `read_return_value` matches. diff --git a/executor/tests/rust.rs b/executor/tests/rust.rs index 3e39e317a..597cb6b1c 100644 --- a/executor/tests/rust.rs +++ b/executor/tests/rust.rs @@ -274,7 +274,7 @@ fn test_commit() { fn test_ef_io_demo_concatenates_writes() { // Demo guest reads its private input via EF `read_input`, then emits it // back as the public output via TWO `write_output` calls (split in halves). - // The COPY chip's `write` rows concatenate the two calls; the executor's + // The COPY chip's commit rows concatenate the two calls; the executor's // `commit_public_output` appends in the same order. let input: Vec = b"hello world!".to_vec(); run_program_and_check_public_output( diff --git a/prover/src/tables/types.rs b/prover/src/tables/types.rs index a5b0547f3..24d4e9b70 100644 --- a/prover/src/tables/types.rs +++ b/prover/src/tables/types.rs @@ -311,7 +311,7 @@ pub enum BusId { Ecall = 19, // ID 20 is reserved for the removed CommitNextByte bus: COMMIT's per-byte // recursion moved to COPY, which chains over [`BusId::CopyNext`]. - /// Commit output bus (sent by COPY's `write` rows): verifier computes the receiver contribution externally + /// Commit output bus (sent by COPY's commit rows): verifier computes the receiver contribution externally /// from `VmProof.public_output` using the shared LogUp challenges Commit = 21, /// Keccak core ↔ round chip: (timestamp, round, state[200 bytes]) diff --git a/prover/src/tests/prove_elfs_tests.rs b/prover/src/tests/prove_elfs_tests.rs index a44304575..42e63e831 100644 --- a/prover/src/tests/prove_elfs_tests.rs +++ b/prover/src/tests/prove_elfs_tests.rs @@ -3692,7 +3692,7 @@ fn test_prove_private_input_different_values() { /// End-to-end: EF zkVM IO interface — demo guest reads its private input via /// `read_input` and emits it back through TWO `write_output` calls. The -/// running `x254` index of COPY's `write` rows concatenates them; the resulting proof's +/// running `x254` index of COPY's commit rows concatenates them; the resulting proof's /// `public_output` must equal the original input. #[test] fn test_prove_ef_io_demo_concatenates() {