Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6e3ad65
docs(security): specify selective L2 sizing
quangvdao Aug 6, 2026
5f6f8db
docs(security): link selective L2 draft
quangvdao Aug 6, 2026
d86586f
security(sis): add physical norm foundations
quangvdao Aug 6, 2026
ed6c62f
feat(protocol): bind selective L2 proof shape
quangvdao Aug 6, 2026
395cbc7
feat(protocol): prove physical L2 fold norms
quangvdao Aug 6, 2026
92e7aee
feat(protocol): fuse physical L2 range proof
quangvdao Aug 6, 2026
0a7993d
feat(planner): select measured L2 fold routes
quangvdao Aug 7, 2026
fd68594
test(protocol): harden selective L2 fold path
quangvdao Aug 7, 2026
3f0931d
docs(security): document selective L2 fold sizing
quangvdao Aug 7, 2026
00a7a0c
refactor(protocol): isolate Linf fold grinding
quangvdao Aug 7, 2026
b09ab0a
fix(security): close selective L2 audit gaps
quangvdao Aug 7, 2026
c064889
docs(security): reconcile selective L2 lifecycle
quangvdao Aug 7, 2026
2280003
style(security): satisfy strict Clippy graphs
quangvdao Aug 7, 2026
4465e58
fix(security): close remote selective L2 gaps
quangvdao Aug 7, 2026
8edff34
fix(profile): report physical L2 proof costs
quangvdao Aug 7, 2026
b320182
fix(planner): bound selective L2 rollout
quangvdao Aug 7, 2026
a15d792
fix(profile): preserve failed benchmark reports
quangvdao Aug 7, 2026
dd0a09b
docs(security): complete selective L2 acceptance
quangvdao Aug 7, 2026
b7232a0
merge(main): integrate signed-sparse cutover
quangvdao Aug 7, 2026
e2adafb
refactor(l2): reuse canonical fold machinery
quangvdao Aug 7, 2026
5cce751
merge(main): integrate adaptive dimensions
quangvdao Aug 8, 2026
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions book/src/how/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,5 @@ norm. Keep the fold-reprice correction explicit.
- Paper §3.12 `sec:batched-soundness` (`def:batched-weak-opening`, `lem:batched-weak-binding`, `prop:committed-fold-price`).
- `specs/weak-binding-norm-fix.md` (fold reprice — keep the correction section).
- `specs/fold-linf-rejection.md` (fold digit-count tightening).
- `specs/selective-l2-fold-security-sizing.md` (active physical norm correction
and optional L2 route).
29 changes: 19 additions & 10 deletions crates/akita-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ pub fn policy_of<Cfg: CommitmentConfig>() -> PlannerPolicy {
sis_modulus_profile: Cfg::sis_modulus_profile(),
sis_security_policy: akita_types::DEFAULT_SIS_SECURITY_POLICY,
sis_table_digest: akita_types::sis::SisTableDigest::CURRENT,
sis_l2_table_digest: akita_types::SisL2TableDigest::CURRENT,
selective_l2_fold_caps: Cfg::selective_l2_fold_caps(),
ring_subfield_norm_bound: Cfg::ring_subfield_embedding_norm_bound(),
claim_ext_degree: Cfg::EXT_DEGREE,
chal_ext_degree: Cfg::EXT_DEGREE,
Expand Down Expand Up @@ -241,6 +243,11 @@ pub trait CommitmentConfig: Clone + Send + Sync + 'static {
/// Exact SIS modulus profile used by security-floor lookups.
fn sis_modulus_profile() -> SisModulusProfileId;

/// Measured later-fold L2 caps admitted as additional planner candidates.
fn selective_l2_fold_caps() -> &'static [akita_schedules::SelectiveL2FoldCap] {
&[]
}

