use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub use super::composition::{ShapeContract, ShapeExpr};
pub use super::kind::ContractKind;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Contract {
pub metadata: Metadata,
#[serde(default)]
pub equations: BTreeMap<String, Equation>,
#[serde(default)]
pub proof_obligations: Vec<ProofObligation>,
#[serde(default)]
pub kernel_structure: Option<KernelStructure>,
#[serde(default)]
pub simd_dispatch: BTreeMap<String, BTreeMap<String, String>>,
#[serde(default)]
pub enforcement: BTreeMap<String, EnforcementRule>,
#[serde(default)]
pub falsification_tests: Vec<FalsificationTest>,
#[serde(default)]
pub kani_harnesses: Vec<KaniHarness>,
#[serde(default)]
pub qa_gate: Option<QaGate>,
#[serde(default)]
pub verification_summary: Option<VerificationSummary>,
#[serde(default)]
pub type_invariants: Vec<TypeInvariant>,
#[serde(default)]
pub coq_spec: Option<CoqSpec>,
}
impl Contract {
pub fn is_registry(&self) -> bool {
self.metadata.registry || self.metadata.kind == ContractKind::Registry
}
pub fn kind(&self) -> ContractKind {
if self.metadata.registry && self.metadata.kind == ContractKind::Kernel {
ContractKind::Registry
} else {
self.metadata.kind
}
}
pub fn requires_proofs(&self) -> bool {
self.kind() == ContractKind::Kernel
}
pub fn provability_violations(&self) -> Vec<String> {
if !self.requires_proofs() {
return vec![];
}
let mut violations = Vec::new();
if self.proof_obligations.is_empty() {
violations.push("Kernel contract has no proof_obligations".into());
}
if self.falsification_tests.is_empty() {
violations.push("Kernel contract has no falsification_tests".into());
}
if self.kani_harnesses.is_empty() {
violations.push("Kernel contract has no kani_harnesses".into());
}
if self.falsification_tests.len() < self.proof_obligations.len() {
violations.push(format!(
"falsification_tests ({}) < proof_obligations ({})",
self.falsification_tests.len(),
self.proof_obligations.len(),
));
}
violations
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Metadata {
pub version: String,
#[serde(default)]
pub created: Option<String>,
#[serde(default)]
pub author: Option<String>,
pub description: String,
#[serde(default)]
pub references: Vec<String>,
#[serde(default)]
pub depends_on: Vec<String>,
#[serde(default)]
pub registry: bool,
#[serde(default)]
pub kind: ContractKind,
#[serde(default)]
pub enforcement_level: Option<EnforcementLevel>,
#[serde(default)]
pub locked_level: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EnforcementLevel {
Basic,
Standard,
Strict,
Proven,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Equation {
pub formula: String,
#[serde(default)]
pub domain: Option<String>,
#[serde(default)]
pub codomain: Option<String>,
#[serde(default)]
pub invariants: Vec<String>,
#[serde(default)]
pub preconditions: Vec<String>,
#[serde(default)]
pub postconditions: Vec<String>,
#[serde(default)]
pub lean_theorem: Option<String>,
#[serde(default)]
pub float_tolerance: Option<f64>,
#[serde(default)]
pub assumes: Option<ShapeContract>,
#[serde(default)]
pub guarantees: Option<ShapeContract>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProofObligation {
#[serde(rename = "type")]
pub obligation_type: ObligationType,
pub property: String,
#[serde(default)]
pub formal: Option<String>,
#[serde(default)]
pub tolerance: Option<f64>,
#[serde(default)]
pub applies_to: Option<AppliesTo>,
#[serde(default)]
pub lean: Option<LeanProof>,
#[serde(default)]
pub requires: Option<String>,
#[serde(default)]
pub applies_to_phase: Option<String>,
#[serde(default)]
pub parent_contract: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ObligationType {
#[default]
Invariant,
Equivalence,
Bound,
Monotonicity,
Idempotency,
Linearity,
Symmetry,
Associativity,
Conservation,
Ordering,
Completeness,
Soundness,
Involution,
Determinism,
Roundtrip,
#[serde(rename = "state_machine")]
StateMachine,
Classification,
Independence,
Termination,
Precondition,
Postcondition,
Frame,
#[serde(rename = "loop_invariant")]
LoopInvariant,
#[serde(rename = "loop_variant")]
LoopVariant,
#[serde(rename = "old_state")]
OldState,
Subcontract,
}
impl std::fmt::Display for ObligationType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Invariant => "invariant",
Self::Equivalence => "equivalence",
Self::Bound => "bound",
Self::Monotonicity => "monotonicity",
Self::Idempotency => "idempotency",
Self::Linearity => "linearity",
Self::Symmetry => "symmetry",
Self::Associativity => "associativity",
Self::Conservation => "conservation",
Self::Ordering => "ordering",
Self::Completeness => "completeness",
Self::Soundness => "soundness",
Self::Involution => "involution",
Self::Determinism => "determinism",
Self::Roundtrip => "roundtrip",
Self::StateMachine => "state_machine",
Self::Classification => "classification",
Self::Independence => "independence",
Self::Termination => "termination",
Self::Precondition => "precondition",
Self::Postcondition => "postcondition",
Self::Frame => "frame",
Self::LoopInvariant => "loop_invariant",
Self::LoopVariant => "loop_variant",
Self::OldState => "old_state",
Self::Subcontract => "subcontract",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AppliesTo {
All,
Scalar,
Simd,
Converter,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelStructure {
pub phases: Vec<KernelPhase>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelPhase {
pub name: String,
pub description: String,
#[serde(default)]
pub invariant: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnforcementRule {
pub description: String,
#[serde(default)]
pub check: Option<String>,
#[serde(default)]
pub severity: Option<String>,
#[serde(default)]
pub reference: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FalsificationTest {
pub id: String,
pub rule: String,
pub prediction: String,
#[serde(default)]
pub test: Option<String>,
pub if_fails: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct KaniHarness {
pub id: String,
pub obligation: String,
#[serde(default)]
pub property: Option<String>,
#[serde(default)]
pub bound: Option<u32>,
#[serde(default)]
pub strategy: Option<KaniStrategy>,
#[serde(default)]
pub solver: Option<String>,
#[serde(default)]
pub harness: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KaniStrategy {
Exhaustive,
StubFloat,
Compositional,
BoundedInt,
}
impl std::fmt::Display for KaniStrategy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Exhaustive => "exhaustive",
Self::StubFloat => "stub_float",
Self::Compositional => "compositional",
Self::BoundedInt => "bounded_int",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LeanProof {
pub theorem: String,
#[serde(default)]
pub module: Option<String>,
#[serde(default)]
pub status: LeanStatus,
#[serde(default)]
pub depends_on: Vec<String>,
#[serde(default)]
pub mathlib_imports: Vec<String>,
#[serde(default)]
pub notes: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LeanStatus {
Proved,
#[default]
Sorry,
Wip,
NotApplicable,
}
impl std::fmt::Display for LeanStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Proved => "proved",
Self::Sorry => "sorry",
Self::Wip => "wip",
Self::NotApplicable => "not-applicable",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationSummary {
pub total_obligations: u32,
#[serde(default)]
pub l2_property_tested: u32,
#[serde(default)]
pub l3_kani_proved: u32,
#[serde(default)]
pub l4_lean_proved: u32,
#[serde(default)]
pub l4_sorry_count: u32,
#[serde(default)]
pub l4_not_applicable: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QaGate {
pub id: String,
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub checks: Vec<String>,
#[serde(default)]
pub pass_criteria: Option<String>,
#[serde(default)]
pub falsification: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeInvariant {
pub name: String,
#[serde(rename = "type")]
pub type_name: String,
pub predicate: String,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoqSpec {
pub module: String,
#[serde(default)]
pub imports: Vec<String>,
#[serde(default)]
pub definitions: Vec<CoqDefinition>,
#[serde(default)]
pub obligations: Vec<CoqObligation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoqDefinition {
pub name: String,
pub statement: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoqObligation {
pub links_to: String,
pub coq_lemma: String,
#[serde(default = "coq_status_default")]
pub status: String,
}
fn coq_status_default() -> String {
"stub".to_string()
}
#[cfg(test)]
#[path = "types_tests.rs"]
mod tests;