use alloc::vec;
use alloc::vec::Vec;
use p3_challenger::{CanObserve, CanSampleUniformBits, FieldChallenger, GrindingChallenger};
use p3_commit::MultilinearPcs;
use p3_field::ExtensionField;
use p3_multilinear_util::point::Point;
use p3_security::{CandidateSet, ErrorBits, SecurityTerm};
use crate::table::{OpeningEvals, OpeningProtocol};
#[derive(Clone, Debug)]
pub struct PrescribedOpeningSecurity {
pub terms: Vec<SecurityTerm>,
pub log2_max_candidates: f64,
}
impl PrescribedOpeningSecurity {
#[must_use]
pub fn single(label: &'static str, error: ErrorBits, log2_max_candidates: f64) -> Self {
Self {
terms: vec![SecurityTerm::new(label, error)],
log2_max_candidates,
}
}
#[must_use]
pub fn candidates(&self) -> Option<CandidateSet> {
CandidateSet::from_log2(self.log2_max_candidates)
}
pub fn charge_reduction(&mut self, term: SecurityTerm) {
let unusable = SecurityTerm {
bits: ErrorBits::from_log2(0.0),
..term
};
let charged = self
.candidates()
.map_or(unusable, |candidates| term.over_candidates(candidates));
self.terms.push(charged);
}
#[must_use]
pub fn error(&self) -> ErrorBits {
ErrorBits::sum(&self.terms.iter().map(|term| term.bits).collect::<Vec<_>>())
}
}
pub trait PrescribedPointPcs<Challenge, Challenger>: MultilinearPcs<Challenge, Challenger>
where
Challenge: ExtensionField<Self::Val>,
Challenger: FieldChallenger<Self::Val>
+ GrindingChallenger<Witness = Self::Val>
+ CanSampleUniformBits<Self::Val>
+ CanObserve<Self::Commitment>,
{
fn prescribed_security(
&self,
_protocol: &OpeningProtocol,
) -> Option<PrescribedOpeningSecurity> {
None
}
fn open_at(
&self,
prover_data: Self::ProverData,
protocol: &OpeningProtocol,
points: &[Point<Challenge>],
challenger: &mut Challenger,
) -> Result<Self::Proof, Self::ProverError>;
fn verify_at(
&self,
commitment: &Self::Commitment,
proof: &Self::Proof,
protocol: &OpeningProtocol,
points: &[Point<Challenge>],
challenger: &mut Challenger,
) -> Result<Vec<OpeningEvals<Challenge>>, Self::Error>;
}
#[cfg(test)]
mod tests {
use p3_security::{CandidateSet, ErrorBits, SecurityTerm};
use super::PrescribedOpeningSecurity;
fn evidence(log2: f64) -> PrescribedOpeningSecurity {
PrescribedOpeningSecurity {
terms: alloc::vec::Vec::new(),
log2_max_candidates: log2,
}
}
#[test]
fn a_reduction_pays_the_union_bound_over_the_candidate_set() {
let mut security = evidence(4.0);
security.charge_reduction(SecurityTerm::new("r", ErrorBits::from_log2(100.0)));
assert_eq!(security.terms[0].bits.bits(), 96.0);
assert_eq!(security.terms[0].label, "r");
security.charge_reduction(SecurityTerm::new("weak", ErrorBits::from_log2(3.0)));
assert_eq!(security.terms[1].bits.bits(), 0.0);
}
#[test]
fn unique_decoding_leaves_a_reduction_at_its_own_strength() {
let mut security = evidence(0.0);
security.charge_reduction(SecurityTerm::new("r", ErrorBits::from_log2(100.0)));
assert_eq!(security.terms[0].bits.bits(), 100.0);
assert_eq!(security.candidates(), Some(CandidateSet::UNIQUE));
}
#[test]
fn a_charge_forwards_the_candidate_count_to_the_layer_above() {
let mut inner = evidence(4.0);
inner.charge_reduction(SecurityTerm::new("inner", ErrorBits::from_log2(100.0)));
assert_eq!(inner.log2_max_candidates, 4.0);
let mut outer = inner.clone();
outer.charge_reduction(SecurityTerm::new("outer", ErrorBits::from_log2(100.0)));
assert_eq!(outer.log2_max_candidates, 4.0);
assert_eq!(outer.terms[0].bits.bits(), 96.0);
assert_eq!(outer.terms[1].bits.bits(), 96.0);
}
#[test]
fn the_layer_above_never_charges_a_term_the_layer_below_already_charged() {
let mut security = evidence(4.0);
security.charge_reduction(SecurityTerm::new("inner", ErrorBits::from_log2(100.0)));
let after_inner = security.terms.clone();
security.charge_reduction(SecurityTerm::new("outer", ErrorBits::from_log2(100.0)));
assert_eq!(security.terms[..1], after_inner[..]);
assert_eq!(security.terms[0].bits.bits(), 96.0);
assert_ne!(security.terms[0].bits.bits(), 92.0);
assert_eq!(
security.error(),
ErrorBits::sum(&[ErrorBits::from_log2(96.0), ErrorBits::from_log2(96.0)])
);
}
#[test]
fn a_count_that_names_no_set_leaves_a_reduction_with_no_bound() {
for count in [-1.0, f64::INFINITY, f64::NAN] {
let mut security = evidence(count);
assert_eq!(security.candidates(), None);
security.charge_reduction(SecurityTerm::new("r", ErrorBits::from_log2(100.0)));
assert_eq!(security.terms[0].bits.bits(), 0.0);
assert_eq!(security.terms[0].label, "r");
}
}
}