// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_validation.rs // version: 4 //! Machine-readable Metaplex Token Metadata validation contract. /// Maximum evidence entries accepted by one Metaplex validation scenario. pub const MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE: usize = 64; /// Exact validation status for one Metaplex scenario. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum MetaplexTokenMetadataValidationStatus { /// Scenario has not been executed. NotRun, /// Scenario passed deterministic synthetic validation. SyntheticValidated, /// Scenario was simulated on Devnet or Testnet. Simulated, /// Scenario was submitted but confirmation evidence is incomplete. Submitted, /// Scenario was confirmed and all postconditions passed. Confirmed, /// Scenario is unavailable in the selected environment. Unavailable, /// Scenario ran and failed. Failed, } /// One bounded validation evidence entry. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct MetaplexTokenMetadataValidationEvidence { /// Stable evidence kind. pub kind: std::string::String, /// Bounded evidence value. pub value: std::string::String, } /// One scenario declared by the canonical Metaplex validation matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataValidationMatrixScenario { /// Stable scenario identifier. pub id: std::string::String, /// Required scenario mode. pub mode: crate::MetaplexTokenMetadataScenarioMode, /// Exact validation status. pub status: crate::MetaplexTokenMetadataValidationStatus, /// Evidence kinds required before confirmation. pub required_evidence: std::vec::Vec, /// Observed evidence. #[serde(default)] pub evidence: std::vec::Vec, } /// Canonical Metaplex validation matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataValidationMatrix { /// Matrix schema version. pub matrix_version: u32, /// Owning milestone. pub milestone: std::string::String, /// Ordered validation scenarios. pub scenarios: std::vec::Vec, } /// Transaction location covered by one cross-validation case. #[derive( Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize, )] #[serde(rename_all = "snake_case")] pub enum MetaplexTokenMetadataTransactionPath { /// Top-level instruction. Outer, /// Cross-program invocation. Cpi, } /// Expected transaction outcome for one cross-validation case. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum MetaplexTokenMetadataTransactionOutcome { /// Successful transaction path. Success, /// Failed or rejected transaction path. Failed, } /// Stable failure category exercised by one negative case. #[derive( Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize, )] #[serde(rename_all = "snake_case")] pub enum MetaplexTokenMetadataFailureClass { /// Transaction failed before committed observations could be emitted. TransactionFailed, /// Program ID mismatch. ProgramId, /// Account owner mismatch. Owner, /// Program-derived address mismatch. Pda, /// Positional account or flag mismatch. Accounts, /// Truncated instruction payload. PayloadTruncated, /// Forbidden trailing instruction bytes. PayloadSuffix, /// Unknown instruction discriminant. UnknownDiscriminant, /// Conflicting Metaplex and Token-2022 metadata sources. MetadataSourceConflict, } /// One case in the closed Metaplex cross-validation corpus. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataCrossValidationCase { /// Stable case identifier. pub id: std::string::String, /// Outer or CPI transaction path. pub path: crate::MetaplexTokenMetadataTransactionPath, /// Expected transaction outcome. pub outcome: crate::MetaplexTokenMetadataTransactionOutcome, /// Optional asset family for successful corpus cases. pub asset_family: std::option::Option, /// Optional stable negative-case category. pub failure_class: std::option::Option, /// Whether status changes require actual RPC evidence. pub requires_network_evidence: bool, /// Exact observed validation status. pub status: crate::MetaplexTokenMetadataValidationStatus, /// Evidence kinds required before the declared status is accepted. pub required_evidence: std::vec::Vec, /// Bounded observed evidence. #[serde(default)] pub evidence: std::vec::Vec, } /// Closed Metaplex cross-validation corpus for `0.4.7-pre.010`. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataCrossValidationMatrix { /// Matrix schema version. pub matrix_version: u32, /// Exact owning prerelease. pub milestone: std::string::String, /// Ordered validation cases. pub cases: std::vec::Vec, } /// Loads and validates the closed Metaplex cross-validation corpus. pub fn load_metaplex_token_metadata_cross_validation_matrix() -> kb_core::Result { let parsed = match serde_json::from_str::( include_str!( "../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_CROSS_VALIDATION_MATRIX.json" ), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_matrix_invalid_json", format!("Metaplex cross-validation matrix JSON is invalid: {error}"), )); }, }; if let std::result::Result::Err(error) = validate_metaplex_token_metadata_cross_validation_matrix(&parsed) { return std::result::Result::Err(error); } return std::result::Result::Ok(parsed); } /// Validates the closed Metaplex cross-validation corpus and evidence claims. pub fn validate_metaplex_token_metadata_cross_validation_matrix( matrix: &crate::MetaplexTokenMetadataCrossValidationMatrix, ) -> kb_core::Result<()> { if matrix.matrix_version != 1 || matrix.milestone != "0.4.7-pre.010" { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_matrix_contract_mismatch", "Metaplex cross-validation matrix must use version 1 for 0.4.7-pre.010", )); } let mut ids = std::collections::BTreeSet::<&str>::new(); for case in &matrix.cases { if !ids.insert(case.id.as_str()) || case.required_evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_case_invalid", "Metaplex cross-validation cases must be unique and declare evidence", )); } if case.evidence.len() > crate::MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_evidence_limit_exceeded", "Metaplex cross-validation evidence exceeds the compiled bound", )); } if case.outcome == crate::MetaplexTokenMetadataTransactionOutcome::Success && case.asset_family.is_none() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_success_without_asset_family", "Successful Metaplex corpus cases must identify an asset family", )); } if case.outcome == crate::MetaplexTokenMetadataTransactionOutcome::Failed && case.failure_class.is_none() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_failure_without_class", "Failed Metaplex corpus cases must identify a failure class", )); } if matches!( case.status, crate::MetaplexTokenMetadataValidationStatus::NotRun | crate::MetaplexTokenMetadataValidationStatus::Unavailable ) && !case.evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_unobserved_with_evidence", "Not-run or unavailable cross-validation cases must not contain evidence", )); } if case.requires_network_evidence && matches!( case.status, crate::MetaplexTokenMetadataValidationStatus::SyntheticValidated ) { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_network_overdeclared", "Network cases cannot be declared validated from synthetic evidence", )); } if matches!( case.status, crate::MetaplexTokenMetadataValidationStatus::Simulated | crate::MetaplexTokenMetadataValidationStatus::Submitted | crate::MetaplexTokenMetadataValidationStatus::Confirmed ) { let observed = case .evidence .iter() .map(|evidence| return evidence.kind.as_str()) .collect::>(); if !case .required_evidence .iter() .all(|kind| return observed.contains(kind.as_str())) { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_cross_validation_status_without_required_evidence", "Observed Metaplex network status lacks required evidence", )); } } } return std::result::Result::Ok(()); } /// Loads and validates the canonical Metaplex validation matrix. pub fn load_metaplex_token_metadata_validation_matrix() -> kb_core::Result { let parsed = match serde_json::from_str::(include_str!( "../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_VALIDATION_MATRIX.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_matrix_invalid_json", format!("Metaplex validation matrix JSON is invalid: {error}"), )); }, }; if let std::result::Result::Err(error) = validate_metaplex_token_metadata_validation_matrix(&parsed) { return std::result::Result::Err(error); } return std::result::Result::Ok(parsed); } /// Validates the canonical Metaplex validation matrix. pub fn validate_metaplex_token_metadata_validation_matrix( matrix: &crate::MetaplexTokenMetadataValidationMatrix, ) -> kb_core::Result<()> { if matrix.matrix_version != 1 || matrix.milestone != "0.4.7" { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_matrix_contract_mismatch", "Metaplex validation matrix must use version 1 for milestone 0.4.7", )); } let expected = crate::metaplex_token_metadata_synthetic_scenarios() .iter() .map(|scenario| return scenario.id.clone()) .collect::>(); let actual = matrix .scenarios .iter() .map(|scenario| return scenario.id.clone()) .collect::>(); if actual != expected { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_matrix_inventory_mismatch", "Metaplex validation matrix inventory differs from the compiled scenario inventory", )); } let mut ids = std::collections::BTreeSet::<&str>::new(); for scenario in &matrix.scenarios { if !ids.insert(scenario.id.as_str()) || scenario.required_evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_matrix_scenario_invalid", "Metaplex validation scenarios must be unique and declare required evidence", )); } if scenario.evidence.len() > crate::MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_evidence_limit_exceeded", "Metaplex validation evidence exceeds the compiled bound", )); } if matches!( scenario.status, crate::MetaplexTokenMetadataValidationStatus::NotRun | crate::MetaplexTokenMetadataValidationStatus::Unavailable ) && !scenario.evidence.is_empty() { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_unobserved_with_evidence", "Not-run or unavailable Metaplex scenarios must not contain evidence", )); } if scenario.status == crate::MetaplexTokenMetadataValidationStatus::Confirmed { let observed = scenario .evidence .iter() .map(|evidence| return evidence.kind.as_str()) .collect::>(); if !scenario .required_evidence .iter() .all(|kind| return observed.contains(kind.as_str())) { return std::result::Result::Err(kb_core::Error::new( "metaplex_token_metadata_validation_confirmed_without_required_evidence", "Confirmed Metaplex scenario lacks required evidence", )); } } } return std::result::Result::Ok(()); } #[cfg(test)] mod tests { #[test] fn canonical_matrix_is_machine_readable_and_matches_synthetic_inventory() { let result = crate::load_metaplex_token_metadata_validation_matrix(); assert!(result.is_ok()); if let std::result::Result::Ok(matrix) = result { assert_eq!(matrix.scenarios.len(), 5); } } #[test] fn confirmed_scenario_requires_every_declared_evidence_kind() { let result = crate::load_metaplex_token_metadata_validation_matrix(); assert!(result.is_ok()); if let std::result::Result::Ok(mut matrix) = result { matrix.scenarios[0].status = crate::MetaplexTokenMetadataValidationStatus::Confirmed; assert!(crate::validate_metaplex_token_metadata_validation_matrix(&matrix).is_err()); matrix.scenarios[0].evidence = matrix.scenarios[0] .required_evidence .iter() .map(|kind| { return crate::MetaplexTokenMetadataValidationEvidence { kind: kind.clone(), value: "synthetic-proof".to_string(), }; }) .collect(); assert!(crate::validate_metaplex_token_metadata_validation_matrix(&matrix).is_ok()); } } #[test] fn cross_validation_corpus_is_closed_and_covers_every_required_dimension() { let result = crate::load_metaplex_token_metadata_cross_validation_matrix(); assert!(result.is_ok()); if let std::result::Result::Ok(matrix) = result { assert_eq!(matrix.cases.len(), 19); let paths = matrix .cases .iter() .map(|case| return case.path) .collect::>( ); assert_eq!(paths.len(), 2); let families = matrix .cases .iter() .filter_map(|case| return case.asset_family) .collect::>( ); assert_eq!(families.len(), 5); let failures = matrix .cases .iter() .filter_map(|case| return case.failure_class) .collect::>( ); assert_eq!(failures.len(), 9); assert_eq!( matrix.cases.iter().filter(|case| return case.requires_network_evidence).count(), 3 ); } } #[test] fn network_cases_cannot_be_promoted_from_synthetic_or_incomplete_evidence() { let result = crate::load_metaplex_token_metadata_cross_validation_matrix(); assert!(result.is_ok()); if let std::result::Result::Ok(mut matrix) = result { let network = matrix.cases.iter_mut().find(|case| return case.requires_network_evidence); assert!(network.is_some()); if let std::option::Option::Some(case) = network { case.status = crate::MetaplexTokenMetadataValidationStatus::SyntheticValidated; } assert!( crate::validate_metaplex_token_metadata_cross_validation_matrix(&matrix).is_err() ); } } } /// One current Metaplex operation in the closed Devnet execution matrix. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataDevnetExecutionOperation { /// Stable instruction discriminator. pub discriminator: u8, /// Official instruction name. pub name: std::string::String, /// Stable executor operation code. pub operation_code: std::string::String, /// Whether the operation is deprecated. pub deprecated: bool, /// Prerelease responsible for the campaign. pub campaign_prerelease: std::string::String, /// Implementation status of the reusable runner. pub runner_status: std::string::String, /// Exact observed network status. pub network_status: std::string::String, /// Evidence required after simulation. pub required_evidence: std::vec::Vec, /// Additional evidence required after submission. pub submission_evidence: std::vec::Vec, } /// Closed inventory of every current Metaplex operation requiring Devnet coverage. #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MetaplexTokenMetadataDevnetExecutionMatrix { /// Matrix schema version. pub matrix_version: u32, /// Owning prerelease. pub milestone: std::string::String, /// Canonical Token Metadata program ID. pub program_id: std::string::String, /// Ordered current operations. pub operations: std::vec::Vec, } /// Loads and validates the closed Devnet execution matrix. pub fn load_metaplex_token_metadata_devnet_execution_matrix() -> kb_core::Result { let matrix = match serde_json::from_str::( include_str!( "../../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_DEVNET_EXECUTION_MATRIX.json" ), ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(kb_core::Error::new( "metaplex_devnet_execution_matrix_invalid_json", format!("Metaplex Devnet execution matrix JSON is invalid: {error}"), )); }, }; if let std::result::Result::Err(error) = validate_metaplex_token_metadata_devnet_execution_matrix(&matrix) { return std::result::Result::Err(error); } return std::result::Result::Ok(matrix); } /// Validates exact current-operation coverage and conservative network statuses. pub fn validate_metaplex_token_metadata_devnet_execution_matrix( matrix: &crate::MetaplexTokenMetadataDevnetExecutionMatrix, ) -> kb_core::Result<()> { if matrix.matrix_version != 1 || matrix.milestone != "0.4.7-pre.012" || matrix.program_id != kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID || matrix.operations.len() != 20 { return std::result::Result::Err(kb_core::Error::new( "metaplex_devnet_execution_matrix_contract_mismatch", "Metaplex Devnet matrix must declare exactly 20 current operations for pre.012", )); } let expected = [ "metadata.metaplex_token_metadata.create_escrow_account", "metadata.metaplex_token_metadata.close_escrow_account", "metadata.metaplex_token_metadata.transfer_out_of_escrow", "metadata.metaplex_token_metadata.burn", "metadata.metaplex_token_metadata.create", "metadata.metaplex_token_metadata.mint", "metadata.metaplex_token_metadata.delegate", "metadata.metaplex_token_metadata.revoke", "metadata.metaplex_token_metadata.lock", "metadata.metaplex_token_metadata.unlock", "metadata.metaplex_token_metadata.migrate", "metadata.metaplex_token_metadata.transfer", "metadata.metaplex_token_metadata.update", "metadata.metaplex_token_metadata.use", "metadata.metaplex_token_metadata.verify", "metadata.metaplex_token_metadata.unverify", "metadata.metaplex_token_metadata.collect", "metadata.metaplex_token_metadata.print", "metadata.metaplex_token_metadata.resize", "metadata.metaplex_token_metadata.close_accounts", ] .into_iter() .collect::>(); let observed = matrix .operations .iter() .map(|operation| return operation.operation_code.as_str()) .collect::>(); if observed != expected { return std::result::Result::Err(kb_core::Error::new( "metaplex_devnet_execution_matrix_operation_mismatch", "Metaplex Devnet matrix must cover the exact 20 current operation codes", )); } for operation in &matrix.operations { if operation.deprecated || operation.runner_status != "implemented" || !matches!(operation.campaign_prerelease.as_str(), "0.4.7-pre.011" | "0.4.7-pre.012") || operation.required_evidence.is_empty() || operation.submission_evidence.is_empty() || !matches!( operation.network_status.as_str(), "not_run" | "simulated" | "confirmed" | "unavailable" ) { return std::result::Result::Err(kb_core::Error::new( "metaplex_devnet_execution_matrix_entry_invalid", "Metaplex Devnet entries must be current, implemented, evidenced and conservatively classified", )); } } return std::result::Result::Ok(()); } #[cfg(test)] mod devnet_execution_matrix_tests { #[test] fn devnet_execution_matrix_is_closed_current_and_conservative() { let result = crate::load_metaplex_token_metadata_devnet_execution_matrix(); assert!(result.is_ok()); let matrix = if let std::result::Result::Ok(value) = result { value } else { return; }; assert_eq!(matrix.operations.len(), 20); assert_eq!( matrix .operations .iter() .filter(|operation| return operation.campaign_prerelease == "0.4.7-pre.011") .count(), 4, ); assert_eq!( matrix .operations .iter() .filter(|operation| return operation.campaign_prerelease == "0.4.7-pre.012") .count(), 16, ); assert!(matrix.operations.iter().all(|operation| return !operation.deprecated)); assert!( matrix .operations .iter() .all(|operation| return operation.runner_status == "implemented") ); assert!( matrix .operations .iter() .all(|operation| return operation.network_status == "not_run") ); } }