Skip to content
Merged
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 docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Provable security and conjectured security](./cryptography/security.md)
- [Lookup argument](./cryptography/lookup.md)
- [Virtual machine](./virtual_machine/introduction.md)
- [Continuations design](./continuations_design.md)

## Getting started

Expand Down
2 changes: 1 addition & 1 deletion docs/continuations_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ The integrated `prove_and_verify_continuation` is now a thin wrapper
likewise split into `prove_epoch` + `verify_epoch`.

The bundle is prover-supplied and therefore **untrusted**. Per epoch it carries the
`MultiProof`, the `public_output` slice, `table_counts`, `page_configs`,
`MultiProof`, the `public_output` slice, `table_counts`,
`num_private_input_pages`, `runtime_page_ranges`, the bound `reg_fini` (`R_{i+1}`),
the epoch `l2g_root`, and the touched-cell `boundary`; plus the global `MultiProof`
and the `private_inputs`. Everything the integrated path reused from prover memory
Expand Down
6 changes: 3 additions & 3 deletions executor/src/vm/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ pub struct ExecutionResult {
/// Size of each log chunk - balances memory usage vs callback overhead
const CHUNK_SIZE: usize = 100_000;

/// Default number of cycles (instructions) per continuation epoch.
pub const DEFAULT_EPOCH_SIZE: usize = 100_000;

/// Result of executing one continuation epoch: the logs produced during the
/// epoch and the VM state at the epoch boundary. The boundary state is the
/// starting state of the next epoch.
Expand Down Expand Up @@ -157,6 +154,9 @@ impl Executor {
/// cycles. Each epoch captures its logs and the VM state at the epoch
/// boundary, which is the starting state of the next epoch. Consumes the
/// executor.
///
/// Test/bench helper — the production continuation prover streams epochs via
/// `resume_with_limit` directly.
pub fn run_epochs(mut self, epoch_size: usize) -> Result<Vec<EpochExecution>, ExecutorError> {
assert!(epoch_size > 0, "epoch_size must be greater than zero");

Expand Down
30 changes: 26 additions & 4 deletions prover/src/continuation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,8 +1088,30 @@ mod tests {
// The bundle's `boundary` field is used only to rebuild the global AIRs' touched-
// PAGE set (genesis is recomputed from the ELF). The cross-epoch memory values
// live in the committed L2G traces, tied to the epoch proofs by
// `verify_l2g_commitment_binding` (exercised by the reorder test). Tampering a
// boundary value is therefore inconsequential; omitting/adding a touched page is
// caught by the GlobalMemory bus (unmatched fini / air count mismatch). So there
// is no meaningful "tamper a boundary value" negative test.
// `verify_l2g_commitment_binding` (exercised by test_split_verify_rejects_tampered_l2g_root
// below). Tampering a boundary value is therefore inconsequential; omitting/adding
// a touched page is caught by the GlobalMemory bus (unmatched fini / air count
// mismatch). So there is no meaningful "tamper a boundary value" negative test.

// Negative: corrupting an epoch's claimed L2G table root must be rejected —
// `verify_l2g_commitment_binding` compares each epoch's `l2g_root` against the
// corresponding sub-proof root in the global proof, so a mismatched root causes
// the binding to fail. Guards the L2G root↔global commitment binding.
#[test]
fn test_split_verify_rejects_tampered_l2g_root() {
let _ = env_logger::builder().is_test(true).try_init();
let elf_bytes = asm_elf_bytes("all_loadstore_32");
let mut bundle =
prove_continuation(&elf_bytes, &[], 8, &ProofOptions::default_test_options()).unwrap();
assert!(
bundle.epochs.len() >= 2,
"need multiple epochs to exercise the binding"
);
bundle.epochs[0].l2g_root[0] ^= 0xFF;
assert!(
verify_continuation(&elf_bytes, &bundle, &ProofOptions::default_test_options())
.unwrap()
.is_none()
);
}
}
3 changes: 2 additions & 1 deletion prover/src/tables/global_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
//! | init_epoch | Epoch | Genesis sentinel (always `GENESIS_EPOCH`) |
//! | fini | Byte | Value after the last touching epoch |
//! | fini_epoch | Epoch | Last touching epoch (`GENESIS_EPOCH` if untouched) |
//! | fini_timestamp | DWordWL | Last access timestamp (0 if untouched) |
//! | fini_timestamp_lo | Word | Last access timestamp low word (0 if untouched) |
//! | fini_timestamp_hi | Word | Last access timestamp high word (0 if untouched) |
//!
//! Virtual: `address = page_base + offset`, `page_base` constant per instance.
//!
Expand Down
18 changes: 15 additions & 3 deletions prover/src/tables/trace_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,12 @@ impl RegisterState {
}
Self {
regs,
index_register: (init.get(&508).copied().unwrap_or(0), 1),
index_register: (
init.get(&register::register_base_address(254))
.copied()
.unwrap_or(0),
1,
),
pc_register: (word(510) | (word(511) << 32), 1),
}
}
Expand Down Expand Up @@ -1069,8 +1074,15 @@ fn collect_commit_memw_ops(
let old_value = [old_index as u64, 0, 0, 0, 0, 0, 0, 0];
let new_value = [new_index as u64, 0, 0, 0, 0, 0, 0, 0];
let old_timestamps = [old_ts, 0, 0, 0, 0, 0, 0, 0];
let memw_op = MemwOperation::new(true, 508, new_value, ts, 1, true)
.with_old(old_value, old_timestamps);
let memw_op = MemwOperation::new(
true,
register::register_base_address(254),
new_value,
ts,
1,
true,
)
.with_old(old_value, old_timestamps);
memw_ops.push(memw_op);
register_state.write_index(new_index, ts);
}
Expand Down
Loading
Loading