0.4.7-pre.008

This commit is contained in:
2026-08-02 11:58:34 +02:00
parent d831623980
commit d6cb4b0cce
11 changed files with 534 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
# file: Cargo.toml # file: Cargo.toml
# version: 14 # version: 15
[workspace] [workspace]
resolver = "3" resolver = "3"
@@ -18,7 +18,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.4.7-pre.7" version = "0.4.7-pre.8"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md --> <!-- file: docs/plans/V0_4_7_METAPLEX_TOKEN_METADATA_COMPLETION_PLAN.md -->
<!-- version: 8 --> <!-- version: 9 -->
# Plan `0.4.7` — achèvement de Metaplex Token Metadata # Plan `0.4.7` — achèvement de Metaplex Token Metadata
@@ -583,3 +583,12 @@ Le développement fonctionnel peut commencer après acceptation des recommandati
- orchestration simulation-first, résolution des signers et postconditions explicites ; - orchestration simulation-first, résolution des signers et postconditions explicites ;
- aucune dépendance vers `kb-pipeline-demo-scenarios` ; - aucune dépendance vers `kb-pipeline-demo-scenarios` ;
- les fixtures et campagnes réseau restent réservées à `pre.008`. - les fixtures et campagnes réseau restent réservées à `pre.008`.
## État de `0.4.7-pre.008`
- inventaire synthétique fermé pour NFT, SFT, fungible, collection et pNFT ;
- matrice automatisée de validation Metaplex avec preuves bornées ;
- aucune soumission automatique ;
- CLI inchangé faute de besoin démontré pour une fixture Metaplex dédiée ;
- scénarios UI du desktop conservés pour `pre.009`.

View File

