v0.4.7-pre.010
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 16
|
# version: 17
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -18,7 +18,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.4.7-pre.9"
|
version = "0.4.7-pre.10"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||||
|
|||||||
18
ROADMAP.md
18
ROADMAP.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 23 -->
|
<!-- version: 25 -->
|
||||||
|
|
||||||
# ROADMAP — khadhroony-bot3
|
# ROADMAP — khadhroony-bot3
|
||||||
|
|
||||||
@@ -30,7 +30,8 @@ Version clôturée.
|
|||||||
- auditer la matérialisation migrée et compléter uniquement les projections manquantes ;
|
- auditer la matérialisation migrée et compléter uniquement les projections manquantes ;
|
||||||
- implémenter les intents typés, builders, exécuteur, préflights et postconditions ;
|
- implémenter les intents typés, builders, exécuteur, préflights et postconditions ;
|
||||||
- intégrer l’exécution et les validations dans `kb-pipeline` ;
|
- intégrer l’exécution et les validations dans `kb-pipeline` ;
|
||||||
- ajouter les scénarios réutilisables, le CLI et les panneaux desktop ;
|
- ajouter les scénarios synthétiques et les validations automatisées Devnet/Testnet dans `kb-pipeline-demo-scenarios`, en parallèle des scénarios UI conservés dans le desktop ;
|
||||||
|
- étendre le CLI uniquement lorsqu’une préparation de fixture dédiée le justifie ;
|
||||||
- valider le parcours réseau et PostgreSQL de bout en bout lorsque les fixtures sont disponibles.
|
- valider le parcours réseau et PostgreSQL de bout en bout lorsque les fixtures sont disponibles.
|
||||||
|
|
||||||
### Contraintes
|
### Contraintes
|
||||||
@@ -44,7 +45,9 @@ Version clôturée.
|
|||||||
- exécution simulation-first avec signers, coûts et confirmations exacts ;
|
- exécution simulation-first avec signers, coûts et confirmations exacts ;
|
||||||
- intégration pipeline et démonstrations complètes ;
|
- intégration pipeline et démonstrations complètes ;
|
||||||
- tests contractuels, unitaires et stateful ;
|
- tests contractuels, unitaires et stateful ;
|
||||||
- limites réseau et opérations historiques decode-only documentées.
|
- limites réseau documentées ;
|
||||||
|
- opérations remplacées redirigées vers leur remplacement canonique final ;
|
||||||
|
- opérations obsolètes encore constructibles exposées comme exécutables dépréciées et soumises à une approbation explicite.
|
||||||
|
|
||||||
## 0.4.8 — SPL Token Metadata et décision off-chain metadata
|
## 0.4.8 — SPL Token Metadata et décision off-chain metadata
|
||||||
|
|
||||||
@@ -80,10 +83,10 @@ Version clôturée.
|
|||||||
|
|
||||||
### Scénarios de démonstration
|
### Scénarios de démonstration
|
||||||
|
|
||||||
- rendre `kb-pipeline-demo-scenarios` pleinement autonome hors desktop ;
|
- ajouter dans `kb-pipeline-demo-scenarios` des tests automatisés reproduisant en parallèle les scénarios Devnet/Testnet des panneaux desktop ;
|
||||||
- améliorer le CLI, les fixtures et les rapports ;
|
- conserver les scénarios UI Devnet/Testnet dans `kb-app-demo-desktop` ;
|
||||||
- compléter les démonstrations des surfaces `0.4.x` ;
|
- améliorer le CLI uniquement pour les préparations de fixtures et opérations explicitement justifiées ;
|
||||||
- maintenir les adapters Tauri minces.
|
- compléter les démonstrations des surfaces `0.4.x` et maintenir les adaptateurs Tauri minces.
|
||||||
|
|
||||||
### Wallet
|
### Wallet
|
||||||
|
|
||||||
@@ -187,3 +190,4 @@ Vérifier sa nature, son déploiement et son appartenance réelle à la famille
|
|||||||
- opérations historiques et backfills avancés ;
|
- opérations historiques et backfills avancés ;
|
||||||
- optimisations, observabilité et outils d’administration ;
|
- optimisations, observabilité et outils d’administration ;
|
||||||
- extensions futures validées par des contrats bornés.
|
- extensions futures validées par des contrats bornés.
|
||||||
|
|
||||||
|
|||||||
@@ -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: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
# Plan `0.4.7` — achèvement de Metaplex Token Metadata
|
# Plan `0.4.7` — achèvement de Metaplex Token Metadata
|
||||||
|
|
||||||
@@ -467,7 +467,7 @@ La numérotation ci-dessous est un plan fermé initial. Elle peut être regroup
|
|||||||
- fonctionnement manuel des parcours retenus ;
|
- fonctionnement manuel des parcours retenus ;
|
||||||
- réouverture et état applicatif cohérents.
|
- réouverture et état applicatif cohérents.
|
||||||
|
|
||||||
### `0.4.7-pre.010` — corpus et validations croisées
|
### `0.4.7-pre.010` — corpus et validations croisées — réalisée, validation locale requise
|
||||||
|
|
||||||
**Travaux**
|
**Travaux**
|
||||||
|
|
||||||
|
|||||||
41
docs/validation/V0_4_7_PRE_010_METAPLEX_CROSS_VALIDATION.md
Normal file
41
docs/validation/V0_4_7_PRE_010_METAPLEX_CROSS_VALIDATION.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!-- file: docs/validation/V0_4_7_PRE_010_METAPLEX_CROSS_VALIDATION.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Validation `0.4.7-pre.010` — corpus croisé Metaplex Token Metadata
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Cette prerelease ferme le corpus de validation croisée sans ajouter une nouvelle capacité d’exécution. Elle vérifie l’alignement entre décodeur, matérialisation, exécuteur, pipeline, scénarios réutilisables et desktop.
|
||||||
|
|
||||||
|
## Corpus machine-readable
|
||||||
|
|
||||||
|
`test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_CROSS_VALIDATION_MATRIX.json` contient 19 cas :
|
||||||
|
|
||||||
|
- chemins outer et CPI ;
|
||||||
|
- transactions réussies et échouées ;
|
||||||
|
- NFT, SFT, token fongible, collection et programmable NFT ;
|
||||||
|
- mauvais Program ID, owner, PDA et comptes ;
|
||||||
|
- payload tronqué, suffixe interdit et discriminant inconnu ;
|
||||||
|
- conflit Metaplex/metadata Token-2022 ;
|
||||||
|
- contrats séparés de simulation, soumission et postcondition Devnet.
|
||||||
|
|
||||||
|
## Exactitude des preuves réseau
|
||||||
|
|
||||||
|
Les trois cas réseau restent `not_run` sans preuve. Le validateur interdit de les promouvoir avec le statut `synthetic_validated` et exige toutes les catégories de preuves prévues avant `simulated`, `submitted` ou `confirmed`.
|
||||||
|
|
||||||
|
Le panneau desktop `pre.009-fix-002` charge le profil et prépare la base Devnet, mais n’exécute pas encore une transaction Metaplex. Cette préparation n’est donc pas comptée comme validation réseau.
|
||||||
|
|
||||||
|
## Validation locale requise
|
||||||
|
|
||||||
|
```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
|
||||||
|
cargo test -p kb-lib
|
||||||
|
cargo test -p kb-pipeline
|
||||||
|
cargo test -p kb-app-demo-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
Les résultats doivent être ajoutés à ce rapport avant validation définitive de `pre.010`.
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
<!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md -->
|
<!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md -->
|
||||||
<!-- version: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
|
# CHANGELOG — kb-pipeline-demo-scenarios
|
||||||
|
|
||||||
|
## 0.4.7-pre.010
|
||||||
|
|
||||||
|
- ajout d’un corpus fermé de validation croisée Metaplex couvrant outer/CPI, succès/échec, cinq familles d’actifs et neuf classes d’erreur ;
|
||||||
|
- ajout de trois contrats de preuve réseau distincts pour simulation, soumission et postcondition ;
|
||||||
|
- refus machine-readable de toute promotion réseau fondée uniquement sur des preuves synthétiques.
|
||||||
|
|
||||||
## 0.4.7-pre.009-fix-001
|
## 0.4.7-pre.009-fix-001
|
||||||
|
|
||||||
- ajout de l’inventaire Metaplex Devnet simulation et de son test de couverture.
|
- ajout de l’inventaire Metaplex Devnet simulation et de son test de couverture.
|
||||||
|
|
||||||
|
|
||||||
# CHANGELOG — kb-pipeline-demo-scenarios
|
|
||||||
|
|
||||||
## 0.4.7-pre.008
|
## 0.4.7-pre.008
|
||||||
|
|
||||||
- ajout de l’inventaire synthétique Metaplex NFT, SFT, fungible, collection et pNFT ;
|
- ajout de l’inventaire synthétique Metaplex NFT, SFT, fungible, collection et pNFT ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-pipeline-demo-scenarios/README.md -->
|
<!-- file: kb-pipeline-demo-scenarios/README.md -->
|
||||||
<!-- version: 2 -->
|
<!-- version: 3 -->
|
||||||
|
|
||||||
# kb-pipeline-demo-scenarios
|
# kb-pipeline-demo-scenarios
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ La crate est mixte :
|
|||||||
- 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 ;
|
- scénarios synthétiques et matrice de validation Metaplex Token Metadata ;
|
||||||
|
- corpus fermé de validation croisée outer/CPI, cas positifs/négatifs et preuves réseau exactes ;
|
||||||
- progression observable et annulation coopérative.
|
- progression observable et annulation coopérative.
|
||||||
|
|
||||||
Elle ne remplace ni le pipeline générique ni l’application desktop.
|
Elle ne remplace ni le pipeline générique ni l’application desktop.
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<!-- file: kb-pipeline-demo-scenarios/TODO.md -->
|
<!-- file: kb-pipeline-demo-scenarios/TODO.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# TODO — kb-pipeline-demo-scenarios
|
# TODO — kb-pipeline-demo-scenarios
|
||||||
|
|
||||||
## `0.4.7`
|
## `0.4.7`
|
||||||
|
|
||||||
|
- [x] Corpus croisé Metaplex - fermer les chemins outer/CPI, cinq familles d’actifs, échecs transactionnels et entrées invalides.
|
||||||
- [ ] Tests réseau Metaplex - exécuter les validations Devnet/Testnet lorsque les fixtures, comptes et fonds nécessaires sont disponibles.
|
- [ ] Tests réseau Metaplex - exécuter les validations Devnet/Testnet lorsque les fixtures, comptes et fonds nécessaires sont disponibles.
|
||||||
- [ ] Preuves Metaplex - renseigner les signatures, simulations et postconditions observées sans déclarer de validation réseau non exécutée.
|
- [ ] Preuves Metaplex - renseigner les signatures, simulations et postconditions observées sans déclarer de validation réseau non exécutée.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-pipeline-demo-scenarios/USAGE.md -->
|
<!-- file: kb-pipeline-demo-scenarios/USAGE.md -->
|
||||||
<!-- version: 3 -->
|
<!-- version: 4 -->
|
||||||
|
|
||||||
# Utilisation de kb-pipeline-demo-scenarios
|
# Utilisation de kb-pipeline-demo-scenarios
|
||||||
|
|
||||||
@@ -220,3 +220,17 @@ assert!(scenarios.iter().all(|scenario| {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Cet inventaire prépare les tests et campagnes Devnet simulation-first. Il ne constitue pas à lui seul une validation réseau réussie.
|
Cet inventaire prépare les tests et campagnes Devnet simulation-first. Il ne constitue pas à lui seul une validation réseau réussie.
|
||||||
|
|
||||||
|
## Charger le corpus de validation croisée Metaplex
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn load_metaplex_cross_validation() -> kb_core::Result<usize> {
|
||||||
|
let matrix = kb_pipeline_demo_scenarios::load_metaplex_token_metadata_cross_validation_matrix();
|
||||||
|
return match matrix {
|
||||||
|
Ok(value) => Ok(value.cases.len()),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Les cas marqués `requiresNetworkEvidence` ne peuvent pas être déclarés validés à partir de preuves synthétiques. Une simulation, une soumission ou une confirmation exige les preuves RPC déclarées par le cas.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-pipeline-demo-scenarios/src/lib.rs
|
// file: kb-pipeline-demo-scenarios/src/lib.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -79,6 +79,16 @@ pub use self::solana_metaplex_token_metadata_scenarios::metaplex_token_metadata_
|
|||||||
pub use self::solana_metaplex_token_metadata_scenarios::metaplex_token_metadata_synthetic_scenarios;
|
pub use self::solana_metaplex_token_metadata_scenarios::metaplex_token_metadata_synthetic_scenarios;
|
||||||
/// Maximum evidence entries accepted by one Metaplex validation scenario.
|
/// Maximum evidence entries accepted by one Metaplex validation scenario.
|
||||||
pub use self::solana_metaplex_token_metadata_validation::MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE;
|
pub use self::solana_metaplex_token_metadata_validation::MAX_METAPLEX_TOKEN_METADATA_VALIDATION_EVIDENCE;
|
||||||
|
/// One case in the closed Metaplex cross-validation corpus.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataCrossValidationCase;
|
||||||
|
/// Closed Metaplex cross-validation corpus for `0.4.7-pre.010`.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataCrossValidationMatrix;
|
||||||
|
/// Stable failure category exercised by one negative case.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataFailureClass;
|
||||||
|
/// Expected transaction outcome for one cross-validation case.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataTransactionOutcome;
|
||||||
|
/// Transaction location covered by one cross-validation case.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataTransactionPath;
|
||||||
/// One bounded Metaplex validation evidence entry.
|
/// One bounded Metaplex validation evidence entry.
|
||||||
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationEvidence;
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationEvidence;
|
||||||
/// Canonical Metaplex validation matrix.
|
/// Canonical Metaplex validation matrix.
|
||||||
@@ -87,8 +97,12 @@ pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataVa
|
|||||||
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationMatrixScenario;
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationMatrixScenario;
|
||||||
/// Exact validation status for one Metaplex scenario.
|
/// Exact validation status for one Metaplex scenario.
|
||||||
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationStatus;
|
pub use self::solana_metaplex_token_metadata_validation::MetaplexTokenMetadataValidationStatus;
|
||||||
|
/// Loads and validates the closed Metaplex cross-validation corpus.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::load_metaplex_token_metadata_cross_validation_matrix;
|
||||||
/// Loads and validates the canonical Metaplex validation matrix.
|
/// Loads and validates the canonical Metaplex validation matrix.
|
||||||
pub use self::solana_metaplex_token_metadata_validation::load_metaplex_token_metadata_validation_matrix;
|
pub use self::solana_metaplex_token_metadata_validation::load_metaplex_token_metadata_validation_matrix;
|
||||||
|
/// Validates the closed Metaplex cross-validation corpus and evidence claims.
|
||||||
|
pub use self::solana_metaplex_token_metadata_validation::validate_metaplex_token_metadata_cross_validation_matrix;
|
||||||
/// Validates the canonical Metaplex validation matrix.
|
/// Validates the canonical Metaplex validation matrix.
|
||||||
pub use self::solana_metaplex_token_metadata_validation::validate_metaplex_token_metadata_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.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_validation.rs
|
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_validation.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Machine-readable Metaplex Token Metadata validation contract.
|
//! Machine-readable Metaplex Token Metadata validation contract.
|
||||||
|
|
||||||
@@ -64,6 +64,203 @@ pub struct MetaplexTokenMetadataValidationMatrix {
|
|||||||
pub scenarios: std::vec::Vec<crate::MetaplexTokenMetadataValidationMatrixScenario>,
|
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.
|
/// Loads and validates the canonical Metaplex validation matrix.
|
||||||
pub fn load_metaplex_token_metadata_validation_matrix()
|
pub fn load_metaplex_token_metadata_validation_matrix()
|
||||||
-> kb_core::Result<crate::MetaplexTokenMetadataValidationMatrix> {
|
-> kb_core::Result<crate::MetaplexTokenMetadataValidationMatrix> {
|
||||||
@@ -189,4 +386,55 @@ mod tests {
|
|||||||
assert!(crate::validate_metaplex_token_metadata_validation_matrix(&matrix).is_ok());
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
{
|
||||||
|
"matrixVersion": 1,
|
||||||
|
"milestone": "0.4.7-pre.010",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"id": "outer_success_nft",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cpi_success_nft",
|
||||||
|
"path": "cpi",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outer_failed_transaction",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "transaction_failed",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"uncommitted_intent"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cpi_failed_transaction",
|
||||||
|
"path": "cpi",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "transaction_failed",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"uncommitted_intent"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "invalid_program_id",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "program_id",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "invalid_owner",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "owner",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "invalid_pda",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "pda",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "invalid_accounts",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "accounts",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "truncated_payload",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "payload_truncated",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forbidden_suffix",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "payload_suffix",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "unknown_discriminant",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "unknown_discriminant",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "metaplex_token_2022_conflict",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "failed",
|
||||||
|
"assetFamily": null,
|
||||||
|
"failureClass": "metadata_source_conflict",
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"stable_diagnostic"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outer_success_sft",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "sft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outer_success_fungible",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "fungible",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outer_success_collection",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "collection",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outer_success_programmable_nft",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "programmable_nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": false,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"test_suite",
|
||||||
|
"decoded_instruction",
|
||||||
|
"materialized_fact"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "devnet_simulation_available",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": true,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"rpc_endpoint",
|
||||||
|
"simulation_logs",
|
||||||
|
"slot"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "devnet_submission_available",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": true,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"signature",
|
||||||
|
"confirmed_slot"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "devnet_postcondition_available",
|
||||||
|
"path": "outer",
|
||||||
|
"outcome": "success",
|
||||||
|
"assetFamily": "nft",
|
||||||
|
"failureClass": null,
|
||||||
|
"requiresNetworkEvidence": true,
|
||||||
|
"status": "not_run",
|
||||||
|
"requiredEvidence": [
|
||||||
|
"signature",
|
||||||
|
"confirmed_slot",
|
||||||
|
"postcondition"
|
||||||
|
],
|
||||||
|
"evidence": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user