use sha2::{Digest, Sha256};
use crate::monotone_search::{SearchLimits, minimize_antitone};
use crate::{CohomologySpace, Error, Result};
use super::model::{
CohomologyInterventionArtifact, CohomologyInterventionCandidate, CohomologyInterventionLimits,
CohomologyInterventionScenario, CohomologyInterventionStatus,
};
use super::oracle::{TopologyOracle, map_status};
use super::validation::{
validate_claim_shape, validate_decoded_artifact, validate_edits, validate_problem,
validate_root_blocker_claim, validate_status_claim,
};
use super::wire::{
Reader, decode_candidates, decode_header, decode_prefix, decode_proof_data, decode_scenarios,
decode_search_data, decode_trailer, encode_candidates, encode_header, encode_prefix,
encode_proof_data, encode_scenarios, encode_search_data, validate_artifact_size,
};
impl CohomologyInterventionArtifact {
#[allow(clippy::too_many_arguments)]
pub fn build(
vertex_count: usize,
dimension: usize,
scale: f64,
modulus: u32,
scenarios: &[CohomologyInterventionScenario],
candidates: &[CohomologyInterventionCandidate],
max_edits: usize,
limits: CohomologyInterventionLimits,
) -> Result<Self> {
Self::from_parts(
vertex_count,
dimension,
scale,
modulus,
scenarios.to_vec(),
candidates.to_vec(),
max_edits,
limits.max_oracle_calls,
limits.max_search_nodes,
limits,
)
}
#[allow(clippy::too_many_arguments)]
pub(super) fn from_parts(
vertex_count: usize,
dimension: usize,
scale: f64,
modulus: u32,
scenarios: Vec<CohomologyInterventionScenario>,
candidates: Vec<CohomologyInterventionCandidate>,
max_edits: usize,
oracle_limit: usize,
node_limit: usize,
limits: CohomologyInterventionLimits,
) -> Result<Self> {
validate_problem(
vertex_count,
scale,
&scenarios,
&candidates,
max_edits,
oracle_limit,
node_limit,
limits,
)?;
let oracle = TopologyOracle::build(
vertex_count,
dimension,
scale,
modulus,
&scenarios,
&candidates,
limits.cohomology,
)?;
let before_ranks = oracle
.spaces
.iter()
.map(CohomologySpace::rank)
.collect::<Vec<_>>();
let costs = candidates
.iter()
.map(|candidate| candidate.cost)
.collect::<Vec<_>>();
let search = minimize_antitone(
&costs,
max_edits,
SearchLimits {
oracle_calls: oracle_limit,
search_nodes: node_limit,
},
|selected| oracle.survives(selected),
)?;
let after_ranks = if search.selected.is_empty() {
before_ranks.clone()
} else {
oracle.ranks(&search.selected)?
};
let edits = search
.selected
.iter()
.map(|position| candidates[*position])
.collect();
let mut artifact = Self {
vertex_count,
dimension,
scale,
modulus,
scenarios,
candidates,
max_edits,
oracle_limit,
node_limit,
status: map_status(search.status),
edits,
lower_bound_cost: search.lower_bound,
upper_bound_cost: search.upper_bound,
oracle_calls: search.oracle_calls,
search_nodes: search.search_nodes,
cache_hits: search.cache_hits,
root_blockers: search.root_blockers,
root_blocker_bound: search.root_blocker_bound,
before_ranks,
after_ranks,
digest: [0; 32],
};
artifact.validate_claim(limits)?;
artifact.digest = artifact.compute_digest()?;
Ok(artifact)
}
pub fn verify(&self, limits: CohomologyInterventionLimits) -> Result<()> {
if self.oracle_limit > limits.max_oracle_calls || self.node_limit > limits.max_search_nodes
{
return Err(Error::InvalidInput(
"cohomology intervention search limits exceed verifier limits".into(),
));
}
let rebuilt = Self::from_parts(
self.vertex_count,
self.dimension,
self.scale,
self.modulus,
self.scenarios.clone(),
self.candidates.clone(),
self.max_edits,
self.oracle_limit,
self.node_limit,
limits,
)?;
if rebuilt != *self {
return Err(Error::InvalidInput(
"cohomology intervention differs from weighted verification".into(),
));
}
Ok(())
}
pub fn encode(&self, limits: CohomologyInterventionLimits) -> Result<Vec<u8>> {
self.verify(limits)?;
let mut output = self.encode_payload()?;
output.extend_from_slice(&self.digest);
if output.len() > limits.max_bytes {
return Err(Error::InvalidInput(
"cohomology intervention artifact exceeds its byte limit".into(),
));
}
Ok(output)
}
pub fn decode(bytes: &[u8], limits: CohomologyInterventionLimits) -> Result<Self> {
validate_artifact_size(bytes, limits)?;
let mut reader = Reader::new(bytes);
decode_prefix(&mut reader)?;
let header = decode_header(&mut reader)?;
let scenarios = decode_scenarios(&mut reader, limits)?;
let candidates = decode_candidates(&mut reader, limits)?;
let search = decode_search_data(&mut reader, candidates.len())?;
let edits = search
.edit_indices
.iter()
.map(|position| candidates[*position])
.collect();
let proof = decode_proof_data(&mut reader, candidates.len(), scenarios.len(), limits)?;
let digest = decode_trailer(&mut reader)?;
let artifact = Self {
vertex_count: header.vertex_count,
dimension: header.dimension,
scale: header.scale,
modulus: header.modulus,
scenarios,
candidates,
max_edits: search.max_edits,
oracle_limit: search.oracle_limit,
node_limit: search.node_limit,
status: search.status,
edits,
lower_bound_cost: search.lower_bound_cost,
upper_bound_cost: search.upper_bound_cost,
oracle_calls: search.oracle_calls,
search_nodes: search.search_nodes,
cache_hits: search.cache_hits,
root_blockers: proof.root_blockers,
root_blocker_bound: proof.root_blocker_bound,
before_ranks: proof.before_ranks,
after_ranks: proof.after_ranks,
digest,
};
validate_decoded_artifact(&artifact, bytes, limits)?;
Ok(artifact)
}
pub fn vertex_count(&self) -> usize {
self.vertex_count
}
pub fn dimension(&self) -> usize {
self.dimension
}
pub fn scale(&self) -> f64 {
self.scale
}
pub fn modulus(&self) -> u32 {
self.modulus
}
pub fn scenarios(&self) -> &[CohomologyInterventionScenario] {
&self.scenarios
}
pub fn candidates(&self) -> &[CohomologyInterventionCandidate] {
&self.candidates
}
pub fn max_edits(&self) -> usize {
self.max_edits
}
pub fn status(&self) -> CohomologyInterventionStatus {
self.status
}
pub fn edits(&self) -> &[CohomologyInterventionCandidate] {
&self.edits
}
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 oracle_calls(&self) -> usize {
self.oracle_calls
}
pub fn search_nodes(&self) -> usize {
self.search_nodes
}
pub fn cache_hits(&self) -> usize {
self.cache_hits
}
pub fn root_blockers(&self) -> &[Vec<usize>] {
&self.root_blockers
}
pub fn root_blocker_bound(&self) -> u64 {
self.root_blocker_bound
}
pub fn before_ranks(&self) -> &[usize] {
&self.before_ranks
}
pub fn after_ranks(&self) -> &[usize] {
&self.after_ranks
}
pub fn digest(&self) -> &[u8; 32] {
&self.digest
}
pub(super) fn validate_claim(&self, limits: CohomologyInterventionLimits) -> Result<()> {
validate_problem(
self.vertex_count,
self.scale,
&self.scenarios,
&self.candidates,
self.max_edits,
self.oracle_limit,
self.node_limit,
limits,
)?;
validate_claim_shape(self)?;
validate_edits(self)?;
validate_root_blocker_claim(self, limits)?;
validate_status_claim(self)
}
pub(super) fn compute_digest(&self) -> Result<[u8; 32]> {
let mut hash = Sha256::new();
hash.update(b"holos-cohomology-intervention-v2");
hash.update(self.encode_payload()?);
Ok(hash.finalize().into())
}
pub(super) fn encode_payload(&self) -> Result<Vec<u8>> {
let mut output = Vec::new();
encode_prefix(&mut output);
encode_header(&mut output, self)?;
encode_scenarios(&mut output, &self.scenarios)?;
encode_candidates(&mut output, &self.candidates)?;
encode_search_data(&mut output, self)?;
encode_proof_data(&mut output, self)?;
Ok(output)
}
}