Files
khadhroony-bot3/kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_validation.rs
2026-08-03 09:34:16 +02:00

616 lines
25 KiB
Rust

// 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<std::string::String>,
/// Observed evidence.
#[serde(default)]
pub evidence: std::vec::Vec<crate::MetaplexTokenMetadataValidationEvidence>,
}
/// 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<crate::MetaplexTokenMetadataValidationMatrixScenario>,
}
/// 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<crate::MetaplexTokenMetadataAssetFamily>,
/// Optional stable negative-case category.
pub failure_class: std::option::Option<crate::MetaplexTokenMetadataFailureClass>,
/// 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<std::string::String>,
/// Bounded observed evidence.
#[serde(default)]
pub evidence: std::vec::Vec<crate::MetaplexTokenMetadataValidationEvidence>,
}
/// 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<crate::MetaplexTokenMetadataCrossValidationCase>,
}
/// Loads and validates the closed Metaplex cross-validation corpus.
pub fn load_metaplex_token_metadata_cross_validation_matrix()
-> kb_core::Result<crate::MetaplexTokenMetadataCrossValidationMatrix> {
let parsed = match serde_json::from_str::<crate::MetaplexTokenMetadataCrossValidationMatrix>(
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::<std::collections::BTreeSet<&str>>();
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<crate::MetaplexTokenMetadataValidationMatrix> {
let parsed =
match serde_json::from_str::<crate::MetaplexTokenMetadataValidationMatrix>(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::<std::vec::Vec<std::string::String>>();
let actual = matrix
.scenarios
.iter()
.map(|scenario| return scenario.id.clone())
.collect::<std::vec::Vec<std::string::String>>();
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::<std::collections::BTreeSet<&str>>();
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::<std::collections::BTreeSet<crate::MetaplexTokenMetadataTransactionPath>>(
);
assert_eq!(paths.len(), 2);
let families = matrix
.cases
.iter()
.filter_map(|case| return case.asset_family)
.collect::<std::collections::BTreeSet<crate::MetaplexTokenMetadataAssetFamily>>(
);
assert_eq!(families.len(), 5);
let failures = matrix
.cases
.iter()
.filter_map(|case| return case.failure_class)
.collect::<std::collections::BTreeSet<crate::MetaplexTokenMetadataFailureClass>>(
);
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<std::string::String>,
/// Additional evidence required after submission.
pub submission_evidence: std::vec::Vec<std::string::String>,
}
/// 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<crate::MetaplexTokenMetadataDevnetExecutionOperation>,
}
/// Loads and validates the closed Devnet execution matrix.
pub fn load_metaplex_token_metadata_devnet_execution_matrix()
-> kb_core::Result<crate::MetaplexTokenMetadataDevnetExecutionMatrix> {
let matrix = match serde_json::from_str::<crate::MetaplexTokenMetadataDevnetExecutionMatrix>(
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::<std::collections::BTreeSet<&str>>();
let observed = matrix
.operations
.iter()
.map(|operation| return operation.operation_code.as_str())
.collect::<std::collections::BTreeSet<&str>>();
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")
);
}
}