use serde::{Deserialize, Serialize};
use tatara_lisp::DeriveTataraDomain;
use crate::perf::{earned_tier, Ceiling, ProofTier, Technique};
use crate::SpecError;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PerturbationAxis {
Representation,
RedundantWrite,
ForceOrder,
Resolution,
PartialShape,
Lifetime,
}
impl PerturbationAxis {
#[must_use]
pub fn is_always_risky(self) -> bool {
matches!(
self,
PerturbationAxis::ForceOrder
| PerturbationAxis::Resolution
| PerturbationAxis::PartialShape
| PerturbationAxis::Lifetime
)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ByteRisk {
ByteSafe,
ByteRisky,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum GatingMethod {
DifferentialOracle,
VerifyMode,
ShadowCanary,
Metamorphic,
SingleByteCheck,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PromotionStatus {
DarkGated,
Measured,
Verified,
Promoted,
Discarded,
Rejected,
}
#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone)]
#[tatara(keyword = "defdarkside-lever")]
pub struct DarkSideLever {
pub name: String,
#[serde(default)]
pub flag: String,
pub technique: Technique,
pub axis: PerturbationAxis,
#[serde(rename = "byteRisk")]
pub byte_risk: ByteRisk,
pub attacks: String,
#[serde(rename = "costShare", default)]
pub cost_share: Option<f32>,
pub gate: GatingMethod,
pub status: PromotionStatus,
#[serde(default)]
pub backstop: String,
pub ceiling: Ceiling,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum DarkHonesty {
TierOverclaim,
RiskyGatedBySingleCheck,
RiskyPromotedWithoutDifferential,
PromotedWithoutBackstop,
PromotedWithoutCeiling,
PromotedOnWeakGate,
RejectedWithoutCeiling,
}
impl DarkSideLever {
#[must_use]
pub fn earned_tier(&self) -> ProofTier {
earned_tier(self.technique)
}
#[must_use]
pub fn honesty_violation(&self) -> Option<DarkHonesty> {
if self.byte_risk == ByteRisk::ByteSafe
&& (self.axis.is_always_risky() || self.earned_tier() != ProofTier::ByteSufficient)
{
return Some(DarkHonesty::TierOverclaim);
}
if self.byte_risk == ByteRisk::ByteRisky && self.gate == GatingMethod::SingleByteCheck {
return Some(DarkHonesty::RiskyGatedBySingleCheck);
}
if self.status == PromotionStatus::Promoted {
if self.byte_risk == ByteRisk::ByteRisky
&& self.gate != GatingMethod::DifferentialOracle
{
return Some(DarkHonesty::RiskyPromotedWithoutDifferential);
}
if self.gate == GatingMethod::Metamorphic {
return Some(DarkHonesty::PromotedOnWeakGate);
}
if self.backstop.trim().is_empty() {
return Some(DarkHonesty::PromotedWithoutBackstop);
}
if self.ceiling == Ceiling::NotApplicable {
return Some(DarkHonesty::PromotedWithoutCeiling);
}
}
if self.status == PromotionStatus::Rejected && self.ceiling == Ceiling::NotApplicable {
return Some(DarkHonesty::RejectedWithoutCeiling);
}
None
}
#[must_use]
pub fn is_honest(&self) -> bool {
self.honesty_violation().is_none()
}
}
const CANONICAL_DARKSIDE_LISP: &str = include_str!("../specs/darkside.lisp");
pub fn load_canonical() -> Result<Vec<DarkSideLever>, SpecError> {
let levers = crate::loader::load_all::<DarkSideLever>(CANONICAL_DARKSIDE_LISP)?;
for lever in &levers {
if let Some(v) = lever.honesty_violation() {
return Err(SpecError::Interp {
phase: "darkside::honesty".to_string(),
message: format!(
"dark-side lever `{}` REFUSED: {:?} (axis {:?}, byte-risk {:?}, gate {:?}, \
status {:?}, earned tier {:?})",
lever.name,
v,
lever.axis,
lever.byte_risk,
lever.gate,
lever.status,
lever.earned_tier()
),
});
}
}
Ok(levers)
}
#[cfg(test)]
mod tests {
use super::{
load_canonical, ByteRisk, DarkHonesty, DarkSideLever, GatingMethod, PerturbationAxis,
PromotionStatus,
};
use crate::perf::{Ceiling, Technique};
fn lever(axis: PerturbationAxis, byte_risk: ByteRisk, technique: Technique) -> DarkSideLever {
DarkSideLever {
name: "t".into(),
flag: "SUI_T".into(),
technique,
axis,
byte_risk,
attacks: "x".into(),
cost_share: None,
gate: GatingMethod::DifferentialOracle,
status: PromotionStatus::DarkGated,
backstop: String::new(),
ceiling: Ceiling::PartialCorpus,
}
}
#[test]
fn bytesafe_on_a_risky_axis_is_a_tier_overclaim() {
let l = lever(PerturbationAxis::ForceOrder, ByteRisk::ByteSafe, Technique::ReprSwap);
assert_eq!(l.honesty_violation(), Some(DarkHonesty::TierOverclaim));
}
#[test]
fn bytesafe_with_a_non_bytesufficient_technique_is_a_tier_overclaim() {
let l = lever(
PerturbationAxis::Representation,
ByteRisk::ByteSafe,
Technique::ResolutionChange,
);
assert_eq!(l.honesty_violation(), Some(DarkHonesty::TierOverclaim));
}
#[test]
fn bytesafe_repr_swap_on_representation_is_honest() {
let l = lever(
PerturbationAxis::Representation,
ByteRisk::ByteSafe,
Technique::ReprSwap,
);
assert!(l.is_honest(), "{:?}", l.honesty_violation());
}
#[test]
fn risky_gated_by_single_check_is_caught() {
let mut l = lever(PerturbationAxis::Resolution, ByteRisk::ByteRisky, Technique::ResolutionChange);
l.gate = GatingMethod::SingleByteCheck;
assert_eq!(l.honesty_violation(), Some(DarkHonesty::RiskyGatedBySingleCheck));
}
#[test]
fn promoted_without_backstop_is_caught() {
let mut l = lever(PerturbationAxis::Representation, ByteRisk::ByteRisky, Technique::ReprSwap);
l.status = PromotionStatus::Promoted;
l.gate = GatingMethod::DifferentialOracle;
l.backstop = String::new();
assert_eq!(l.honesty_violation(), Some(DarkHonesty::PromotedWithoutBackstop));
}
#[test]
fn promoted_risky_without_differential_is_caught() {
let mut l = lever(PerturbationAxis::ForceOrder, ByteRisk::ByteRisky, Technique::ForceOrderChange);
l.status = PromotionStatus::Promoted;
l.gate = GatingMethod::VerifyMode;
l.backstop = "runaway-force-depth".into();
assert_eq!(
l.honesty_violation(),
Some(DarkHonesty::RiskyPromotedWithoutDifferential)
);
}
#[test]
fn a_fully_evidenced_promotion_is_honest() {
let mut l = lever(PerturbationAxis::Representation, ByteRisk::ByteRisky, Technique::ReprSwap);
l.status = PromotionStatus::Promoted;
l.gate = GatingMethod::DifferentialOracle;
l.backstop = "ir-fallback-to-walker".into();
l.ceiling = Ceiling::PartialCorpus;
assert!(l.is_honest(), "{:?}", l.honesty_violation());
}
#[test]
fn canonical_catalog_loads_and_every_row_is_honest() {
let levers = load_canonical().expect("canonical dark-side catalog must load + be honest");
assert!(levers.len() >= 3, "expected the catalog to carry the M0 levers");
for l in &levers {
assert!(l.is_honest(), "row `{}` dishonest: {:?}", l.name, l.honesty_violation());
}
}
}