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
12 changes: 12 additions & 0 deletions prover/src/continuation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,18 @@ pub fn prove_continuation(
if executor.pc() == 0 {
break;
}
// The cross-epoch ordering check (IsB20 on `fini_epoch - 1 - init_epoch`)
// only spans `local_to_global::MAX_EPOCHS` epochs. Beyond that the IsB20 bus
// cannot balance, so an honest proof is impossible — fail fast with a clear
// error instead of building an unprovable trace. The verifier already
// rejects any such proof; this is a prover-side guard for a clean message.
if index >= local_to_global::MAX_EPOCHS {
return Err(Error::InvalidContinuationEpochSize(format!(
"execution needs more than {} continuation epochs (the IsB20 cross-epoch \
ordering range); use a larger epoch size",
local_to_global::MAX_EPOCHS
)));
}
let register_init: Vec<u32> = if index == 0 {
register::register_init_from_entry_point(elf.entry_point)
} else {
Expand Down
12 changes: 11 additions & 1 deletion prover/src/tables/local_to_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ type Provenance = PagedMem<(u64, u64, u64)>;
/// (1-based) epoch label, so `init_epoch < fini_epoch` holds for genesis cells.
pub const GENESIS_EPOCH: u64 = 0;

/// Maximum number of epochs a continuation run may have.
///
/// The cross-epoch ordering check proves `init_epoch < fini_epoch` via an `IsB20`
/// (20-bit) lookup on `fini_epoch - 1 - init_epoch`. A genesis-sourced cell
/// finalized in epoch `index` (0-based) has gap `index`, so every epoch must
/// satisfy `index < 2^20`. A run needing more epochs cannot be proved — the
/// IsB20 bus would not balance — so the driver rejects it up front (see
/// `prove_continuation`).
pub const MAX_EPOCHS: u64 = 1 << 20;

/// A cell's state when an epoch first touches it.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct InitClaim {
Expand Down Expand Up @@ -462,7 +472,7 @@ pub fn collect_bitwise_from_l2g(boundaries: &[CellBoundary]) -> Vec<BitwiseOpera
// Ordering: IsB20[fini_epoch - 1 - init_epoch]. Honest rows have
// init_epoch < fini_epoch, so the difference is a small non-negative value.
let diff = b.fini.epoch - 1 - b.init.originating_epoch;
debug_assert!(diff < (1 << 20), "epoch gap exceeds IsB20 range");
debug_assert!(diff < MAX_EPOCHS, "epoch gap exceeds IsB20 range");
ops.push(BitwiseOperation::b20(
(diff & 0xFF) as u8,
((diff >> 8) & 0xFF) as u8,
Expand Down
Loading