/// Prove that the concrete base field has exactly the modulus named by
/// the SIS profile. Runtime callers use this before table lookup so a
/// synthetic or miswired field cannot silently inherit a nearby profile.
Expand Down Expand Up @@ -560,7 +567,7 @@ mod tests {
#[cfg(test)]
mod sis_schedule_width_audit {
use super::*;
use akita_types::sis::min_secure_rank;
use akita_types::sis::{min_secure_l2_rank, min_secure_rank, InnerCommitSecurityRoute};

pub(super) fn assert_schedule_stays_within_audited_sis_widths(
schedule: &FoldSchedule,
Expand All @@ -577,10 +584,13 @@ mod sis_schedule_width_audit {
{
let d = u32::try_from(lp.d_a()).expect("ring dimension fits in u32");

let a_rank = min_secure_rank(
lp.inner_commit_matrix.sis_table_key(),
u64::try_from(lp.inner_width()).expect("inner width should fit in u64"),
)
let width = u64::try_from(lp.inner_width()).expect("inner width should fit in u64");
let a_rank = match lp.inner_commit_matrix.security_route() {
InnerCommitSecurityRoute::Linf(key) => min_secure_rank(key, width),
InnerCommitSecurityRoute::L2 { table_key, .. } => {
min_secure_l2_rank(table_key, width)
}
}
.unwrap_or_else(|| {
panic!(
"missing audited A-row SIS width for D={d}, num_vars={num_vars}, level={level_idx}, lb={}, width={}",
Expand Down Expand Up @@ -690,11 +700,10 @@ mod fp128_policy_tests {
let schedule =
SmallCfg::get_params_for_prove(&opening_batch).expect("small-field schedule");
let root_params = &schedule.root.params.final_group.commitment;
assert!(
root_params.inner_commit_matrix.coeff_linf_bound()
>= root_params.outer_commit_matrix.coeff_linf_bound() * 2,
"A-role L-infinity bound should include the psi norm bound"
);
assert!(root_params
.inner_commit_matrix
.coeff_linf_bound()
.is_some_and(|bound| bound > 0));
}

#[test]
Expand Down
102 changes: 102 additions & 0 deletions crates/akita-config/src/proof_optimized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,100 @@ pub(crate) const PROOF_OPTIMIZED_LOG_BASIS_MAX: u32 = 6;
/// fallback.
pub const STANDARD_ONEHOT_CHUNK_SIZE: usize = 256;

const FP128_D64_ONEHOT_L2_CAPS: &[akita_schedules::SelectiveL2FoldCap] = &[
akita_schedules::SelectiveL2FoldCap {
fold_level: 3,
input_witness_len: 948_672,
physical_response_len: 65_536,
fold_basis: 16,
fold_digit_count: 3,
response_l2_sq_cap: 1 << 29,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 4,
input_witness_len: 419_328,
physical_response_len: 65_536,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 29,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 5,
input_witness_len: 223_040,
physical_response_len: 32_768,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 32,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 6,
input_witness_len: 135_936,
physical_response_len: 16_384,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 32,
},
];

const FP32_D128_ONEHOT_L2_CAPS: &[akita_schedules::SelectiveL2FoldCap] = &[
akita_schedules::SelectiveL2FoldCap {
fold_level: 3,
input_witness_len: 386_560,
physical_response_len: 32_768,
fold_basis: 16,
fold_digit_count: 3,
response_l2_sq_cap: 1 << 29,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 4,
input_witness_len: 252_416,
physical_response_len: 32_768,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 29,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 5,
input_witness_len: 130_816,
physical_response_len: 16_384,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 31,
},
];

const FP64_D128_ONEHOT_L2_CAPS: &[akita_schedules::SelectiveL2FoldCap] = &[
akita_schedules::SelectiveL2FoldCap {
fold_level: 3,
input_witness_len: 282_240,
physical_response_len: 32_768,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 32,
},
akita_schedules::SelectiveL2FoldCap {
fold_level: 4,
input_witness_len: 160_384,
physical_response_len: 32_768,
fold_basis: 64,
fold_digit_count: 2,
response_l2_sq_cap: 1 << 31,
},
];

fn selective_l2_fold_caps<Cfg: 'static>() -> &'static [akita_schedules::SelectiveL2FoldCap] {
let config = TypeId::of::<Cfg>();
if config == TypeId::of::<fp128::D64OneHot>() {
FP128_D64_ONEHOT_L2_CAPS
} else if config == TypeId::of::<fp32::D128OneHot>() {
FP32_D128_ONEHOT_L2_CAPS
} else if config == TypeId::of::<fp64::D128OneHot>() {
FP64_D128_ONEHOT_L2_CAPS
} else {
&[]
}
}

/// Bound setup preprocessing work before schedule resolution.
///
/// This is a verifier-facing allocation/CPU guard for untrusted serialized
Expand Down Expand Up @@ -493,6 +587,10 @@ macro_rules! impl_proof_optimized_preset {
$family
}

fn selective_l2_fold_caps() -> &'static [akita_schedules::SelectiveL2FoldCap] {
$crate::proof_optimized::selective_l2_fold_caps::<Self>()
}

fn setup_matrix_capacity(
max_num_vars: usize,
max_num_batched_polys: usize,
Expand Down Expand Up @@ -570,6 +668,10 @@ macro_rules! impl_proof_optimized_preset {
$family
}

fn selective_l2_fold_caps() -> &'static [akita_schedules::SelectiveL2FoldCap] {
$crate::proof_optimized::selective_l2_fold_caps::<Self>()
}

fn setup_matrix_capacity(
max_num_vars: usize,
max_num_batched_polys: usize,
Expand Down
11 changes: 9 additions & 2 deletions crates/akita-config/tests/runtime_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,16 @@ fn resolved_row_audit_rejects_low_rank_root_d_and_terminal_a() {
.witness
.inner_commit_matrix = akita_types::InnerCommitMatrixParams::new_unchecked(
matrix.security_policy(),
matrix.sis_table_key().table_digest,
matrix
.sis_table_key()
.expect("terminal test matrix is L infinity")
.table_digest,
matrix.sis_modulus_profile(),
0,
matrix.input_width(),
matrix.coeff_linf_bound(),
matrix
.coeff_linf_bound()
.expect("terminal test matrix is L infinity"),
matrix.ring_dimension(),
);
assert_mutated_row_is_rejected::<Cfg>(profiles, low_rank_terminal);
Expand Down Expand Up @@ -339,6 +344,8 @@ fn assert_policy_matches_cfg<Cfg: CommitmentConfig>() {
sis_modulus_profile: Cfg::sis_modulus_profile(),
sis_security_policy: akita_types::DEFAULT_SIS_SECURITY_POLICY,
sis_table_digest: akita_types::SisTableDigest::CURRENT,
sis_l2_table_digest: akita_types::SisL2TableDigest::CURRENT,
selective_l2_fold_caps: Cfg::selective_l2_fold_caps(),
ring_subfield_norm_bound: Cfg::ring_subfield_embedding_norm_bound(),
claim_ext_degree: Cfg::EXT_DEGREE,
chal_ext_degree: Cfg::EXT_DEGREE,
Expand Down
1 change: 1 addition & 0 deletions crates/akita-pcs/benches/setup_index_weight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fn make_case_with_shape(
level_params
.inner_commit_matrix
.sis_table_key()
.expect("benchmark setup matrix is L infinity")
.table_digest,
level_params.inner_commit_matrix.sis_modulus_profile(),
n_a,
Expand Down
34 changes: 34 additions & 0 deletions crates/akita-pcs/examples/profile/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,15 @@ pub(crate) fn emit_runtime_schedule_summary(
setup_prefix.map_or(0, |prefix| prefix.natural_len);
let setup_prefix_padded_field_elements =
setup_prefix.map_or(0, |prefix| prefix.n_prefix().unwrap_or(0));
let security_route = lp.inner_commit_matrix.security_route();
let (response_l2_sq_cap, norm_proof_shape) = match security_route {
akita_types::InnerCommitSecurityRoute::Linf(_) => (None, None),
akita_types::InnerCommitSecurityRoute::L2 {
response_l2_sq_cap,
norm_proof_shape,
..
} => (Some(response_l2_sq_cap), Some(norm_proof_shape)),
};
tracing::info!(
label,
level = level_idx,
Expand All @@ -397,6 +406,9 @@ pub(crate) fn emit_runtime_schedule_summary(
n_a = lp.inner_commit_matrix.output_rank(),
n_b = lp.outer_commit_matrix.output_rank(),
n_d = lp.open_commit_matrix.output_rank(),
security_route = ?security_route,
response_l2_sq_cap = ?response_l2_sq_cap,
norm_proof_shape = ?norm_proof_shape,
challenge_l1_mass = lp.challenge_l1_mass(),
log_basis_inner = lp.log_basis_inner,
log_basis_outer = lp.log_basis_outer,
Expand Down Expand Up @@ -569,6 +581,24 @@ where
.sum::<usize>();
let stage1_range_image_evaluation_size =
stage1.range_image_evaluation.serialized_size(Compress::No);
let (stage1_norm_proof_size, response_l2_sq) =
stage1.norm_proof.as_ref().map_or((0, None), |norm| {
(
norm.response_l2_sq.serialized_size(Compress::No)
+ norm
.subclaims
.iter()
.map(|claim| claim.serialized_size(Compress::No))
.sum::<usize>()
+ norm
.virtual_evaluations
.iter()
.map(|evaluation| evaluation.serialized_size(Compress::No))
.sum::<usize>()
+ norm.sumcheck.serialized_size(Compress::No),
Some(norm.response_l2_sq),
)
});
let stage2_sumcheck_size = stage2_intermediate
.sumcheck_proof
.serialized_size(Compress::No);
Expand Down Expand Up @@ -597,6 +627,8 @@ where
stage1_sumcheck_bytes = stage1_sumcheck_size,
stage1_interstage_claims_bytes = stage1_interstage_claims_size,
stage1_range_image_evaluation_bytes = stage1_range_image_evaluation_size,
stage1_norm_proof_bytes = stage1_norm_proof_size,
response_l2_sq = ?response_l2_sq,
stage2_sumcheck_bytes = stage2_sumcheck_size,
stage3_sumcheck_bytes = stage3_sumcheck_size,
next_w_payload_bytes = next_w_payload_size,
Expand All @@ -611,6 +643,7 @@ where
eprintln!(
"[{label}] stage1_range_image_evaluation={stage1_range_image_evaluation_size} bytes"
);
eprintln!("[{label}] stage1_norm_proof={stage1_norm_proof_size} bytes");
eprintln!("[{label}] stage2_sumcheck={stage2_sumcheck_size} bytes");
eprintln!("[{label}] stage3_sumcheck={stage3_sumcheck_size} bytes");
eprintln!(
Expand All @@ -627,6 +660,7 @@ where
+ stage1_sumcheck_size
+ stage1_interstage_claims_size
+ stage1_range_image_evaluation_size
+ stage1_norm_proof_size
+ stage2_sumcheck_size
+ stage3_sumcheck_size
+ next_w_payload_size
Expand Down
21 changes: 15 additions & 6 deletions crates/akita-pcs/src/scheme/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,18 @@ fn expected_same_point_batched_shape(
.expect("commitment payload geometry")
.transmitted_coefficients()
};
let root_stage1 = DigitRangePlan::new(1usize << root_params.log_basis_open)
.expect("scheduled root range basis")
.proof_shapes_for_route(
root_rounds,
root_params.inner_commit_matrix.security_route(),
)
.expect("scheduled root Stage 1 shape");
let root_shape = LevelProofShape {
extension_opening_reduction: None,
opening_payload_coeffs: opening_payload_coeffs(root_params),
stage1_stages: DigitRangePlan::new(1usize << root_params.log_basis_open)
.expect("scheduled root range basis")
.stage_shapes(root_rounds),
stage1_stages: root_stage1.0,
stage1_norm: root_stage1.1,
stage2_sumcheck_proof: vec![3; root_rounds],
stage3_sumcheck: None,
next_witness_binding: match root_successor {
Expand All @@ -191,12 +197,15 @@ fn expected_same_point_batched_shape(
let level_params = &step.params.witness;
let output_witness_len = step.output_witness_len;
let rounds = batched_shape_rounds(level_params.d_a(), output_witness_len);
let stage1 = DigitRangePlan::new(1usize << level_params.log_basis_open)
.expect("scheduled range basis")
.proof_shapes_for_route(rounds, level_params.inner_commit_matrix.security_route())
.expect("scheduled Stage 1 shape");
recursive_folds.push(LevelProofShape {
extension_opening_reduction: None,
opening_payload_coeffs: opening_payload_coeffs(level_params),
stage1_stages: DigitRangePlan::new(1usize << level_params.log_basis_open)
.expect("scheduled range basis")
.stage_shapes(rounds),
stage1_stages: stage1.0,
stage1_norm: stage1.1,
stage2_sumcheck_proof: vec![3; rounds],
stage3_sumcheck: None,
next_witness_binding: match schedule.recursive_folds.get(index + 1) {
Expand Down
Loading
Loading