// file: kb-pipeline/src/solana_token2022_validation.rs // version: 4 //! Machine-readable validation evidence contract for the Token-2022 milestone. /// Maximum number of evidence records accepted in one validation report. pub const MAX_TOKEN2022_VALIDATION_EVIDENCE: usize = 64; /// Required validation environment for one scenario. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum Token2022ValidationEnvironment { /// Deterministic offline fixtures and builder comparisons. Offline, /// A local validator under operator control. Localnet, /// Solana Devnet. Devnet, /// Scenario may run on Localnet or Devnet according to deployment availability. LocalnetOrDevnet, /// Mainnet observations without mutable execution. MainnetObservation, /// PostgreSQL persistence and replay validation. Postgres, /// Tauri application smoke validation. Tauri, } /// Exact status of one validation scenario. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum Token2022ValidationStatus { /// Scenario is declared but has not been run. NotRun, /// Scenario was simulated without submission. Simulated, /// Scenario was submitted but confirmation evidence is incomplete. Submitted, /// Scenario was confirmed and its required postconditions passed. Confirmed, /// Scenario is not available in the selected environment. Unavailable, /// Scenario ran and failed. Failed, } /// One bounded proof attached to a validation scenario. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Token2022ValidationEvidence { /// Stable evidence kind such as `signature`, `test_suite`, or `replay`. pub kind: std::string::String, /// Bounded evidence value. pub value: std::string::String, } /// One declared Token-2022 validation scenario and its observed evidence. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Token2022ValidationScenario { /// Stable scenario identifier. pub id: std::string::String, /// Validation environment. pub environment: crate::Token2022ValidationEnvironment, /// Exact observed status. pub status: crate::Token2022ValidationStatus, /// Whether canonical hydration is required. pub requires_canonical_hydration: bool, /// Whether core extraction is required. pub requires_core_extraction: bool, /// Whether decode replay is required. pub requires_decode_replay: bool, /// Whether materialization is required. pub requires_materialization: bool, /// Whether a second idempotent replay is required. pub requires_second_replay: bool, /// Ordered evidence records. pub evidence: std::vec::Vec, } /// Machine-readable validation matrix loaded from the canonical JSON document. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Token2022ValidationMatrix { /// Matrix schema version. pub matrix_version: u32, /// Milestone owning this matrix. pub milestone: std::string::String, /// Aggregate matrix status. pub status: std::string::String, /// Exact accepted status vocabulary. pub status_vocabulary: std::vec::Vec, /// Scenarios in stable roadmap order. pub scenarios: std::vec::Vec, } /// One scenario declared by the canonical validation matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Token2022ValidationMatrixScenario { /// Stable scenario identifier. pub id: std::string::String, /// Required execution environment. pub environment: crate::Token2022ValidationEnvironment, /// Exact observed status. pub status: crate::Token2022ValidationStatus, /// Evidence kinds required before this scenario may be confirmed. pub required_evidence: std::vec::Vec, /// Observed bounded evidence. #[serde(default)] pub evidence: std::vec::Vec, } /// Loads and validates the canonical Token-2022 validation matrix. pub fn load_token2022_validation_matrix() -> kb_core::Result { let parsed = match serde_json::from_str::(include_str!( "../../docs/SPL_TOKEN2022_VALIDATION_MATRIX.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_invalid_json", format!("Token-2022 validation matrix JSON is invalid: {error}"), )); }, }; if let std::result::Result::Err(error) = validate_token2022_validation_matrix(&parsed) { return std::result::Result::Err(error); } return std::result::Result::Ok(parsed); } /// Validates schema, scenario inventory, statuses, and observed evidence. pub fn validate_token2022_validation_matrix( matrix: &crate::Token2022ValidationMatrix, ) -> kb_core::Result<()> { if matrix.matrix_version != 2 || matrix.milestone != "0.4.6" { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_contract_mismatch", "Token-2022 validation matrix must use version 2 for milestone 0.4.6", )); } let expected_statuses = ["not_run", "simulated", "submitted", "confirmed", "unavailable", "failed"]; let actual_statuses = matrix .status_vocabulary .iter() .map(|status| return status.as_str()) .collect::>(); if actual_statuses.as_slice() != expected_statuses { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_status_vocabulary_mismatch", "Token-2022 validation status vocabulary differs from the compiled contract", )); } let expected_ids = [ "offline_full_regression", "token2022_public_devnet", "elgamal_registry_devnet", "confidential_transfer_localnet_or_devnet", "confidential_mint_burn_localnet_or_devnet", "permissioned_confidential_burn_localnet_or_devnet", "mainnet_observation_corpus", "postgres_double_replay", "tauri_smoke", ]; let actual_ids = matrix .scenarios .iter() .map(|scenario| return scenario.id.as_str()) .collect::>(); if actual_ids.as_slice() != expected_ids { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_scenario_inventory_mismatch", "Token-2022 validation scenario inventory or order differs from the compiled contract", )); } for scenario in &matrix.scenarios { if scenario.required_evidence.is_empty() || scenario.required_evidence.len() > crate::MAX_TOKEN2022_VALIDATION_EVIDENCE { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_required_evidence_invalid", "Every Token-2022 validation scenario must declare bounded required evidence", )); } let mut required = std::collections::BTreeSet::<&str>::new(); for evidence_kind in &scenario.required_evidence { if evidence_kind.trim().is_empty() || !required.insert(evidence_kind.as_str()) { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_required_evidence_invalid", "Required Token-2022 validation evidence must be non-empty and unique", )); } } if scenario.status == crate::Token2022ValidationStatus::Confirmed { let observed = scenario .evidence .iter() .map(|evidence| return evidence.kind.as_str()) .collect::>(); if !required.iter().all(|kind| return observed.contains(kind)) { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_confirmed_without_required_evidence", format!( "Confirmed Token-2022 validation scenario {} lacks required evidence", scenario.id ), )); } } else if matches!( scenario.status, crate::Token2022ValidationStatus::NotRun | crate::Token2022ValidationStatus::Unavailable ) && !scenario.evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_matrix_unobserved_with_evidence", "Not-run or unavailable Token-2022 scenarios must not retain observed evidence", )); } } return std::result::Result::Ok(()); } /// Complete validation report checked before milestone closure. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Token2022ValidationReport { /// Scenarios in stable roadmap order. pub scenarios: std::vec::Vec, } /// Validates that a milestone report is bounded, unique, and does not overclaim evidence. pub fn validate_token2022_validation_report( report: &crate::Token2022ValidationReport, ) -> kb_core::Result<()> { if report.scenarios.is_empty() { return std::result::Result::Err(kb_core::Error::config( "Token-2022 validation report must contain at least one scenario", )); } let mut ids = std::collections::BTreeSet::::new(); for scenario in &report.scenarios { if scenario.id.trim().is_empty() || !ids.insert(scenario.id.clone()) { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_scenario_identity_invalid", "Token-2022 validation scenario ids must be non-empty and unique", )); } if scenario.evidence.len() > crate::MAX_TOKEN2022_VALIDATION_EVIDENCE { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_evidence_limit_exceeded", format!( "Token-2022 validation accepts at most {} evidence records per scenario", crate::MAX_TOKEN2022_VALIDATION_EVIDENCE ), )); } for evidence in &scenario.evidence { if evidence.kind.trim().is_empty() || evidence.value.trim().is_empty() { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_evidence_invalid", "Token-2022 validation evidence kind and value must be non-empty", )); } } let completed = matches!( scenario.status, crate::Token2022ValidationStatus::Simulated | crate::Token2022ValidationStatus::Submitted | crate::Token2022ValidationStatus::Confirmed | crate::Token2022ValidationStatus::Failed ); if completed && scenario.evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_completed_without_evidence", "A completed Token-2022 validation scenario must retain evidence", )); } if scenario.status == crate::Token2022ValidationStatus::Confirmed && (scenario.requires_canonical_hydration || scenario.requires_core_extraction || scenario.requires_decode_replay || scenario.requires_materialization || scenario.requires_second_replay) && !has_pipeline_evidence(scenario) { return std::result::Result::Err(kb_core::Error::new( "token2022_validation_confirmed_without_pipeline_evidence", "A confirmed end-to-end Token-2022 scenario must retain hydration, extraction, replay, materialization, and idempotence evidence", )); } } return std::result::Result::Ok(()); } fn has_pipeline_evidence(scenario: &crate::Token2022ValidationScenario) -> bool { let kinds = scenario .evidence .iter() .map(|evidence| return evidence.kind.as_str()) .collect::>(); if scenario.requires_canonical_hydration && !kinds.contains("canonical_hydration") { return false; } if scenario.requires_core_extraction && !kinds.contains("core_extraction") { return false; } if scenario.requires_decode_replay && !kinds.contains("decode_replay") { return false; } if scenario.requires_materialization && !kinds.contains("materialization") { return false; } if scenario.requires_second_replay && !kinds.contains("second_replay") { return false; } return true; } #[cfg(test)] mod tests { fn scenario(status: crate::Token2022ValidationStatus) -> crate::Token2022ValidationScenario { return crate::Token2022ValidationScenario { id: "confidential_transfer_devnet".to_string(), environment: crate::Token2022ValidationEnvironment::Devnet, status, requires_canonical_hydration: true, requires_core_extraction: true, requires_decode_replay: true, requires_materialization: true, requires_second_replay: true, evidence: vec![ crate::Token2022ValidationEvidence { kind: "signature".to_string(), value: "signature".to_string(), }, crate::Token2022ValidationEvidence { kind: "canonical_hydration".to_string(), value: "1".to_string(), }, crate::Token2022ValidationEvidence { kind: "core_extraction".to_string(), value: "1".to_string(), }, crate::Token2022ValidationEvidence { kind: "decode_replay".to_string(), value: "1".to_string(), }, crate::Token2022ValidationEvidence { kind: "materialization".to_string(), value: "1".to_string(), }, crate::Token2022ValidationEvidence { kind: "second_replay".to_string(), value: "0_new_outputs".to_string(), }, ], }; } #[test] fn confirmed_end_to_end_scenario_requires_complete_pipeline_evidence() { let report = crate::Token2022ValidationReport { scenarios: vec![scenario(crate::Token2022ValidationStatus::Confirmed)], }; assert!(crate::validate_token2022_validation_report(&report).is_ok()); let mut incomplete = scenario(crate::Token2022ValidationStatus::Confirmed); incomplete.evidence.retain(|evidence| return evidence.kind != "second_replay"); assert!( crate::validate_token2022_validation_report(&crate::Token2022ValidationReport { scenarios: vec![incomplete] }) .is_err() ); } #[test] fn not_run_and_unavailable_scenarios_do_not_invent_evidence() { let mut not_run = scenario(crate::Token2022ValidationStatus::NotRun); not_run.evidence.clear(); let mut unavailable = scenario(crate::Token2022ValidationStatus::Unavailable); unavailable.id = "permissioned_burn_devnet".to_string(); unavailable.evidence.clear(); assert!( crate::validate_token2022_validation_report(&crate::Token2022ValidationReport { scenarios: vec![not_run, unavailable] }) .is_ok() ); } #[test] fn duplicate_ids_empty_evidence_and_completed_without_evidence_fail_closed() { let first = scenario(crate::Token2022ValidationStatus::Confirmed); let duplicate = first.clone(); assert!( crate::validate_token2022_validation_report(&crate::Token2022ValidationReport { scenarios: vec![first, duplicate] }) .is_err() ); let mut failed = scenario(crate::Token2022ValidationStatus::Failed); failed.evidence.clear(); assert!( crate::validate_token2022_validation_report(&crate::Token2022ValidationReport { scenarios: vec![failed] }) .is_err() ); } #[test] fn canonical_matrix_matches_compiled_inventory_and_observed_offline_evidence() { let matrix = crate::load_token2022_validation_matrix(); assert!(matrix.is_ok()); let matrix = match matrix { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"), }; assert_eq!(matrix.scenarios.len(), 9); assert_eq!(matrix.scenarios[0].status, crate::Token2022ValidationStatus::Confirmed); assert_eq!(matrix.scenarios[0].evidence.len(), 4); assert!( matrix.scenarios[1..] .iter() .all(|scenario| return scenario.status == crate::Token2022ValidationStatus::NotRun) ); } #[test] fn confirmed_matrix_scenario_requires_every_declared_evidence_kind() { let matrix = crate::load_token2022_validation_matrix(); assert!(matrix.is_ok()); let mut matrix = match matrix { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"), }; matrix.scenarios[0].evidence.retain(|evidence| return evidence.kind != "clippy"); assert!(crate::validate_token2022_validation_matrix(&matrix).is_err()); } }