use crate::monotone_proof::{ProofWork, proof_topology_checks, verify_proof, verify_root_blockers};
use crate::{CoverageLimits, Error, Result};
use super::evaluate::{
evaluate_coverage_plan, selected_cost, survives, validate_actions, validate_source,
};
use super::model::{
CoverageAction, CoveragePlanEvaluation, CoverageSpecification, CoverageSynthesisArtifact,
CoverageSynthesisLimits, CoverageSynthesisStatus, EvaluationClaim,
};
use super::search::{build_coverage_proof, solve_coverage_search, validate_build_inputs};
impl CoverageSynthesisArtifact {
pub fn build(
specification: CoverageSpecification,
actions: Vec<CoverageAction>,
max_activations: usize,
limits: CoverageSynthesisLimits,
) -> Result<Self> {
validate_build_inputs(&specification, &actions, limits)?;
let costs = actions.iter().map(|action| action.cost).collect::<Vec<_>>();
let search =
solve_coverage_search(&specification, &actions, &costs, max_activations, limits)?;
let before = evaluate_coverage_plan(&specification, &actions, &[], limits.coverage)?;
let after =
evaluate_coverage_plan(&specification, &actions, &search.selected, limits.coverage)?;
let built = build_coverage_proof(
&specification,
&actions,
&costs,
max_activations,
&search,
limits,
)?;
let mut artifact = Self {
specification,
actions,
max_activations,
oracle_limit: limits.max_oracle_calls,
node_limit: limits.max_search_nodes,
status: search.status,
selected: search.selected,
lower_bound_cost: search.lower_bound,
upper_bound_cost: search.upper_bound,
producer_oracle_calls: search.oracle_calls,
producer_search_nodes: search.search_nodes,
producer_cache_hits: search.cache_hits,
root_blockers: search.root_blockers,
before: EvaluationClaim::from(&before),
after: EvaluationClaim::from(&after),
proof: built.proof,
proof_work: built.work,
digest: [0; 32],
};
artifact.verify(limits)?;
artifact.digest = artifact.compute_digest()?;
Ok(artifact)
}
pub fn verify(&self, limits: CoverageSynthesisLimits) -> Result<()> {
validate_coverage_artifact_inputs(self, limits)?;
let (_, after) = checked_coverage_evaluations(self, limits.coverage)?;
let costs = self
.actions
.iter()
.map(|action| action.cost)
.collect::<Vec<_>>();
let selected_cost = selected_cost(&costs, &self.selected)?;
verify_coverage_root(self, &costs, limits)?;
verify_coverage_status(self, &costs, selected_cost, &after, limits)?;
verify_coverage_proof_checks(self)
}
pub fn specification(&self) -> &CoverageSpecification {
&self.specification
}
pub fn actions(&self) -> &[CoverageAction] {
&self.actions
}
pub fn status(&self) -> CoverageSynthesisStatus {
self.status
}
pub fn selected(&self) -> &[usize] {
&self.selected
}
pub fn lower_bound_cost(&self) -> Option<u64> {
self.lower_bound_cost
}
pub fn upper_bound_cost(&self) -> Option<u64> {
self.upper_bound_cost
}
pub fn producer_oracle_calls(&self) -> usize {
self.producer_oracle_calls
}
pub fn producer_search_nodes(&self) -> usize {
self.producer_search_nodes
}
pub fn proof_topology_checks(&self) -> usize {
self.proof_work.checks
}
pub fn proof_nodes(&self) -> usize {
self.proof_work.nodes
}
pub fn selected_failure_checks(&self) -> usize {
self.after.checks
}
pub fn minimum_witness_triangles(&self) -> Option<usize> {
self.after.minimum_witness_triangles
}
pub fn digest(&self) -> &[u8; 32] {
&self.digest
}
}
fn validate_coverage_artifact_inputs(
artifact: &CoverageSynthesisArtifact,
limits: CoverageSynthesisLimits,
) -> Result<()> {
artifact.specification.validate(limits.coverage)?;
validate_source(&artifact.specification, limits)?;
validate_actions(&artifact.specification, &artifact.actions, limits.coverage)?;
validate_coverage_result_shape(artifact)
}
fn checked_coverage_evaluations(
artifact: &CoverageSynthesisArtifact,
limits: CoverageLimits,
) -> Result<(CoveragePlanEvaluation, CoveragePlanEvaluation)> {
let before = evaluate_coverage_plan(&artifact.specification, &artifact.actions, &[], limits)?;
let after = evaluate_coverage_plan(
&artifact.specification,
&artifact.actions,
&artifact.selected,
limits,
)?;
verify_evaluation_claims(artifact, &before, &after)?;
Ok((before, after))
}
fn validate_coverage_result_shape(artifact: &CoverageSynthesisArtifact) -> Result<()> {
let invalid_selection = artifact.selected.len()
> artifact.max_activations.min(artifact.actions.len())
|| artifact
.selected
.iter()
.any(|index| *index >= artifact.actions.len())
|| artifact.selected.windows(2).any(|pair| pair[0] >= pair[1]);
if artifact.oracle_limit == 0
|| artifact.node_limit == 0
|| artifact.producer_oracle_calls > artifact.oracle_limit
|| artifact.producer_search_nodes > artifact.node_limit
|| invalid_selection
{
Err(Error::InvalidInput(
"coverage synthesis result shape or producer work is invalid".into(),
))
} else {
Ok(())
}
}
fn verify_evaluation_claims(
artifact: &CoverageSynthesisArtifact,
before: &CoveragePlanEvaluation,
after: &CoveragePlanEvaluation,
) -> Result<()> {
if artifact.before != EvaluationClaim::from(before)
|| artifact.after != EvaluationClaim::from(after)
{
Err(Error::InvalidInput(
"coverage synthesis evaluation claims differ from exact checks".into(),
))
} else {
Ok(())
}
}
fn verify_coverage_root(
artifact: &CoverageSynthesisArtifact,
costs: &[u64],
limits: CoverageSynthesisLimits,
) -> Result<()> {
let mut oracle = |selected: &[usize]| {
survives(
&artifact.specification,
&artifact.actions,
selected,
limits.coverage,
)
};
verify_root_blockers(
&artifact.root_blockers,
costs,
artifact.lower_bound_cost,
&mut oracle,
)
}
fn verify_coverage_status(
artifact: &CoverageSynthesisArtifact,
costs: &[u64],
selected_cost: u64,
after: &CoveragePlanEvaluation,
limits: CoverageSynthesisLimits,
) -> Result<()> {
match artifact.status {
CoverageSynthesisStatus::Optimal => {
verify_optimal_coverage(artifact, costs, selected_cost, after, limits)
}
CoverageSynthesisStatus::Infeasible => {
verify_infeasible_coverage(artifact, costs, after, limits)
}
CoverageSynthesisStatus::SearchIncomplete => {
verify_incomplete_coverage(artifact, selected_cost, after)
}
}
}
fn verify_optimal_coverage(
artifact: &CoverageSynthesisArtifact,
costs: &[u64],
selected_cost: u64,
after: &CoveragePlanEvaluation,
limits: CoverageSynthesisLimits,
) -> Result<()> {
if !after.criterion_holds
|| artifact.lower_bound_cost != Some(selected_cost)
|| artifact.upper_bound_cost != Some(selected_cost)
{
return Err(Error::InvalidInput(
"optimal coverage result has an invalid incumbent or bound".into(),
));
}
verify_checked_coverage_proof(
artifact,
costs,
Some(selected_cost),
limits,
"optimal coverage result has no proof tree",
)
}
fn verify_infeasible_coverage(
artifact: &CoverageSynthesisArtifact,
costs: &[u64],
after: &CoveragePlanEvaluation,
limits: CoverageSynthesisLimits,
) -> Result<()> {
if !artifact.selected.is_empty()
|| artifact.lower_bound_cost.is_some()
|| artifact.upper_bound_cost.is_some()
|| after.criterion_holds
{
return Err(Error::InvalidInput(
"infeasible coverage result has an incumbent or finite bound".into(),
));
}
verify_checked_coverage_proof(
artifact,
costs,
None,
limits,
"infeasible coverage result has no proof tree",
)
}
fn verify_incomplete_coverage(
artifact: &CoverageSynthesisArtifact,
selected_cost: u64,
after: &CoveragePlanEvaluation,
) -> Result<()> {
let invalid_gap = artifact
.lower_bound_cost
.zip(artifact.upper_bound_cost)
.is_some_and(|(lower, upper)| lower > upper);
if artifact.proof.is_some()
|| artifact.proof_work != ProofWork::default()
|| artifact.upper_bound_cost.is_some() != after.criterion_holds
|| artifact
.upper_bound_cost
.is_some_and(|cost| cost != selected_cost)
|| invalid_gap
{
Err(Error::InvalidInput(
"incomplete coverage result has an invalid gap".into(),
))
} else {
Ok(())
}
}
fn verify_checked_coverage_proof(
artifact: &CoverageSynthesisArtifact,
costs: &[u64],
cutoff: Option<u64>,
limits: CoverageSynthesisLimits,
missing_message: &str,
) -> Result<()> {
let proof = artifact
.proof
.as_ref()
.ok_or_else(|| Error::InvalidInput(missing_message.into()))?;
let mut oracle = |selected: &[usize]| {
survives(
&artifact.specification,
&artifact.actions,
selected,
limits.coverage,
)
};
let work = verify_proof(
proof,
costs,
artifact.max_activations.min(artifact.actions.len()),
cutoff,
limits.proof(),
&mut oracle,
)?;
if work != artifact.proof_work {
Err(Error::InvalidInput(
"coverage proof work differs from the checked tree".into(),
))
} else {
Ok(())
}
}
fn verify_coverage_proof_checks(artifact: &CoverageSynthesisArtifact) -> Result<()> {
let wrong = artifact
.proof
.as_ref()
.is_some_and(|proof| proof_topology_checks(proof) != artifact.proof_work.checks);
if wrong {
Err(Error::InvalidInput(
"coverage proof check count differs from its tree".into(),
))
} else {
Ok(())
}
}