-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat] Add internal verifier & root verifier program (#763)
* Add internal verifier & root verifier program * Add doc comments for public values * Add doc comments for RootVmVerifierPvs * Remove init_memory_commit from AppExecutionCommit * Address comments
- Loading branch information
1 parent
eb46470
commit 226f67b
Showing
22 changed files
with
1,162 additions
and
228 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
use ax_stark_sdk::{ | ||
ax_stark_backend::{config::Val, p3_field::AbstractField}, | ||
config::baby_bear_poseidon2::BabyBearPoseidon2Config, | ||
}; | ||
use axvm_circuit::{ | ||
arch::{ | ||
hasher::{poseidon2::vm_poseidon2_hasher, Hasher}, | ||
VmConfig, | ||
}, | ||
system::{ | ||
memory::{memory_image_to_equipartition, tree::MemoryNode}, | ||
program::trace::AxVmCommittedExe, | ||
}, | ||
}; | ||
use axvm_native_compiler::ir::DIGEST_SIZE; | ||
|
||
type SC = BabyBearPoseidon2Config; | ||
|
||
/// `AppExecutionCommit` has all the commitments users should check against the final proof. | ||
pub struct AppExecutionCommit<T> { | ||
/// Commitment of the leaf VM verifier program which commits the VmConfig of App VM. | ||
/// Internal verifier will verify `leaf_vm_verifier_commit`. | ||
pub leaf_vm_verifier_commit: [T; DIGEST_SIZE], | ||
/// Commitment of the executable. It's computed as | ||
/// compress( | ||
/// compress( | ||
/// hash(app_program_commit), | ||
/// hash(init_memory_commit) | ||
/// ), | ||
/// hash(right_pad(pc_start, 0)) | ||
/// ) | ||
/// `right_pad` example, if pc_start = 123, right_pad(pc_start, 0) = [123,0,0,0,0,0,0,0] | ||
pub exe_commit: [T; DIGEST_SIZE], | ||
} | ||
|
||
impl AppExecutionCommit<Val<SC>> { | ||
/// Users should use this function to compute `AppExecutionCommit` and check it against the final | ||
/// proof. | ||
pub fn compute( | ||
app_vm_config: &VmConfig, | ||
app_exe: &AxVmCommittedExe<SC>, | ||
leaf_vm_verifier_exe: &AxVmCommittedExe<SC>, | ||
) -> Self { | ||
assert!(app_exe.exe.program.max_num_public_values <= app_vm_config.num_public_values); | ||
let hasher = vm_poseidon2_hasher(); | ||
let memory_dimensions = app_vm_config.memory_config.memory_dimensions(); | ||
let app_program_commit: [Val<SC>; DIGEST_SIZE] = | ||
app_exe.committed_program.prover_data.commit.into(); | ||
let leaf_verifier_program_commit: [Val<SC>; DIGEST_SIZE] = leaf_vm_verifier_exe | ||
.committed_program | ||
.prover_data | ||
.commit | ||
.into(); | ||
|
||
let init_memory_commit = MemoryNode::tree_from_memory( | ||
memory_dimensions, | ||
&memory_image_to_equipartition(app_exe.exe.init_memory.clone()), | ||
&hasher, | ||
) | ||
.hash(); | ||
let mut padded_pc_start = [Val::<SC>::zero(); DIGEST_SIZE]; | ||
padded_pc_start[0] = Val::<SC>::from_canonical_u32(app_exe.exe.pc_start); | ||
let app_hash = hasher.hash(&app_program_commit); | ||
let init_memory_hash = hasher.hash(&init_memory_commit); | ||
let pc_start_hash = hasher.hash(&padded_pc_start); | ||
let compress_1 = hasher.compress(&app_hash, &init_memory_hash); | ||
let user_commit = hasher.compress(&compress_1, &pc_start_hash); | ||
|
||
Self { | ||
leaf_vm_verifier_commit: leaf_verifier_program_commit, | ||
exe_commit: user_commit, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
extern crate core; | ||
|
||
pub mod commit; | ||
pub mod config; | ||
pub mod verifier; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
use std::array; | ||
|
||
use ax_stark_sdk::ax_stark_backend::p3_field::AbstractField; | ||
use axvm_circuit::{ | ||
arch::{CONNECTOR_AIR_ID, MERKLE_AIR_ID, PROGRAM_CACHED_TRACE_INDEX}, | ||
system::{connector::VmConnectorPvs, memory::merkle::MemoryMerklePvs}, | ||
}; | ||
use axvm_native_compiler::{ir::Config, prelude::*}; | ||
use axvm_recursion::{digest::DigestVariable, vars::StarkProofVariable}; | ||
|
||
pub mod types; | ||
|
||
pub fn assert_or_assign_connector_pvs<C: Config>( | ||
builder: &mut Builder<C>, | ||
dst: &VmConnectorPvs<Felt<C::F>>, | ||
proof_idx: RVar<C::N>, | ||
proof_pvs: &VmConnectorPvs<Felt<C::F>>, | ||
) { | ||
builder.if_eq(proof_idx, RVar::zero()).then_or_else( | ||
|builder| { | ||
builder.assign(&dst.initial_pc, proof_pvs.initial_pc); | ||
}, | ||
|builder| { | ||
// assert prev.final_pc == curr.initial_pc | ||
builder.assert_felt_eq(dst.final_pc, proof_pvs.initial_pc); | ||
// assert prev.is_terminate == 0 | ||
builder.assert_felt_eq(dst.is_terminate, C::F::zero()); | ||
}, | ||
); | ||
// Update final_pc | ||
builder.assign(&dst.final_pc, proof_pvs.final_pc); | ||
// Update is_terminate | ||
builder.assign(&dst.is_terminate, proof_pvs.is_terminate); | ||
// Update exit_code | ||
builder.assign(&dst.exit_code, proof_pvs.exit_code); | ||
} | ||
|
||
pub fn assert_or_assign_memory_pvs<C: Config>( | ||
builder: &mut Builder<C>, | ||
dst: &MemoryMerklePvs<Felt<C::F>, DIGEST_SIZE>, | ||
proof_idx: RVar<C::N>, | ||
proof_pvs: &MemoryMerklePvs<Felt<C::F>, DIGEST_SIZE>, | ||
) { | ||
builder.if_eq(proof_idx, RVar::zero()).then_or_else( | ||
|builder| { | ||
builder.assign(&dst.initial_root, proof_pvs.initial_root); | ||
}, | ||
|builder| { | ||
// assert prev.final_root == curr.initial_root | ||
builder.assert_eq::<[_; DIGEST_SIZE]>(dst.final_root, proof_pvs.initial_root); | ||
}, | ||
); | ||
// Update final_root | ||
builder.assign(&dst.final_root, proof_pvs.final_root); | ||
} | ||
|
||
pub fn get_program_commit<C: Config>( | ||
builder: &mut Builder<C>, | ||
proof: &StarkProofVariable<C>, | ||
) -> [Felt<C::F>; DIGEST_SIZE] { | ||
let t_id = RVar::from(PROGRAM_CACHED_TRACE_INDEX); | ||
let commit = builder.get(&proof.commitments.main_trace, t_id); | ||
let commit = if let DigestVariable::Felt(commit) = commit { | ||
commit | ||
} else { | ||
unreachable!() | ||
}; | ||
array::from_fn(|i| builder.get(&commit, i)) | ||
} | ||
|
||
pub fn get_connector_pvs<C: Config>( | ||
builder: &mut Builder<C>, | ||
proof: &StarkProofVariable<C>, | ||
) -> VmConnectorPvs<Felt<C::F>> { | ||
let a_id = RVar::from(CONNECTOR_AIR_ID); | ||
let a_input = builder.get(&proof.per_air, a_id); | ||
let proof_pvs = &a_input.public_values; | ||
VmConnectorPvs { | ||
initial_pc: builder.get(proof_pvs, 0), | ||
final_pc: builder.get(proof_pvs, 1), | ||
exit_code: builder.get(proof_pvs, 2), | ||
is_terminate: builder.get(proof_pvs, 3), | ||
} | ||
} | ||
|
||
pub fn get_memory_pvs<C: Config>( | ||
builder: &mut Builder<C>, | ||
proof: &StarkProofVariable<C>, | ||
) -> MemoryMerklePvs<Felt<C::F>, DIGEST_SIZE> { | ||
let a_id = RVar::from(MERKLE_AIR_ID); | ||
let a_input = builder.get(&proof.per_air, a_id); | ||
MemoryMerklePvs { | ||
initial_root: array::from_fn(|i| builder.get(&a_input.public_values, i)), | ||
final_root: array::from_fn(|i| builder.get(&a_input.public_values, i + DIGEST_SIZE)), | ||
} | ||
} | ||
|
||
/// Asserts that a single segment VM exits successfully. | ||
pub fn assert_single_segment_vm_exit_successfully<C: Config>( | ||
builder: &mut Builder<C>, | ||
proof: &StarkProofVariable<C>, | ||
) { | ||
let connector_pvs = get_connector_pvs(builder, proof); | ||
// FIXME: does single segment VM program always have pc_start = 0? | ||
// Start PC should be 0 | ||
builder.assert_felt_eq(connector_pvs.initial_pc, C::F::zero()); | ||
// Terminate should be 1 | ||
builder.assert_felt_eq(connector_pvs.is_terminate, C::F::one()); | ||
// Exit code should be 0 | ||
builder.assert_felt_eq(connector_pvs.exit_code, C::F::zero()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
use std::{array, borrow::BorrowMut}; | ||
|
||
use ax_stark_sdk::ax_stark_backend::p3_field::PrimeField32; | ||
use axvm_circuit::{ | ||
circuit_derive::AlignedBorrow, | ||
system::{connector::VmConnectorPvs, memory::merkle::MemoryMerklePvs}, | ||
}; | ||
use axvm_native_compiler::prelude::*; | ||
|
||
#[derive(Debug, AlignedBorrow)] | ||
#[repr(C)] | ||
pub struct VmVerifierPvs<T> { | ||
/// The commitment of the app program. | ||
pub app_commit: [T; DIGEST_SIZE], | ||
/// The merged execution state of all the segments this circuit aggregates. | ||
pub connector: VmConnectorPvs<T>, | ||
/// The memory state before/after all the segments this circuit aggregates. | ||
pub memory: MemoryMerklePvs<T, DIGEST_SIZE>, | ||
/// The merkle root of all public values. This is only meaningful when the last segment is | ||
/// aggregated by this circuit. | ||
pub public_values_commit: [T; DIGEST_SIZE], | ||
} | ||
|
||
impl<F: PrimeField32> VmVerifierPvs<Felt<F>> { | ||
pub fn uninit<C: Config<F = F>>(builder: &mut Builder<C>) -> Self { | ||
Self { | ||
app_commit: array::from_fn(|_| builder.uninit()), | ||
connector: VmConnectorPvs { | ||
initial_pc: builder.uninit(), | ||
final_pc: builder.uninit(), | ||
exit_code: builder.uninit(), | ||
is_terminate: builder.uninit(), | ||
}, | ||
memory: MemoryMerklePvs { | ||
initial_root: array::from_fn(|_| builder.uninit()), | ||
final_root: array::from_fn(|_| builder.uninit()), | ||
}, | ||
public_values_commit: array::from_fn(|_| builder.uninit()), | ||
} | ||
} | ||
} | ||
|
||
impl<F: Default + Clone> VmVerifierPvs<Felt<F>> { | ||
pub fn flatten(self) -> Vec<Felt<F>> { | ||
let mut v = vec![Felt(0, Default::default()); VmVerifierPvs::<u8>::width()]; | ||
*v.as_mut_slice().borrow_mut() = self; | ||
v | ||
} | ||
} |
Oops, something went wrong.