@@ -0,0 +1,30 @@
<!-- file: docs/validation/V0_4_7_PRE_008_METAPLEX_SYNTHETIC_AND_NETWORK_VALIDATION.md -->
<!-- version: 1 -->
# Validation `0.4.7-pre.008` — scénarios Metaplex
## Couverture
- NFT ;
- SFT ;
- token fongible ;
- collection ;
- programmable NFT.
## Contrats
Les scénarios synthétiques sont déterministes et simulation-safe. La matrice réseau distingue `not_run`, `synthetic_validated`, `simulated`, `submitted`, `confirmed`, `unavailable` et `failed`. Aucun statut réseau nest déclaré sans preuve observée.
## CLI
Aucune nouvelle commande nest ajoutée : le CLI reste destiné à la préparation Token-2022 tant quune fixture Metaplex dédiée nest pas nécessaire.
## Validation locale à exécuter
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --all-targets
python3 scripts/audit_rust_workspace_rules.py
cargo test -p kb-pipeline-demo-scenarios
```

View File

@@ -1,8 +1,14 @@
<!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md --> <!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md -->
<!-- version: 8 --> <!-- version: 9 -->
# CHANGELOG — kb-pipeline-demo-scenarios # CHANGELOG — kb-pipeline-demo-scenarios
## 0.4.7-pre.008
- ajout de linventaire synthétique Metaplex NFT, SFT, fungible, collection et pNFT ;
- ajout de la matrice de validation automatisée Devnet/Testnet et de ses preuves bornées ;
- conservation du CLI sans nouvelle commande, aucune fixture Metaplex réseau dédiée nétant encore nécessaire.
## 0.4.6 ## 0.4.6
- alignement de la crate sur la version fonctionnelle bot3 `0.4.6` ; - alignement de la crate sur la version fonctionnelle bot3 `0.4.6` ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-pipeline-demo-scenarios/README.md --> <!-- file: kb-pipeline-demo-scenarios/README.md -->
<!-- version: 1 --> <!-- version: 2 -->
# kb-pipeline-demo-scenarios # kb-pipeline-demo-scenarios
@@ -17,6 +17,7 @@ La crate est mixte :
- simulation ou soumission explicitement autorisée ; - simulation ou soumission explicitement autorisée ;
- préparation de fixtures publiques ; - préparation de fixtures publiques ;
- validation des matrices et rapports Token-2022 ; - validation des matrices et rapports Token-2022 ;
- scénarios synthétiques et matrice de validation Metaplex Token Metadata ;
- progression observable et annulation coopérative. - progression observable et annulation coopérative.
Elle ne remplace ni le pipeline générique ni lapplication desktop. Elle ne remplace ni le pipeline générique ni lapplication desktop.

View File

@@ -1,12 +1,12 @@
<!-- file: kb-pipeline-demo-scenarios/TODO.md --> <!-- file: kb-pipeline-demo-scenarios/TODO.md -->
<!-- version: 2 --> <!-- version: 3 -->
# TODO — kb-pipeline-demo-scenarios # TODO — kb-pipeline-demo-scenarios
## Entre `0.4.6` et `0.4.7` ## `0.4.7`
- [ ] Metaplex Token Metadata - ajouter les scénarios nécessaires à la clôture fonctionnelle de `0.4.7`. - [ ] Tests réseau Metaplex - exécuter les validations Devnet/Testnet lorsque les fixtures, comptes et fonds nécessaires sont disponibles.
- [ ] Tests Devnet - ajouter les validations de bout en bout Metaplex sans dépendre du desktop. - [ ] Preuves Metaplex - renseigner les signatures, simulations et postconditions observées sans déclarer de validation réseau non exécutée.
## Série `0.5.x` ## Série `0.5.x`

View File

@@ -1,5 +1,5 @@
<!-- file: kb-pipeline-demo-scenarios/USAGE.md --> <!-- file: kb-pipeline-demo-scenarios/USAGE.md -->
<!-- version: 2 --> <!-- version: 3 -->
# Utilisation de kb-pipeline-demo-scenarios # Utilisation de kb-pipeline-demo-scenarios
@@ -179,3 +179,33 @@ Les tests Devnet restent opt-in et peuvent produire des transactions réelles lo
- cette crate est destinée aux démonstrations, validations et outils opérateur, pas au moteur de production autonome ; - cette crate est destinée aux démonstrations, validations et outils opérateur, pas au moteur de production autonome ;
- les scénarios réseau exigent un endpoint, un wallet et des fonds compatibles ; - les scénarios réseau exigent un endpoint, un wallet et des fonds compatibles ;
- la bibliothèque ne fournit pas dinterface graphique. - la bibliothèque ne fournit pas dinterface graphique.
## Charger les scénarios synthétiques Metaplex
```rust
fn metaplex_scenario_ids() -> Vec<String> {
kb_pipeline_demo_scenarios::metaplex_token_metadata_synthetic_scenarios()
.iter()
.map(|scenario| scenario.id.clone())
.collect()
}
```
Linventaire couvre NFT, SFT, token fongible, collection et pNFT. Ces scénarios sont déterministes et ne soumettent aucune transaction.
## Charger la matrice de validation Metaplex
```rust
fn load_metaplex_matrix(
) -> kb_core::Result<kb_pipeline_demo_scenarios::MetaplexTokenMetadataValidationMatrix> {
let result = kb_pipeline_demo_scenarios::load_metaplex_token_metadata_validation_matrix();
match result {
Ok(matrix) => Ok(matrix),
Err(error) => Err(error),
}
}
```
Une validation réseau ne peut passer à `confirmed` que lorsque toutes les preuves déclarées sont présentes. Les statuts `not_run` et `unavailable` nacceptent aucune preuve observée.

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline-demo-scenarios/src/lib.rs // file: kb-pipeline-demo-scenarios/src/lib.rs
// version: 5 // version: 6
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![deny(unreachable_pub)] #![deny(unreachable_pub)]
@@ -12,6 +12,8 @@ mod environment;
mod solana_ata_execution; mod solana_ata_execution;
mod solana_execution; mod solana_execution;
mod solana_memo_execution; mod solana_memo_execution;
mod solana_metaplex_token_metadata_scenarios;
mod solana_metaplex_token_metadata_validation;
mod solana_token_2022_devnet_execution; mod solana_token_2022_devnet_execution;
mod solana_token_2022_devnet_scenarios; mod solana_token_2022_devnet_scenarios;
mod solana_token_2022_validation; mod solana_token_2022_validation;
@@ -65,6 +67,28 @@ pub use self::solana_memo_execution::DevnetMemoExecutionRequest;
pub use self::solana_memo_execution::DevnetMemoExecutionSummary; pub use self::solana_memo_execution::DevnetMemoExecutionSummary;
/// Executes one SPL Memo v4 Devnet simulation or explicitly authorized submission. /// Executes one SPL Memo v4 Devnet simulation or explicitly authorized submission.
pub use self::solana_memo_execution::execute_devnet_memo; pub use self::solana_memo_execution::execute_devnet_memo;
/// Stable asset family covered by one Metaplex scenario.
pub use self::solana_metaplex_token_metadata_scenarios::MetaplexTokenMetadataAssetFamily;
/// One stable Metaplex scenario reusable by automated tests and demo adapters.
pub use self::solana_metaplex_token_metadata_scenarios::MetaplexTokenMetadataScenario;
/// Execution mode accepted by one Metaplex scenario.
pub use self::solana_metaplex_token_metadata_scenarios::MetaplexTokenMetadataScenarioMode;
/// Returns the complete ordered synthetic Metaplex scenario inventory.
pub use self::solana_metaplex_token_metadata_scenarios::metaplex_token_metadata_synthetic_scenarios;
/// Maximum evidence entries accepted by one Metaplex validation scenario.
pub use self::solana_metaplex_token_metadata_validation::MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE;
/// One bounded Metaplex validation evidence entry.
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationEvidence;
/// Canonical Metaplex validation matrix.
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationMatrix;
/// One scenario declared by the canonical Metaplex validation matrix.
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationMatrixScenario;
/// Exact validation status for one Metaplex scenario.
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationStatus;
/// Loads and validates the canonical Metaplex validation matrix.
pub use self::solana_metaplex_token_metadata_validation::load_metaplex_token_metadata_validation_matrix;
/// Validates the canonical Metaplex validation matrix.
pub use self::solana_metaplex_token_metadata_validation::validate_metaplex_token_metadata_validation_matrix;
/// Complete request for one Devnet Token-2022 execution. /// Complete request for one Devnet Token-2022 execution.
pub use self::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest; pub use self::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest;
/// Complete result of one Devnet Token-2022 execution. /// Complete result of one Devnet Token-2022 execution.

View File

@@ -0,0 +1,170 @@
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_scenarios.rs
// version: 1
//! Deterministic Metaplex Token Metadata scenario inventory for tests and Devnet campaigns.
/// Stable asset family covered by one Metaplex scenario.
#[derive(
Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(rename_all = "snake_case")]
pub enum MetaplexTokenMetadataAssetFamily {
/// Non-fungible token.
Nft,
/// Semi-fungible token.
Sft,
/// Fungible token with Metaplex metadata.
Fungible,
/// Collection metadata and verification flow.
Collection,
/// Programmable non-fungible token.
ProgrammableNft,
}
/// Execution mode accepted by one scenario.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MetaplexTokenMetadataScenarioMode {
/// Purely deterministic scenario using synthetic accounts and plans.
Synthetic,
/// Devnet or Testnet simulation without submission.
NetworkSimulation,
/// Explicitly authorized Devnet or Testnet submission.
NetworkSubmission,
}
/// One stable Metaplex scenario reusable by automated tests and demo adapters.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MetaplexTokenMetadataScenario {
/// Stable scenario identifier.
pub id: std::string::String,
/// Asset family exercised by the scenario.
pub asset_family: crate::MetaplexTokenMetadataAssetFamily,
/// Execution mode.
pub mode: crate::MetaplexTokenMetadataScenarioMode,
/// Stable operation codes executed in order.
pub operation_codes: std::vec::Vec<std::string::String>,
/// Whether the scenario requires a collection account.
pub requires_collection: bool,
/// Whether the scenario requires programmable authorization rules.
pub requires_programmable_rules: bool,
/// Whether a confirmed postcondition is mandatory.
pub requires_postcondition: bool,
}
/// Returns the complete ordered synthetic Metaplex scenario inventory.
pub fn metaplex_token_metadata_synthetic_scenarios()
-> std::vec::Vec<crate::MetaplexTokenMetadataScenario> {
return vec![
crate::MetaplexTokenMetadataScenario {
id: "metaplex_nft_create_update".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Nft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_sft_create_mint".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Sft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_MINT_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_fungible_create_update".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Fungible,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_collection_verify_unverify".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::Collection,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_VERIFY_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UNVERIFY_OPERATION.to_string(),
],
requires_collection: true,
requires_programmable_rules: false,
requires_postcondition: true,
},
crate::MetaplexTokenMetadataScenario {
id: "metaplex_pnft_delegate_transfer_revoke".to_string(),
asset_family: crate::MetaplexTokenMetadataAssetFamily::ProgrammableNft,
mode: crate::MetaplexTokenMetadataScenarioMode::Synthetic,
operation_codes: vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_DELEGATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_TRANSFER_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_REVOKE_OPERATION.to_string(),
],
requires_collection: false,
requires_programmable_rules: true,
requires_postcondition: true,
},
];
}
#[cfg(test)]
mod tests {
#[test]
fn synthetic_inventory_is_exact_unique_and_covers_every_required_asset_family() {
let scenarios = crate::metaplex_token_metadata_synthetic_scenarios();
assert_eq!(scenarios.len(), 5);
let ids = scenarios
.iter()
.map(|scenario| return scenario.id.as_str())
.collect::<std::collections::BTreeSet<&str>>();
assert_eq!(ids.len(), scenarios.len());
let families = scenarios
.iter()
.map(|scenario| return scenario.asset_family)
.collect::<std::collections::BTreeSet<crate::MetaplexTokenMetadataAssetFamily>>(
);
assert_eq!(families.len(), 5);
}
#[test]
fn synthetic_scenarios_are_simulation_safe_and_require_postconditions() {
for scenario in crate::metaplex_token_metadata_synthetic_scenarios() {
assert_eq!(scenario.mode, crate::MetaplexTokenMetadataScenarioMode::Synthetic);
assert!(!scenario.operation_codes.is_empty());
assert!(scenario.requires_postcondition);
}
}
#[test]
fn programmable_and_collection_requirements_are_declared_only_where_needed() {
let scenarios = crate::metaplex_token_metadata_synthetic_scenarios();
let programmable = scenarios
.iter()
.filter(|scenario| return scenario.requires_programmable_rules)
.collect::<std::vec::Vec<&crate::MetaplexTokenMetadataScenario>>();
assert_eq!(programmable.len(), 1);
assert_eq!(
programmable[0].asset_family,
crate::MetaplexTokenMetadataAssetFamily::ProgrammableNft
);
let collection = scenarios
.iter()
.filter(|scenario| return scenario.requires_collection)
.collect::<std::vec::Vec<&crate::MetaplexTokenMetadataScenario>>();
assert_eq!(collection.len(), 1);
assert_eq!(collection[0].asset_family, crate::MetaplexTokenMetadataAssetFamily::Collection);
}
}

View File

@@ -0,0 +1,192 @@
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_validation.rs
// version: 1
//! 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>,
}
/// 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());
}
}
}

View File

@@ -0,0 +1,61 @@
{
"matrixVersion": 1,
"milestone": "0.4.7",
"scenarios": [
{
"id": "metaplex_nft_create_update",
"mode": "synthetic",
"status": "not_run",
"requiredEvidence": [
"test_suite",
"prepared_plan",
"postcondition"
],
"evidence": []
},
{
"id": "metaplex_sft_create_mint",
"mode": "synthetic",
"status": "not_run",
"requiredEvidence": [
"test_suite",
"prepared_plan",
"postcondition"
],
"evidence": []
},
{
"id": "metaplex_fungible_create_update",
"mode": "synthetic",
"status": "not_run",
"requiredEvidence": [
"test_suite",
"prepared_plan",
"postcondition"
],
"evidence": []
},
{
"id": "metaplex_collection_verify_unverify",
"mode": "synthetic",
"status": "not_run",
"requiredEvidence": [
"test_suite",
"prepared_plan",
"postcondition"
],
"evidence": []
},
{
"id": "metaplex_pnft_delegate_transfer_revoke",
"mode": "synthetic",
"status": "not_run",
"requiredEvidence": [
"test_suite",
"prepared_plan",
"postcondition"
],
"evidence": []
}
]
}