// file: kb-pipeline-demo-scenarios/src/spl/token_2022/metadata/validation.rs // version: 3 //! Conservative validation matrix for the `0.4.8-pre.012` Token Metadata campaign. /// Maximum evidence items retained by one campaign scenario. pub const MAX_TOKEN_2022_METADATA_VALIDATION_EVIDENCE: usize = 16; /// Exact network-validation status for one Token Metadata scenario. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum Token2022MetadataValidationStatus { /// No real Devnet execution has been recorded. NotRun, /// The scenario completed with all declared evidence. Confirmed, /// The scenario is unavailable for an explicit recorded reason. Unavailable, } /// One bounded evidence item retained by the validation matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Token2022MetadataValidationEvidence { /// Stable evidence kind. pub kind: std::string::String, /// Bounded evidence value such as a signature, slot or diagnostic. pub value: std::string::String, } /// One ordered Token Metadata scenario in the canonical Devnet matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Token2022MetadataValidationScenario { /// Stable campaign step identifier. pub id: std::string::String, /// Short Token Metadata interface operation name. pub operation: std::string::String, /// Current evidence-qualified network status. pub status: crate::Token2022MetadataValidationStatus, /// Evidence kinds required before promotion to `confirmed`. pub required_evidence: std::vec::Vec, /// Bounded observed evidence. pub evidence: std::vec::Vec, } /// Canonical `0.4.8-pre.012` Token Metadata Devnet validation matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct Token2022MetadataValidationMatrix { /// Matrix schema version. pub matrix_version: u32, /// Milestone owning the campaign. pub milestone: std::string::String, /// Token-2022 program ID. pub program_id: std::string::String, /// Five scenarios in campaign order. pub scenarios: std::vec::Vec, } /// Loads and validates the canonical Token Metadata Devnet matrix. pub fn load_token_2022_metadata_validation_matrix() -> kb_core::Result { let matrix = match serde_json::from_str::( include_str!( "../../../../../test-fixtures/contract-matrices/SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json" ), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(kb_core::Error::new( "token_2022_metadata_validation_matrix_invalid_json", error.to_string(), )); }, }; return match crate::validate_token_2022_metadata_validation_matrix(&matrix) { std::result::Result::Ok(()) => std::result::Result::Ok(matrix), std::result::Result::Err(error) => std::result::Result::Err(error), }; } /// Validates campaign coverage and prevents promotion without complete evidence. pub fn validate_token_2022_metadata_validation_matrix( matrix: &crate::Token2022MetadataValidationMatrix, ) -> kb_core::Result<()> { if matrix.matrix_version != 1 || matrix.milestone != "0.4.8-pre.012" || matrix.program_id != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { return std::result::Result::Err(kb_core::Error::config( "Token-2022 metadata validation matrix identity is invalid", )); } let expected = crate::token_2022_metadata_campaign_operation_names(); if matrix.scenarios.len() != expected.len() { return std::result::Result::Err(kb_core::Error::config(format!( "Token-2022 metadata validation matrix requires exactly {} scenarios", expected.len() ))); } let mut ids = std::collections::BTreeSet::new(); for (index, scenario) in matrix.scenarios.iter().enumerate() { if !ids.insert(scenario.id.as_str()) { return std::result::Result::Err(kb_core::Error::config(format!( "duplicate Token-2022 metadata validation scenario {}", scenario.id ))); } if scenario.operation != expected[index] { return std::result::Result::Err(kb_core::Error::config(format!( "Token-2022 metadata scenario {} expected operation {}, got {}", scenario.id, expected[index], scenario.operation ))); } if scenario.required_evidence.is_empty() || scenario.required_evidence.len() > MAX_TOKEN_2022_METADATA_VALIDATION_EVIDENCE || scenario.evidence.len() > MAX_TOKEN_2022_METADATA_VALIDATION_EVIDENCE { return std::result::Result::Err(kb_core::Error::config(format!( "Token-2022 metadata scenario {} has invalid evidence bounds", scenario.id ))); } let mut required = std::collections::BTreeSet::new(); if scenario .required_evidence .iter() .any(|kind| return kind.trim().is_empty() || !required.insert(kind.as_str())) { return std::result::Result::Err(kb_core::Error::config(format!( "Token-2022 metadata scenario {} has empty or duplicate required evidence", scenario.id ))); } let mut observed = std::collections::BTreeSet::new(); for evidence in &scenario.evidence { if evidence.kind.trim().is_empty() || evidence.value.trim().is_empty() || !observed.insert(evidence.kind.as_str()) { return std::result::Result::Err(kb_core::Error::config(format!( "Token-2022 metadata scenario {} has invalid observed evidence", scenario.id ))); } } match scenario.status { crate::Token2022MetadataValidationStatus::NotRun => { if !scenario.evidence.is_empty() { return std::result::Result::Err(kb_core::Error::config(format!( "not-run Token-2022 metadata scenario {} cannot claim evidence", scenario.id ))); } }, crate::Token2022MetadataValidationStatus::Confirmed => { if scenario .required_evidence .iter() .any(|kind| return !observed.contains(kind.as_str())) { return std::result::Result::Err(kb_core::Error::config(format!( "confirmed Token-2022 metadata scenario {} is missing required evidence", scenario.id ))); } }, crate::Token2022MetadataValidationStatus::Unavailable => { if !observed.contains("unavailable_reason") { return std::result::Result::Err(kb_core::Error::config(format!( "unavailable Token-2022 metadata scenario {} requires unavailable_reason", scenario.id ))); } }, } } return std::result::Result::Ok(()); } #[cfg(test)] mod tests { #[test] fn canonical_matrix_is_closed_ordered_and_confirmed_after_network_execution() { let matrix = crate::load_token_2022_metadata_validation_matrix() .unwrap_or_else(|error| panic!("Token-2022 metadata matrix failed: {error}")); assert_eq!(matrix.scenarios.len(), 5); assert!(matrix.scenarios.iter().all(|scenario| { let observed = scenario .evidence .iter() .map(|evidence| return evidence.kind.as_str()) .collect::>(); return scenario.status == crate::Token2022MetadataValidationStatus::Confirmed && scenario .required_evidence .iter() .all(|kind| return observed.contains(kind.as_str())); })); assert_eq!( matrix .scenarios .iter() .map(|scenario| return scenario.operation.as_str()) .collect::>(), crate::token_2022_metadata_campaign_operation_names().as_slice() ); } }