use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConsciousnessAttestation {
pub tier: u8,
pub proof_bytes: Vec<u8>,
pub score_commitment: [u8; 32],
pub generated_at: u64,
pub verifier_signature: Option<Vec<u8>>,
pub verifier_pubkey: Option<Vec<u8>>,
}
pub const MAX_CONSCIOUSNESS_PROOF_SIZE: usize = 50_000;
pub const ATTESTATION_VALIDITY_SECS: u64 = 86_400;
impl ConsciousnessAttestation {
pub fn validate_structure(&self) -> Result<(), String> {
if self.proof_bytes.is_empty() {
return Err("Empty proof bytes".to_string());
}
if self.proof_bytes.len() > MAX_CONSCIOUSNESS_PROOF_SIZE {
return Err(format!(
"Proof too large: {} > {}",
self.proof_bytes.len(),
MAX_CONSCIOUSNESS_PROOF_SIZE
));
}
if self.score_commitment == [0u8; 32] {
return Err("Zero commitment".to_string());
}
if self.tier > 4 {
return Err(format!("Invalid tier: {} (max 4)", self.tier));
}
Ok(())
}
pub fn is_verified(&self) -> bool {
self.verifier_signature.is_some() && self.verifier_pubkey.is_some()
}
pub fn is_expired(&self, current_time: u64) -> bool {
current_time > self.generated_at + ATTESTATION_VALIDITY_SECS
}
pub fn validate_with_freshness(&self, epoch: u64) -> Result<(), String> {
if self.is_expired(epoch) {
return Err(format!(
"attestation expired (generated_at={}, now={}, validity={}s)",
self.generated_at, epoch, ATTESTATION_VALIDITY_SECS
));
}
if self.generated_at > epoch.saturating_add(60) {
return Err(format!(
"attestation dated in the future (generated_at={}, now={})",
self.generated_at, epoch
));
}
Ok(())
}
pub fn tier_name(&self) -> &'static str {
match self.tier {
0 => "Observer",
1 => "Participant",
2 => "Citizen",
3 => "Steward",
4 => "Guardian",
_ => "Unknown",
}
}
pub fn min_threshold(&self) -> f64 {
match self.tier {
0 => 0.0,
1 => 0.2,
2 => 0.3,
3 => 0.4,
4 => 0.6,
_ => 1.0,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VerifyConsciousnessInput {
pub attestation: ConsciousnessAttestation,
pub agent_did: String,
pub action_description: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct VerifyConsciousnessOutput {
pub structure_valid: bool,
pub stark_verified: Option<bool>,
pub tier: u8,
pub expired: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_attestation() {
let att = ConsciousnessAttestation {
tier: 3, proof_bytes: vec![1, 2, 3, 4], score_commitment: [0xAA; 32],
generated_at: 1700000000,
verifier_signature: None,
verifier_pubkey: None,
};
assert!(att.validate_structure().is_ok());
assert_eq!(att.tier_name(), "Steward");
assert!((att.min_threshold() - 0.4).abs() < f64::EPSILON);
assert!(!att.is_verified());
}
#[test]
fn test_empty_proof_rejected() {
let att = ConsciousnessAttestation {
tier: 1,
proof_bytes: vec![],
score_commitment: [0xBB; 32],
generated_at: 0,
verifier_signature: None,
verifier_pubkey: None,
};
assert!(att.validate_structure().is_err());
}
#[test]
fn test_oversized_proof_rejected() {
let att = ConsciousnessAttestation {
tier: 1,
proof_bytes: vec![0; MAX_CONSCIOUSNESS_PROOF_SIZE + 1],
score_commitment: [0xCC; 32],
generated_at: 0,
verifier_signature: None,
verifier_pubkey: None,
};
assert!(att.validate_structure().is_err());
}
#[test]
fn test_expiration() {
let att = ConsciousnessAttestation {
tier: 2,
proof_bytes: vec![1],
score_commitment: [0xDD; 32],
generated_at: 1000,
verifier_signature: None,
verifier_pubkey: None,
};
assert!(!att.is_expired(1000 + ATTESTATION_VALIDITY_SECS - 1));
assert!(att.is_expired(1000 + ATTESTATION_VALIDITY_SECS + 1));
}
}