use serde::{Deserialize, Serialize};
use crate::primitives::{BlockHeight, Timestamp};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum KillSwitchAction {
Pause,
Quarantine,
Terminate,
}
impl KillSwitchAction {
pub fn as_str(&self) -> &'static str {
match self {
KillSwitchAction::Pause => "pause",
KillSwitchAction::Quarantine => "quarantine",
KillSwitchAction::Terminate => "terminate",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct KillSwitchReceipt {
pub receipt_id: String,
pub action: KillSwitchAction,
pub agent_did: String,
pub controller_did: String,
pub reason_code: u16,
pub reason_text: Option<String>,
pub evidence_hash: Option<String>,
pub slash_bps: Option<u16>,
pub cascade: Option<bool>,
pub pause_until: Option<u64>,
pub frozen_at_block: BlockHeight,
pub timestamp: Timestamp,
}
impl KillSwitchReceipt {
pub fn is_reversible(&self) -> bool {
matches!(
self.action,
KillSwitchAction::Pause | KillSwitchAction::Quarantine
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn action_roundtrip_strings() {
assert_eq!(KillSwitchAction::Pause.as_str(), "pause");
assert_eq!(KillSwitchAction::Quarantine.as_str(), "quarantine");
assert_eq!(KillSwitchAction::Terminate.as_str(), "terminate");
}
#[test]
fn receipt_serde_roundtrip() {
let r = KillSwitchReceipt {
receipt_id: "deadbeef".to_string(),
action: KillSwitchAction::Quarantine,
agent_did: "did:tenzro:machine:alice".to_string(),
controller_did: "did:tenzro:human:bob".to_string(),
reason_code: 100,
reason_text: Some("safety".into()),
evidence_hash: None,
slash_bps: None,
cascade: None,
pause_until: None,
frozen_at_block: BlockHeight::new(42),
timestamp: Timestamp::new(1_700_000_000_000),
};
let json = serde_json::to_string(&r).unwrap();
let back: KillSwitchReceipt = serde_json::from_str(&json).unwrap();
assert_eq!(r, back);
assert!(r.is_reversible());
}
#[test]
fn terminate_is_irreversible() {
let r = KillSwitchReceipt {
receipt_id: "abc".to_string(),
action: KillSwitchAction::Terminate,
agent_did: "did:tenzro:machine:alice".to_string(),
controller_did: "did:tenzro:human:bob".to_string(),
reason_code: 200,
reason_text: None,
evidence_hash: None,
slash_bps: Some(10_000),
cascade: Some(true),
pause_until: None,
frozen_at_block: BlockHeight::new(0),
timestamp: Timestamp::new(0),
};
assert!(!r.is_reversible());
}
}