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
11 changes: 10 additions & 1 deletion prover/src/continuation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ fn global_transcript(
elf_bytes: &[u8],
num_epochs: usize,
num_private_input_pages: usize,
fri_final_poly_log_degree: u8,
touched_page_bases: &[u64],
) -> DefaultTranscript<E> {
let mut transcript = DefaultTranscript::<E>::new(&[]);
Expand All @@ -118,6 +119,7 @@ fn global_transcript(
elf_bytes,
num_epochs,
num_private_input_pages,
fri_final_poly_log_degree,
touched_page_bases,
);
transcript
Expand Down Expand Up @@ -686,6 +688,7 @@ fn prove_global(
elf_bytes,
boundaries.len(),
num_private_input_pages,
opts.fri_final_poly_log_degree,
page_bases,
),
#[cfg(feature = "disk-spill")]
Expand Down Expand Up @@ -730,7 +733,13 @@ fn verify_global(
Verifier::multi_verify(
&refs,
proof,
&mut global_transcript(elf_bytes, num_epochs, num_private_input_pages, page_bases),
&mut global_transcript(
elf_bytes,
num_epochs,
num_private_input_pages,
opts.fri_final_poly_log_degree,
page_bases,
),
&FieldElement::zero(),
)
}
Expand Down
10 changes: 8 additions & 2 deletions prover/src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,27 +125,33 @@ pub(crate) fn absorb_statement(
/// Continuation domain tags. Distinct from the monolithic `DOMAIN_TAG` so a
/// monolithic proof and a continuation proof can never share a transcript prefix.
const CONTINUATION_EPOCH_TAG: &[u8] = b"LAMBDAVM_CONTINUATION_EPOCH_V2";
const CONTINUATION_GLOBAL_TAG: &[u8] = b"LAMBDAVM_CONTINUATION_GLOBAL_V1";
const CONTINUATION_GLOBAL_TAG: &[u8] = b"LAMBDAVM_CONTINUATION_GLOBAL_V2";

/// Statement bound into the cross-epoch **global** proof's transcript before
/// Phase A: the ELF (so the global proof is program-bound), the epoch count (so a
/// global proof from a run with a different number of epochs cannot be spliced in),
/// the private-input page count (so the global proof's AIR layout — which touched pages
/// are built non-preprocessed — is canonically pinned, like the monolithic path's
/// `absorb_statement`), and the touched page-base set (which GLOBAL_MEMORY tables exist).
/// `absorb_statement`), `fri_final_poly_log_degree` (which sets the FRI transcript
/// shape, exactly as the monolithic and epoch statements bind it), and the touched
/// page-base set (which GLOBAL_MEMORY tables exist).
/// Prove and verify must call this with identical arguments.
pub(crate) fn absorb_continuation_global_statement(
t: &mut impl IsTranscript<E>,
elf_bytes: &[u8],
num_epochs: usize,
num_private_input_pages: usize,
fri_final_poly_log_degree: u8,
touched_page_bases: &[u64],
) {
t.append_bytes(CONTINUATION_GLOBAL_TAG);
t.append_bytes(&elf_digest(elf_bytes));
t.append_bytes(&(num_epochs as u64).to_le_bytes());
t.append_bytes(&(num_private_input_pages as u64).to_le_bytes());

// fri_final_poly_log_degree: single byte, no endianness concern.
t.append_bytes(&[fri_final_poly_log_degree]);

// Touched page-base set: count-prefixed, each fixed-width u64. Binds the exact set
// (and order) of GLOBAL_MEMORY tables the verifier rebuilds, so a tampered list
// diverges the challenges. Prover and verifier pass the identical canonical
Expand Down
21 changes: 14 additions & 7 deletions prover/src/tests/statement_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ fn global_state(
elf: &[u8],
num_epochs: usize,
num_private_input_pages: usize,
fri_final_poly_log_degree: u8,
touched_page_bases: &[u64],
) -> [u8; 32] {
let mut t = DefaultTranscript::<E>::new(&[]);
Expand All @@ -186,38 +187,44 @@ fn global_state(
elf,
num_epochs,
num_private_input_pages,
fri_final_poly_log_degree,
touched_page_bases,
);
t.state()
}

#[test]
fn continuation_global_state_binds_program_epoch_count_pages_and_touched_set() {
let baseline = global_state(b"elf", 3, 1, &[0x1000, 0x2000]);
assert_eq!(baseline, global_state(b"elf", 3, 1, &[0x1000, 0x2000])); // deterministic
let baseline = global_state(b"elf", 3, 1, 7, &[0x1000, 0x2000]);
assert_eq!(baseline, global_state(b"elf", 3, 1, 7, &[0x1000, 0x2000])); // deterministic
assert_ne!(
baseline,
global_state(b"elf", 4, 1, &[0x1000, 0x2000]),
global_state(b"elf", 4, 1, 7, &[0x1000, 0x2000]),
"must bind epoch count"
);
assert_ne!(
baseline,
global_state(b"other-elf", 3, 1, &[0x1000, 0x2000]),
global_state(b"other-elf", 3, 1, 7, &[0x1000, 0x2000]),
"must bind the ELF"
);
assert_ne!(
baseline,
global_state(b"elf", 3, 2, &[0x1000, 0x2000]),
global_state(b"elf", 3, 2, 7, &[0x1000, 0x2000]),
"must bind the private-input page count"
);
assert_ne!(
baseline,
global_state(b"elf", 3, 1, &[0x1000, 0x3000]),
global_state(b"elf", 3, 1, 8, &[0x1000, 0x2000]),
"must bind fri_final_poly_log_degree"
);
assert_ne!(
baseline,
global_state(b"elf", 3, 1, 7, &[0x1000, 0x3000]),
"must bind the touched page-base set"
);
assert_ne!(
baseline,
global_state(b"elf", 3, 1, &[0x1000]),
global_state(b"elf", 3, 1, 7, &[0x1000]),
"must bind the touched page-base count"
);
}
Loading