v0.4.8
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: olddocs/archivekbot3/001.README.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Archive documentaire de khadhroony-bot3
|
||||
|
||||
@@ -28,3 +28,7 @@ Les audits, guides et checklist temporaires utilisés pour clôturer la migratio
|
||||
## Clôture 0.4.7
|
||||
|
||||
Le plan, le prompt de session et les rapports de prerelease de Metaplex Token Metadata sont archivés sous `docs/plans/`, `docs/validation/` et `prompts/`. Le rapport final actif reste sous `docs/validation/` jusqu’à la publication.
|
||||
|
||||
## Clôture 0.4.8
|
||||
|
||||
Le plan `0.4.8`, le prompt de session `028`, les audits `V0_4_8_PRE_*` et les rapports de validation de prerelease sont archivés sous `docs/plans/`, `docs/audits/`, `docs/validation/` et `prompts/`. Le rapport fonctionnel final `0.4.8` reste actif sous `docs/validation/`.
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_002_METADATA_CONTRACT_AUDIT.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Audit contractuel `0.4.8-pre.002` — Solana Program Metadata et Token-2022
|
||||
|
||||
## 1. Portée
|
||||
|
||||
Cet audit ferme la phase documentaire préalable au code de `0.4.8`. Il compare l’état réel du workspace à deux sources officielles distinctes :
|
||||
|
||||
- `solana-program/program-metadata` pour le programme `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` ;
|
||||
- `solana-program/token-metadata` et l’implémentation Token-2022 pour `spl-token-metadata-interface`.
|
||||
|
||||
Aucun code fonctionnel n’est ajouté par cette prerelease.
|
||||
|
||||
## 2. Sources de vérité retenues
|
||||
|
||||
| Surface | Source officielle | Décision |
|
||||
|---------------------------|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| Solana Program Metadata | dépôt `https://github.com/solana-program/program-metadata`, `README.md`, `idl.json` et sources Rust | l’IDL Codama et les sources du programme sont contractuelles ; la copie locale reste brute |
|
||||
| Token Metadata interface | dépôt `https://github.com/solana-program/token-metadata`, crate `spl-token-metadata-interface` | les cinq discriminateurs et formats Borsh de l’interface sont contractuels |
|
||||
| Implémentation Token-2022 | dépôt `https://github.com/solana-program/token-2022` | Token-2022 est l’implémentation ciblée par le workspace ; aucun Program ID metadata autonome n’est créé |
|
||||
|
||||
L’audit a été réalisé le 5 août 2026. Les implémentations futures doivent continuer à pinner leurs dépendances Cargo et ne pas dépendre implicitement de l’état mouvant de la branche `main`.
|
||||
|
||||
## 3. Validation de l’IDL Solana Program Metadata
|
||||
|
||||
Copie locale auditée :
|
||||
|
||||
```text
|
||||
idls/metadata.ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S.solana_program_metadata.V0_0_0.from_github_solana_program.json
|
||||
```
|
||||
|
||||
Résultat :
|
||||
|
||||
- standard : Codama `1.0.0` ;
|
||||
- nom déclaré : `programMetadata` ;
|
||||
- Program ID déclaré : `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` ;
|
||||
- version déclarée par la source : `0.0.0` ;
|
||||
- comptes : `Buffer` et `Metadata` ;
|
||||
- instructions : 9 ;
|
||||
- PDA : `canonical`, `nonCanonical` et helper conditionnel `metadata` ;
|
||||
- erreurs : 5 ;
|
||||
- SHA-256 de la copie locale : `e6874f519d2ecaa8d92e2d58d1caabc130352c98b5a829375efd31636dbbc7a1`.
|
||||
|
||||
La structure, le Program ID, la version déclarée, les PDA, les comptes et les neuf instructions concordent avec l’IDL officielle consultée. La version `0.0.0` est conservée telle qu’elle est déclarée ; elle ne doit pas être interprétée comme une version fonctionnelle inventée par Khadhroony.
|
||||
|
||||
## 4. Contrat Solana Program Metadata
|
||||
|
||||
### 4.1 PDA
|
||||
|
||||
| PDA | Seeds |
|
||||
|---------------|------------------------------------------------------------------------------------------------------|
|
||||
| canonical | `[program, seed]` |
|
||||
| non-canonical | `[program, authority, seed]` |
|
||||
| metadata | helper conditionnel sélectionnant l’une des deux dérivations selon la présence d’une autorité tierce |
|
||||
|
||||
`seed` est une chaîne UTF-8 de taille fixe 16 octets dans l’IDL. Le code doit distinguer longueur en octets et nombre de caractères Unicode.
|
||||
|
||||
### 4.2 Comptes
|
||||
|
||||
`Buffer` contient : discriminateur, programme optionnel zeroable, autorité optionnelle zeroable, indicateur canonical, seed paddé à l’offset 14 et données restantes.
|
||||
|
||||
`Metadata` contient : discriminateur, programme, autorité optionnelle zeroable, indicateurs mutable et canonical, seed, encodage, compression, format, source de données, longueur de données paddée à l’offset 5 et données restantes.
|
||||
|
||||
Discriminateurs de compte : `Empty = 0`, `Buffer = 1`, `Metadata = 2`.
|
||||
|
||||
### 4.3 Types fermés
|
||||
|
||||
- `Encoding` : `None`, `Utf8`, `Base58`, `Base64` ;
|
||||
- `Compression` : `None`, `Gzip`, `Zlib` ;
|
||||
- `Format` : `None`, `Json`, `Yaml`, `Toml` ;
|
||||
- `DataSource` : `Direct`, `Url`, `External` ;
|
||||
- `ExternalData` : adresse, offset `u32`, longueur optionnelle zeroable `u32`.
|
||||
|
||||
Ces valeurs décrivent uniquement les données on-chain. `Url` ne déclenche aucun téléchargement et `External` ne transforme pas automatiquement un autre compte en observation canonique.
|
||||
|
||||
### 4.4 Instructions
|
||||
|
||||
| Discriminant | Instruction | Comptes | Arguments |
|
||||
|-------------:|----------------|----------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `0` | `write` | `buffer` (writable)<br>`authority` (signer)<br>`sourceBuffer` (optional) | `offset: u32`<br>`data: Option<bytes>` |
|
||||
| `1` | `initialize` | `metadata` (writable)<br>`authority` (signer)<br>`program`<br>`programData` (optional)<br>`system` (optional) | `seed: Seed`<br>`encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
|
||||
| `2` | `setAuthority` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `newAuthority: Option<pubkey>` |
|
||||
| `3` | `setData` | `metadata` (writable)<br>`authority` (signer)<br>`buffer` (writable, optional)<br>`program` (optional)<br>`programData` (optional) | `encoding: Encoding`<br>`compression: Compression`<br>`format: Format`<br>`dataSource: DataSource`<br>`data: Option<bytes>` |
|
||||
| `4` | `setImmutable` | `metadata` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | — |
|
||||
| `5` | `trim` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable)<br>`rent` | — |
|
||||
| `6` | `close` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`destination` (writable) | — |
|
||||
| `7` | `allocate` | `buffer` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional)<br>`system` (optional) | `seed: Option<Seed>` |
|
||||
| `8` | `extend` | `account` (writable)<br>`authority` (signer)<br>`program` (optional)<br>`programData` (optional) | `length: u16` |
|
||||
|
||||
### 4.5 Erreurs officielles
|
||||
|
||||
| Code | Erreur |
|
||||
|-----:|-----------------------------|
|
||||
| 0 | `NotExecutableAccount` |
|
||||
| 1 | `InvalidProgramState` |
|
||||
| 2 | `InvalidProgramDataAccount` |
|
||||
| 3 | `ImmutableMetadataAccount` |
|
||||
| 4 | `InvalidDataLength` |
|
||||
|
||||
## 5. Décisions de modèles et de validation `ProgM6…`
|
||||
|
||||
- le namespace reste `metadata/solana_program_metadata` ;
|
||||
- `pre.003` doit introduire des modèles publics propres à cette surface ;
|
||||
- aucune dépendance au modèle Metaplex ou Token-2022 ne doit être créée ;
|
||||
- le décodeur doit borner explicitement seed, tailles, offsets et longueurs avant allocation ;
|
||||
- les options zeroable et les options Borsh préfixées sont deux contrats distincts ;
|
||||
- les builders devront reproduire exactement les discriminateurs `u8` et les encodages little-endian ;
|
||||
- les comptes optionnels du programme/program-data ne doivent pas être inventés lorsqu’ils sont absents ;
|
||||
- la fixture Devnet doit utiliser un programme upgradeable contrôlé pour valider le chemin canonical ;
|
||||
- un autre programme quelconque peut être ciblé pour le chemin non-canonical, puisque l’autorité tierce signe ce chemin ;
|
||||
- les opérations irréversibles, notamment `SetImmutable`, seront soumises uniquement sur des fixtures jetables dédiées.
|
||||
|
||||
## 6. Matrice de couverture Token-2022 réelle
|
||||
|
||||
| Capacité | Décodage wire | Intent public | Builder | Exécution | Lecture stateful | Matérialisation | Scénario dédié | Desktop | Devnet confirmé |
|
||||
|-------------------|---------------|---------------|---------|----------------------------|----------------------------------|--------------------|----------------|-----------|-----------------|
|
||||
| `Initialize` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
|
||||
| `UpdateField` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
|
||||
| `RemoveKey` | oui | oui | oui | oui, via builder générique | état extension lisible | extension observée | non dédié | non dédié | non établi |
|
||||
| `UpdateAuthority` | oui | non | non | non | état extension lisible | extension observée | non | non | non |
|
||||
| `Emit` | oui | non | non | non | résultat de retour non orchestré | non dédiée | non | non | non |
|
||||
|
||||
Constats complémentaires :
|
||||
|
||||
- les cinq discriminateurs sont déjà reconnus dans `kb-lib/src/decoder/spl/token_2022/wire.rs` ;
|
||||
- le décodeur impose la consommation exacte du payload et rejette les trailing bytes ;
|
||||
- `UpdateField` prend déjà en charge `Name`, `Symbol`, `Uri` et une clé libre ;
|
||||
- `RemoveKey` décode déjà le booléen `idempotent` ;
|
||||
- `UpdateAuthority` décode déjà l’autorité nullable ;
|
||||
- `Emit` décode déjà les bornes optionnelles `start` et `end` ;
|
||||
- les intents/builders actuels s’arrêtent à `Initialize`, `UpdateField` et `RemoveKey` ;
|
||||
- le stateful Token-2022 sait relire l’extension `token_metadata`, mais il n’existe pas encore de parcours d’exécution et de postconditions propre aux cinq opérations ;
|
||||
- aucune preuve Devnet spécifique à cette matrice n’est actuellement conservée.
|
||||
|
||||
## 7. Découpage fonctionnel confirmé
|
||||
|
||||
`pre.003` reste consacré aux modèles Solana Program Metadata. Les phases suivantes conservent la séparation prévue : comptes, instructions, matérialisation, builders/exécuteur, pipeline, campagne Devnet, puis complétude Token-2022.
|
||||
|
||||
La complétude Token-2022 devra au minimum ajouter :
|
||||
|
||||
- intents et builders `UpdateTokenMetadataAuthority` et `EmitTokenMetadata` ;
|
||||
- politique de capacité et de signataires ;
|
||||
- validation des bornes de `Emit` ;
|
||||
- lecture et vérification post-exécution de l’update authority ;
|
||||
- capture exploitable du retour de `Emit` ;
|
||||
- campagne synthétique et Devnet dédiée ;
|
||||
- parcours desktop séparé de `ProgM6…`.
|
||||
|
||||
## 8. Critère de clôture de `pre.002`
|
||||
|
||||
Le contrat est considéré fermé pour commencer le code lorsque :
|
||||
|
||||
- les deux surfaces restent distinctes ;
|
||||
- l’IDL officielle locale est validée sans réécriture ;
|
||||
- les comptes, PDA, types, erreurs et neuf instructions sont inventoriés ;
|
||||
- la matrice Token-2022 distingue décodage déjà complet et exécution incomplète ;
|
||||
- les fixtures Devnet nécessaires sont décidées avant leur implémentation.
|
||||
|
||||
Ces conditions sont remplies. `0.4.8-pre.003` peut commencer par les modèles Solana Program Metadata.
|
||||
@@ -0,0 +1,152 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit `0.4.8-pre.005` — instructions et historique de Solana Program Metadata
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Cet audit détermine la surface exacte du décodeur d’instructions du programme :
|
||||
|
||||
```text
|
||||
ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S
|
||||
```
|
||||
|
||||
Il vérifie séparément :
|
||||
|
||||
- l’inventaire stable actuel ;
|
||||
- les écarts entre l’IDL Codama, les clients générés et le processeur on-chain ;
|
||||
- l’existence éventuelle d’instructions supprimées, remplacées ou obsolètes ;
|
||||
- la politique de couverture à appliquer dans `kb-lib`.
|
||||
|
||||
L’audit ne porte ni sur Metaplex Token Metadata, ni sur les metadata incorporées à Token-2022, ni sur la résolution off-chain des URL.
|
||||
|
||||
## 2. Sources de vérité
|
||||
|
||||
Les sources officielles retenues sont :
|
||||
|
||||
- les tags `program@v1.0.0` et `program@v1.0.1` du dépôt `solana-program/program-metadata` ;
|
||||
- `program/src/instruction.rs` et l’entrypoint du programme ;
|
||||
- les neuf processeurs sous `program/src/processor/` au tag `program@v1.0.1` ;
|
||||
- l’IDL Codama officielle conservée brute sous `idls/` ;
|
||||
- les clients Codama générés, utilisés pour distinguer le contrat SDK canonique des formes supplémentaires acceptées par le runtime ;
|
||||
- l’historique Git antérieur à la première release stable, utilisé uniquement comme provenance.
|
||||
|
||||
Le processeur `program@v1.0.1` est la source de vérité pour les formes réellement acceptées par le runtime actuel. L’IDL reste la source de vérité du contrat SDK publié.
|
||||
|
||||
## 3. Inventaire stable
|
||||
|
||||
Les deux releases stables auditées exposent le même inventaire de neuf instructions :
|
||||
|
||||
| Tag | Code Khadhroony | Instruction officielle | Statut |
|
||||
|----:|-----------------|------------------------|--------|
|
||||
| `0` | `write` | `Write` | stable |
|
||||
| `1` | `initialize` | `Initialize` | stable |
|
||||
| `2` | `set_authority` | `SetAuthority` | stable |
|
||||
| `3` | `set_data` | `SetData` | stable |
|
||||
| `4` | `set_immutable` | `SetImmutable` | stable |
|
||||
| `5` | `trim` | `Trim` | stable |
|
||||
| `6` | `close` | `Close` | stable |
|
||||
| `7` | `allocate` | `Allocate` | stable |
|
||||
| `8` | `extend` | `Extend` | stable |
|
||||
|
||||
Aucun discriminant stable supplémentaire n’a été trouvé. Aucun de ces neuf discriminants n’est marqué obsolète dans les releases stables auditées.
|
||||
|
||||
## 4. Historique antérieur à la première release stable
|
||||
|
||||
### 4.1 Tag `5`
|
||||
|
||||
Un commit de travail du 10 janvier 2025 a introduit une instruction nommée `WithdrawExcessLamports` au tag `5`. Le 19 février 2025, avant `program@v1.0.0`, ce même tag a été remplacé par `Trim`.
|
||||
|
||||
Cette évolution ne crée pas une dixième instruction à couvrir :
|
||||
|
||||
- le discriminant reste `5` ;
|
||||
- le contrat stable publié est `Trim` ;
|
||||
- aucune release stable auditée n’expose `WithdrawExcessLamports` comme entrée distincte ;
|
||||
- le comportement de `Trim` comprend désormais le redimensionnement du compte et le retrait de l’excédent de lamports.
|
||||
|
||||
Décision : le décodeur publie uniquement `trim` pour le tag `5`. Le nom pré-stable est conservé dans la provenance de l’observation et dans la matrice, sans entrée de couverture historique distincte.
|
||||
|
||||
### 4.2 Autres formes de travail
|
||||
|
||||
L’historique antérieur à `program@v1.0.0` contient plusieurs changements de wire, de comptes optionnels et de builders. Ils appartiennent à une phase explicitement marquée WIP dans le dépôt.
|
||||
|
||||
Aucune compatibilité réseau historique n’est déclarée pour ces formes tant qu’une preuve de déploiement et une transaction de cluster ne sont pas identifiées. Le décodeur ne doit pas inventer des entrées historiques uniquement à partir de commits de développement.
|
||||
|
||||
## 5. Écarts runtime / IDL à conserver
|
||||
|
||||
### 5.1 `Write`
|
||||
|
||||
Le runtime exige un offset `u32` puis sélectionne exactement une source :
|
||||
|
||||
- octets restants dans l’instruction ;
|
||||
- ou compte `source_buffer` lorsque le reliquat est vide.
|
||||
|
||||
Les deux sources simultanées et l’absence des deux sources échouent.
|
||||
|
||||
### 5.2 `Initialize`
|
||||
|
||||
Le header d’arguments fixe mesure 20 octets. Le reliquat peut être vide uniquement lorsque le compte metadata est déjà un `Buffer` préalloué ; sinon des données inline sont requises.
|
||||
|
||||
Le décodeur peut établir l’intention wire, mais la validité de cette condition dépend de l’état du compte et sera vérifiée par les lectures stateful futures.
|
||||
|
||||
### 5.3 `SetAuthority`
|
||||
|
||||
Le SDK encode une option canonique `0` ou `1`. Le runtime traite néanmoins tout flag non nul comme une nouvelle autorité et exige alors exactement 32 octets.
|
||||
|
||||
Pour le flag `0`, les octets suffixes sont ignorés sur le chemin `Metadata`. La suppression d’autorité reste interdite pour un `Buffer` et cette distinction nécessite l’état du compte.
|
||||
|
||||
### 5.4 `SetData`
|
||||
|
||||
L’IDL encode `encoding`, `compression`, `format`, `data_source` puis un reliquat optionnel. Le processeur accepte aussi une forme de trois octets contenant uniquement les trois premiers champs ; cette forme modifie le header sans remplacer les données existantes.
|
||||
|
||||
Lorsque `data_source` est présent :
|
||||
|
||||
- un reliquat non vide fournit les données inline et impose le placeholder pour `buffer` ;
|
||||
- un reliquat vide impose un véritable compte `buffer` ;
|
||||
- les deux sources ou aucune source sont rejetées.
|
||||
|
||||
### 5.5 Instructions sans arguments SDK
|
||||
|
||||
`SetImmutable`, `Trim` et `Close` ne reçoivent pas leur reliquat depuis l’entrypoint. Le runtime ignore donc les octets suivant leur tag, même si le client SDK canonique n’en produit aucun.
|
||||
|
||||
Le décodeur conserve leur longueur et leur préfixe comme suffixe ignoré ; il ne les interprète pas comme de nouveaux arguments.
|
||||
|
||||
### 5.6 Comptes supplémentaires
|
||||
|
||||
Le runtime accepte des comptes restants pour `Write`, `Initialize`, `SetData`, `Allocate` et `Extend`. Il exige une longueur exacte pour `SetAuthority`, `SetImmutable`, `Trim` et `Close`.
|
||||
|
||||
La matrice contractuelle encode explicitement cette différence.
|
||||
|
||||
## 6. Décision de décodage
|
||||
|
||||
Le décodeur `DcMetadataSolanaProgramMetadataDecoder` :
|
||||
|
||||
- reconnaît uniquement le Program ID officiel ;
|
||||
- déclare exactement neuf entrées de couverture non historiques ;
|
||||
- borne la taille de l’instruction et le nombre de comptes contextualisés ;
|
||||
- valide les discriminateurs, longueurs, enums, flags, ordre des comptes et contrats de sources ;
|
||||
- conserve les variantes runtime actuelles qui dépassent le contrat SDK ;
|
||||
- conserve les suffixes runtime ignorés sans leur attribuer de sémantique ;
|
||||
- produit une intention non committée pour une transaction échouée ;
|
||||
- ne télécharge aucune URL et ne lit aucun compte externe implicitement ;
|
||||
- ne déclare aucune forme pré-stable comme compatible réseau sans preuve de cluster.
|
||||
|
||||
## 7. Artefacts liés
|
||||
|
||||
- `test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json` ;
|
||||
- `kb-lib/src/decoder/metadata/solana_program_metadata/instruction.rs` ;
|
||||
- `kb-lib/tests/external_solana_program_metadata_instruction_api.rs` ;
|
||||
- `docs/IDL_AUDIT.md` ;
|
||||
- `docs/IDL_TO_KB_LIB_NOMENCLATURE.md`.
|
||||
|
||||
## 8. Statut de validation
|
||||
|
||||
| Niveau | Statut |
|
||||
|-------------------------------------------------|---------------------------------------------|
|
||||
| audit de l’IDL | validé |
|
||||
| audit des sources `program@v1.0.1` | validé |
|
||||
| comparaison `program@v1.0.0` / `program@v1.0.1` | validée, inventaire inchangé |
|
||||
| audit historique pré-stable | documenté, sans revendication réseau |
|
||||
| tests synthétiques Rust | à exécuter dans le workspace utilisateur |
|
||||
| replay de transactions réelles | planifié dans les phases pipeline et Devnet |
|
||||
| validation Devnet | non encore exécutée |
|
||||
@@ -0,0 +1,187 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Audit des conventions et de la structure des matérialisateurs — `0.4.8-pre.007-fix-004`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Cet audit couvre l’intégralité de `kb-lib/src/materializer` après l’activation du matérialisateur Solana Program Metadata et l’extraction des projections Metaplex Token Metadata et Token-2022.
|
||||
|
||||
Les contrôles portent sur :
|
||||
|
||||
- les identités runtime et les `processorName` persistés ;
|
||||
- les tracing targets ;
|
||||
- la structure des façades et sous-modules ;
|
||||
- l’emplacement, le nommage et la réexportation des constantes ;
|
||||
- les contrats communs `MtMaterializer` et `MtApiEventMaterializer` ;
|
||||
- la neutralité effective des matérialisateurs encore réservés ;
|
||||
- la provenance et les versions de projection des sorties actives.
|
||||
|
||||
## 2. Convention retenue
|
||||
|
||||
Chaque composant possède une paire d’identités issue de son `constants.rs` :
|
||||
|
||||
```text
|
||||
runtime component : kb-lib.materializer.<domain>[.<subsystem>]
|
||||
processorName : materializer.<domain>[.<subsystem>]
|
||||
```
|
||||
|
||||
Le `processorName` est exactement l’identité runtime privée du préfixe `kb-lib.`. Le tracing target d’un composant actif est identique à son identité runtime.
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
kb-lib.materializer.compliance.audit
|
||||
materializer.compliance.audit
|
||||
|
||||
kb-lib.materializer.transaction.annotations
|
||||
materializer.transaction.annotations
|
||||
|
||||
kb-lib.materializer.metadata.solana_program_metadata
|
||||
materializer.metadata.solana_program_metadata
|
||||
```
|
||||
|
||||
Les identités runtime servent au code et au routage des logs. Les `processorName` servent aux sorties persistées, au replay et à la provenance. Ils ne doivent pas être interchangeables.
|
||||
|
||||
## 3. Inventaire audité
|
||||
|
||||
| Composant | Type principal ou API | Statut | Identité runtime | `processorName` |
|
||||
|-------------------------|-----------------------------------------------|---------|--------------------------------------------------------|-------------------------------------------------|
|
||||
| Administration | `MtAdminMaterializer` | actif | `kb-lib.materializer.admin` | `materializer.admin` |
|
||||
| Bridge | `MtBridgeMaterializer` | réservé | `kb-lib.materializer.bridge` | `materializer.bridge` |
|
||||
| Compliance audit | `MtComplianceAuditMaterializer` | actif | `kb-lib.materializer.compliance.audit` | `materializer.compliance.audit` |
|
||||
| Fees | `MtFeesMaterializer` | actif | `kb-lib.materializer.fees` | `materializer.fees` |
|
||||
| Governance | `MtGovernanceMaterializer` | réservé | `kb-lib.materializer.governance` | `materializer.governance` |
|
||||
| Lending | `MtLendingMaterializer` | réservé | `kb-lib.materializer.lending` | `materializer.lending` |
|
||||
| Lifecycle | `MtLifecycleMaterializer` | actif | `kb-lib.materializer.lifecycle` | `materializer.lifecycle` |
|
||||
| Liquidity | `MtLiquidityMaterializer` | réservé | `kb-lib.materializer.liquidity` | `materializer.liquidity` |
|
||||
| Metaplex Token Metadata | `MtMetadataMetaplexTokenMetadataMaterializer` | actif | `kb-lib.materializer.metadata.metaplex_token_metadata` | `materializer.metadata.metaplex_token_metadata` |
|
||||
| Solana Program Metadata | `MtMetadataSolanaProgramMaterializer` | actif | `kb-lib.materializer.metadata.solana_program_metadata` | `materializer.metadata.solana_program_metadata` |
|
||||
| Token-2022 metadata | `MtMetadataToken2022Materializer` | actif | `kb-lib.materializer.metadata.token_2022` | `materializer.metadata.token_2022` |
|
||||
| NFT | `MtNftMaterializer` | réservé | `kb-lib.materializer.nft` | `materializer.nft` |
|
||||
| Oracle | `MtOracleMaterializer` | réservé | `kb-lib.materializer.oracle` | `materializer.oracle` |
|
||||
| Orderbook | `MtOrderbookMaterializer` | réservé | `kb-lib.materializer.orderbook` | `materializer.orderbook` |
|
||||
| Perpetuals | `MtPerpetualsMaterializer` | réservé | `kb-lib.materializer.perpetuals` | `materializer.perpetuals` |
|
||||
| Pool state | `MtPoolStateMaterializer` | réservé | `kb-lib.materializer.pool.state` | `materializer.pool.state` |
|
||||
| Rewards | `MtRewardsMaterializer` | réservé | `kb-lib.materializer.rewards` | `materializer.rewards` |
|
||||
| Risk | `MtRiskMaterializer` | actif | `kb-lib.materializer.risk` | `materializer.risk` |
|
||||
| Routing | `MtRoutingMaterializer` | réservé | `kb-lib.materializer.routing` | `materializer.routing` |
|
||||
| Staking | `MtStakingMaterializer` | actif | `kb-lib.materializer.staking` | `materializer.staking` |
|
||||
| Token accounts | `MtTokenAccountsMaterializer` | actif | `kb-lib.materializer.token.accounts` | `materializer.token.accounts` |
|
||||
| Token metadata risk | `MtTokenMetadataRiskMaterializer` | réservé | `kb-lib.materializer.token.metadata_risk` | `materializer.token.metadata_risk` |
|
||||
| Trades | `MtTradesMaterializer` | réservé | `kb-lib.materializer.trades` | `materializer.trades` |
|
||||
| Transaction annotations | `MtTransactionAnnotationMaterializer` | actif | `kb-lib.materializer.transaction.annotations` | `materializer.transaction.annotations` |
|
||||
| Vault | `MtVaultMaterializer` | réservé | `kb-lib.materializer.vault` | `materializer.vault` |
|
||||
|
||||
Bilan après correction `pre.008` : **25 composants nommés**, dont **11 matérialisateurs actifs** et **14 coquilles réservées**. Les trois domaines metadata exposent des types spécialisés ; aucun wrapper metadata générique ne masque leur ownership.
|
||||
|
||||
## 4. Structure normalisée
|
||||
|
||||
Un matérialisateur simple suit désormais la structure :
|
||||
|
||||
```text
|
||||
<component>.rs
|
||||
<component>/constants.rs
|
||||
<component>/materializer.rs
|
||||
```
|
||||
|
||||
La façade `<component>.rs` ne contient que les déclarations de sous-modules et leurs réexports. Les composants complexes peuvent ajouter des fichiers spécialisés, par exemple `state.rs` ou `account.rs`, sans placer d’implémentation dans la façade.
|
||||
|
||||
Les domaines imbriqués suivent la même règle :
|
||||
|
||||
```text
|
||||
compliance/audit.rs
|
||||
compliance/audit/constants.rs
|
||||
compliance/audit/materializer.rs
|
||||
|
||||
transaction/annotations.rs
|
||||
transaction/annotations/constants.rs
|
||||
transaction/annotations/materializer.rs
|
||||
```
|
||||
|
||||
Les anciens noms génériques `core.rs` ont été remplacés par `materializer.rs`. Les tables de surfaces, familles acceptées, identités, versions de projection et tracing targets résident dans les `constants.rs`, sont réexportées jusqu’à `kb-lib/src/lib.rs` et sont consommées via `crate::`.
|
||||
|
||||
## 5. Coquilles réservées
|
||||
|
||||
Une coquille réservée doit :
|
||||
|
||||
- conserver son type public `Mt*Materializer` ;
|
||||
- implémenter les deux traits communs ;
|
||||
- exposer une constante `MT_*_ACCEPTED_FAMILIES` vide ;
|
||||
- retourner `false` depuis l’ancien `accepts_event` ;
|
||||
- retourner une liste vide depuis l’ancien `materialize_event` ;
|
||||
- retourner `Ignored` depuis le contrat contextuel ;
|
||||
- ne produire aucun événement, aucune sortie et aucune provenance fictive.
|
||||
|
||||
Le comportement antérieur qui pouvait retourner un événement legacy malgré une frontière inactive a été supprimé.
|
||||
|
||||
## 6. Provenance et versionnement
|
||||
|
||||
Les sorties actives utilisent uniquement leur constante `MT_*_PROCESSOR_NAME`. Aucun `processorName` littéral ne reste dans les implémentations.
|
||||
|
||||
Les changements de provenance Metaplex et Token-2022 livrés dans `fix-002` restent associés à `projectionVersion = 2`. Solana Program Metadata conserve `projectionVersion = 1`, car sa projection est introduite dans `0.4.8`.
|
||||
|
||||
L’ajout d’une provenance explicite aux sorties qui ne possédaient encore qu’une version numérique modifie leur contrat persisté. Les matérialisateurs `admin`, `compliance.audit`, `fees`, `lifecycle` et `staking` passent donc de `projectionVersion = 1` à `projectionVersion = 2`. Les matérialisateurs `risk`, `token.accounts` et `transaction.annotations` conservaient déjà une provenance spécialisée et gardent leurs versions existantes.
|
||||
|
||||
Les `output_key` et les familles métier existantes ne sont pas renommées par cet audit.
|
||||
|
||||
## 7. Configuration de logging
|
||||
|
||||
Les routes de `config/example.config.json` sont alignées sur les tracing targets spécialisés :
|
||||
|
||||
```text
|
||||
kb-lib.materializer.compliance.audit
|
||||
kb-lib.materializer.transaction.annotations
|
||||
kb-lib.materializer.token.accounts
|
||||
```
|
||||
|
||||
Les noms de sinks et leurs chemins sont harmonisés avec les mêmes sous-domaines.
|
||||
|
||||
## 8. Garde-fous ajoutés
|
||||
|
||||
L’audit mécanique du workspace vérifie désormais :
|
||||
|
||||
- la paire obligatoire `MT_*_COMPONENT_NAME` / `MT_*_PROCESSOR_NAME` ;
|
||||
- la relation exacte obtenue par suppression du préfixe `kb-lib.` ;
|
||||
- l’absence de constantes hors `constants.rs` ;
|
||||
- la présence d’un `constants.rs` à côté de chaque implémentation ;
|
||||
- l’absence d’identités littérales dans les implémentations ;
|
||||
- la présence conjointe de `projectionVersion` et `processorName` dans chaque payload versionné ;
|
||||
- l’utilisation obligatoire des constantes `MT_*_PROJECTION_VERSION` et `MT_*_PROCESSOR_NAME` dans ces payloads ;
|
||||
- la pureté des façades ;
|
||||
- le résultat vide des deux contrats d’une coquille réservée.
|
||||
|
||||
Les tests Rust conservent en plus l’inventaire typé des matérialisateurs et contrôlent la convention de nommage.
|
||||
|
||||
## 9. Fichiers obsolètes à supprimer
|
||||
|
||||
Les 22 chemins retirés sont fournis dans `delete-files.txt` à la racine du delta. Leur suppression est obligatoire avant la validation, car une extraction ZIP ne peut pas retirer les anciens fichiers.
|
||||
|
||||
## 10. Conclusion
|
||||
|
||||
Les matérialisateurs utilisent désormais une convention unique pour les identités runtime, les processeurs persistés, les tracing targets, les constantes, les façades et les coquilles réservées. Un composant peut être actif ou réservé ; les APIs stateful restent des capacités de son matérialiseur spécialisé et ne constituent pas un type générique parallèle.
|
||||
|
||||
## 11. Correction des identités opérationnelles et du tracing
|
||||
|
||||
La compilation du workspace corrigé a révélé trois tracing targets déclarés mais sans événement réel :
|
||||
|
||||
- `kb-lib.materializer.fees` ;
|
||||
- `kb-lib.materializer.metadata.metaplex_token_metadata` ;
|
||||
- `kb-lib.materializer.metadata.token_2022`.
|
||||
|
||||
Le composant de snapshots Token-2022 réexportait également `MT_METADATA_TOKEN_2022_COMPONENT_NAME` sans le consommer dans son implémentation. Cette situation ne signifiait pas que les snapshots Token-2022 n’étaient pas matérialisés : la fonction `materializer_metadata_materialize_token_2022_snapshot` produisait déjà les projections `TokenMetadata`, `TokenGroup` et `TokenGroupMember`. Elle révélait en revanche que l’identité runtime du composant n’était pas attachée à une opération observable.
|
||||
|
||||
Le correctif ajoute des événements structurés réels pour les chemins suivants :
|
||||
|
||||
- observation et snapshots de frais Token-2022 ;
|
||||
- snapshots de comptes Metaplex et `MetadataV1` ;
|
||||
- snapshots metadata, group et group-member Token-2022.
|
||||
|
||||
Les événements portent le tracing target du composant, son `component_name`, son `processor_name`, sa version, le statut et les identités disponibles. Aucun faux `let _target` n’est utilisé et aucun champ persistant n’est ajouté uniquement pour supprimer un warning.
|
||||
|
||||
L’audit mécanique refuse désormais :
|
||||
|
||||
- un `TRACING_TARGET_*` de `kb-lib` déclaré sans macro `tracing` réelle ;
|
||||
- un `MT_*_COMPONENT_NAME` qui n’est consommé par aucun fichier d’implémentation du composant.
|
||||
|
||||
La correction `pre.008` ajoute les matérialiseurs instructionnels spécialisés Metaplex et Token-2022 parce que leurs instructions sont effectivement décodées. Leurs APIs de snapshots restent distinctes et autoritatives, tandis que les sorties d’instructions restent des faits committés non autoritatifs.
|
||||
@@ -0,0 +1,109 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_007_METAPLEX_MATERIALIZER_STRUCTURE_AUDIT.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Audit `0.4.8-pre.007` — structure et identités des matérialiseurs metadata
|
||||
|
||||
## 1. Questions vérifiées
|
||||
|
||||
Deux ambiguïtés structurelles ont été vérifiées :
|
||||
|
||||
- l’absence initiale d’un sous-module dédié à Metaplex Token Metadata ;
|
||||
- l’emplacement et la couverture de la matérialisation Token-2022 Token Metadata.
|
||||
|
||||
L’audit a également séparé l’identité runtime d’un composant de son `processorName` persisté.
|
||||
|
||||
## 2. Metaplex Token Metadata
|
||||
|
||||
Il n’existait pas d’oubli fonctionnel en `0.4.7`. Les APIs publiques suivantes étaient déjà implémentées :
|
||||
|
||||
- `materializer_metadata_materialize_metaplex_metadata_snapshot` pour l’état autoritatif `MetadataV1` ;
|
||||
- `materializer_metadata_materialize_metaplex_account_snapshot` pour les autres comptes Metaplex canoniques matérialisables.
|
||||
|
||||
Le défaut était structurel : ces fonctions partageaient auparavant `metadata/core.rs` avec d’autres domaines. Elles sont désormais isolées dans :
|
||||
|
||||
```text
|
||||
kb-lib/src/materializer/metadata/metaplex_token_metadata.rs
|
||||
kb-lib/src/materializer/metadata/metaplex_token_metadata/constants.rs
|
||||
kb-lib/src/materializer/metadata/metaplex_token_metadata/state.rs
|
||||
kb-lib/src/materializer/metadata/metaplex_token_metadata/account.rs
|
||||
```
|
||||
|
||||
Les clés de sortie et la sémantique métier restent inchangées. La provenance utilise désormais le `processorName` dédié :
|
||||
|
||||
```text
|
||||
materializer.metadata.metaplex_token_metadata
|
||||
```
|
||||
|
||||
Comme cette valeur persistée change par rapport au contrat antérieur, `projectionVersion` passe de `1` à `2`.
|
||||
|
||||
## 3. Token-2022 Token Metadata
|
||||
|
||||
Les états Token-2022 étaient déjà matérialisés par :
|
||||
|
||||
```text
|
||||
materializer_metadata_materialize_token_2022_snapshot
|
||||
```
|
||||
|
||||
Cette fonction projette les extensions de mint suivantes :
|
||||
|
||||
- `TokenMetadata` ;
|
||||
- `TokenGroup` ;
|
||||
- `TokenGroupMember`.
|
||||
|
||||
Elle était cependant placée dans `metadata/core.rs`. Le correctif l’extrait vers :
|
||||
|
||||
```text
|
||||
kb-lib/src/materializer/metadata/token_2022.rs
|
||||
kb-lib/src/materializer/metadata/token_2022/constants.rs
|
||||
kb-lib/src/materializer/metadata/token_2022/state.rs
|
||||
```
|
||||
|
||||
La fonction publique et les `output_key` restent inchangées. Sa provenance utilise :
|
||||
|
||||
```text
|
||||
materializer.metadata.token_2022
|
||||
```
|
||||
|
||||
Le schéma de projection passe à `2` pour refléter cette correction d’identité.
|
||||
|
||||
## 4. Écart fonctionnel Token-2022 restant
|
||||
|
||||
La matérialisation d’état ne couvre pas à elle seule toutes les observations décodées.
|
||||
|
||||
Les faits `InitializeMetadataPointer` et `UpdateMetadataPointer` sont déjà possédés par le matérialiseur d’administration. En revanche, les cinq observations de `spl-token-metadata-interface` ne disposent pas encore d’une matérialisation d’instruction dédiée :
|
||||
|
||||
- `Initialize` ;
|
||||
- `UpdateField` ;
|
||||
- `RemoveKey` ;
|
||||
- `UpdateAuthority` ;
|
||||
- `Emit`.
|
||||
|
||||
Cet écart doit être fermé dans `0.4.8-pre.010`, en même temps que la complétude des intents, builders, lectures avant/après et postconditions Token-2022.
|
||||
|
||||
## 5. Solana Program Metadata
|
||||
|
||||
L’identité runtime du composant reste :
|
||||
|
||||
```text
|
||||
kb-lib.materializer.metadata.solana_program_metadata
|
||||
```
|
||||
|
||||
Le `processorName` persistant du composant Solana Program Metadata est corrigé en :
|
||||
|
||||
```text
|
||||
materializer.metadata.solana_program_metadata
|
||||
```
|
||||
|
||||
Le tracing target reste distinct et conserve son préfixe runtime :
|
||||
|
||||
```text
|
||||
kb-lib.materializer.metadata.solana_program_metadata
|
||||
```
|
||||
|
||||
## 6. Conséquence de migration
|
||||
|
||||
Les projections dérivées Metaplex et Token-2022 produites avec les anciennes provenances doivent être reconstruites depuis les raws ou migrées si elles sont conservées. Les clés de sortie ne changent pas.
|
||||
|
||||
## 7. Conclusion
|
||||
|
||||
Metaplex et les états metadata Token-2022 étaient déjà matérialisés. `pre.007-fix-002` corrige leurs identités et leur organisation modulaire. `pre.007-fix-003` étend ensuite la même convention à l’intégralité des matérialisateurs ; voir `V0_4_8_PRE_007_MATERIALIZER_CONVENTION_AUDIT.md`. La seule lacune fonctionnelle confirmée concerne les cinq faits d’instructions `spl-token-metadata-interface`, explicitement reportés à `pre.010`.
|
||||
@@ -0,0 +1,52 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_008_METADATA_AND_PIPELINE_NOMENCLATURE_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit de nomenclature metadata et pipeline
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Cet audit corrige deux dérives révélées après l’intégration pipeline de `0.4.8-pre.008` : un matérialiseur Solana Program Metadata nommé hors de la hiérarchie `MtMetadata*`, et des modules `kb-pipeline` préfixés uniformément par `solana_` malgré des domaines Metaplex ou SPL distincts.
|
||||
|
||||
## 2. Matérialiseurs metadata
|
||||
|
||||
La surface publique canonique comprend désormais exactement trois types spécialisés :
|
||||
|
||||
| Domaine | Type | Processor |
|
||||
|---------------------------|-----------------------------------------------|-------------------------------------------------|
|
||||
| Metaplex Token Metadata | `MtMetadataMetaplexTokenMetadataMaterializer` | `materializer.metadata.metaplex_token_metadata` |
|
||||
| Solana Program Metadata | `MtMetadataSolanaProgramMaterializer` | `materializer.metadata.solana_program_metadata` |
|
||||
| Token-2022 metadata/group | `MtMetadataToken2022Materializer` | `materializer.metadata.token_2022` |
|
||||
|
||||
Le type `MtSolanaProgramMetadataMaterializer` et le wrapper `MtMetadataMaterializer` sont supprimés. Chaque type implémente les contrats legacy et contextuel, possède ses constantes, son tracing target et son ownership instructionnel. Les helpers `state.rs` et `account.rs` restent séparés : ils produisent les snapshots autoritatifs, alors que les matérialiseurs d’instructions produisent des faits committés non autoritatifs.
|
||||
|
||||
Les neuf instructions incorporées Token Metadata et Token Group sont classées sous `MdEventFamily::Metadata`. Cette classification évite de confier les changements d’autorité propres aux interfaces metadata au matérialiseur d’administration générique.
|
||||
|
||||
## 3. Registre desktop
|
||||
|
||||
`demo_decode_replay` enregistre les trois types spécialisés. Un test construit une observation représentative de chaque domaine et vérifie qu’elle possède exactement un propriétaire dans le registre runtime.
|
||||
|
||||
## 4. Nomenclature `kb-pipeline`
|
||||
|
||||
Les modules privés suivent la hiérarchie suivante :
|
||||
|
||||
- `metadata_metaplex_token_metadata_*` ;
|
||||
- `metadata_solana_program_*` ;
|
||||
- `spl_ata_stateful` ;
|
||||
- `spl_elgamal_registry_stateful` ;
|
||||
- `spl_token_stateful` ;
|
||||
- `spl_token_2022_*` ;
|
||||
- `solana_stateful` uniquement pour les contrats Solana Core transversaux.
|
||||
|
||||
Les fonctions et structures publiques restent inchangées. Le renommage est donc structurel et documentaire, sans rupture volontaire de l’API consommée par le desktop ou les futurs scénarios Devnet.
|
||||
|
||||
## 5. Fichiers remplacés
|
||||
|
||||
Les anciens fichiers `solana_*` de `kb-pipeline`, l’ancien test externe et l’ancienne matrice sont listés dans `delete-files.txt`. Leur suppression est obligatoire après extraction du delta.
|
||||
|
||||
## 6. Garde-fous
|
||||
|
||||
- les exports racine couvrent les trois matérialiseurs spécialisés ;
|
||||
- le test externe n’utilise que la façade `kb_lib::*` ;
|
||||
- les familles Token-2022 metadata/group sont testées exhaustivement ;
|
||||
- le test externe pipeline utilise des assertions de compilation `const` ;
|
||||
- l’audit des règles vérifie les façades, les exports, les constantes et la nomenclature des composants.
|
||||
@@ -0,0 +1,113 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_008_METADATA_SOLANA_PROGRAM_PIPELINE_AUDIT.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Audit du pipeline Solana Program Metadata — `0.4.8-pre.008`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Cet audit ferme la frontière réutilisable de Solana Program Metadata dans `kb-pipeline` sans introduire de fixture, de wallet de démonstration ni de dépendance envers `kb-pipeline-demo-scenarios`.
|
||||
|
||||
La surface couverte est le programme :
|
||||
|
||||
```text
|
||||
ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S
|
||||
```
|
||||
|
||||
## 2. Séparation des responsabilités
|
||||
|
||||
| Crate ou composant | Responsabilité en `pre.008` |
|
||||
|-----------------------------------------------|----------------------------------------------------------------------------------------|
|
||||
| `kb-lib` | modèles, décodage, matérialisation, intents, builders, exécuteur et safety commun |
|
||||
| `kb-pipeline` | lectures stateful, préflight, readiness simulation-first et postconditions |
|
||||
| `kb-pipeline-demo-scenarios` | aucune modification ; fixture et parcours Devnet réservés à `pre.009` |
|
||||
| `kb-app-demo-desktop/demo_decode_replay` | enregistrement générique du décodeur et du matérialiseur pour la validation historique |
|
||||
| `kb-app-demo-desktop/demo_execution_metadata` | aucune modification ; intégration `ProgM6…` réservée à `pre.009` |
|
||||
|
||||
## 3. Lectures stateful
|
||||
|
||||
Le pipeline distingue explicitement :
|
||||
|
||||
- compte absent ;
|
||||
- compte présent mais vide ou intégralement nul ;
|
||||
- compte `Buffer` initialisé ;
|
||||
- compte `Metadata` initialisé.
|
||||
|
||||
Les réponses RPC doivent être confirmées, non exécutables, complètes et sous la borne maximale. Un état initialisé doit être détenu par `ProgM6…`. Le décodage et la projection autoritative sont délégués aux APIs publiques de `kb-lib`.
|
||||
|
||||
## 4. Préflight
|
||||
|
||||
Le préflight lie l’intent typé au plan préparé et contrôle :
|
||||
|
||||
- le Program ID et le code d’opération exacts ;
|
||||
- l’unique instruction du plan ;
|
||||
- l’approbation explicite des mutations sensibles ;
|
||||
- la décision de `ExSafetyChecker` ;
|
||||
- les snapshots confirmés et non dupliqués ;
|
||||
- l’état du compte cible ;
|
||||
- les sources `Buffer` ;
|
||||
- l’autorité explicite ou le contexte canonical ;
|
||||
- le préfinancement rent des opérations d’allocation ou d’agrandissement.
|
||||
|
||||
Une décision `Deny` bloque le traitement. Une décision `RequireConfirmation` reste exécutable mais doit être satisfaite dans l’enveloppe de readiness.
|
||||
|
||||
## 5. Readiness et postconditions
|
||||
|
||||
La readiness refuse :
|
||||
|
||||
- un plan non lié au préflight ;
|
||||
- un message non simulé ou simulé sous un autre hash ;
|
||||
- une simulation en échec ;
|
||||
- un signer requis non résolu ;
|
||||
- une soumission sans confirmation opérateur ;
|
||||
- une soumission depuis un plan `dry_run`.
|
||||
|
||||
Les neuf opérations possèdent une postcondition stateful :
|
||||
|
||||
| Opération | Postcondition principale |
|
||||
|----------------|-----------------------------------------------------------------|
|
||||
| `Write` | octets attendus au bon offset du `Buffer` |
|
||||
| `Initialize` | état `Metadata`, PDA, seed, descripteurs et contenu compatibles |
|
||||
| `SetAuthority` | autorité finale exacte ou supprimée |
|
||||
| `SetData` | descripteurs et contenu final compatibles |
|
||||
| `SetImmutable` | `mutable = false` |
|
||||
| `Trim` | même type d’état et taille non croissante |
|
||||
| `Close` | compte absent |
|
||||
| `Allocate` | état `Buffer`, autorité, seed et canonicité compatibles |
|
||||
| `Extend` | même type d’état et taille augmentée exactement |
|
||||
|
||||
Une lecture finale absente du rapport produit `NotApplicable`, jamais `Confirmed`.
|
||||
|
||||
## 6. Validation historique prévue
|
||||
|
||||
La validation historique opérateur suit :
|
||||
|
||||
1. `demo_backfill` sur un échantillon borné de signatures ;
|
||||
2. extraction Core ;
|
||||
3. decode replay ciblé sur `ProgM6…` et `metadata.solana_program_metadata` ;
|
||||
4. matérialisation via `materializer.metadata.solana_program_metadata` ;
|
||||
5. lecture bornée des événements matérialisés.
|
||||
|
||||
Cette validation précède les scénarios Devnet de `pre.009` et ne dépend pas de leur fixture.
|
||||
|
||||
## 7. Matrice
|
||||
|
||||
La matrice machine-readable est :
|
||||
|
||||
```text
|
||||
test-fixtures/contract-matrices/METADATA_SOLANA_PROGRAM_PIPELINE_MATRIX.json
|
||||
```
|
||||
|
||||
Elle distingue explicitement la crate généraliste du package de scénarios et ferme l’inventaire des neuf opérations.
|
||||
## 8. Correction de nomenclature `pre.008`
|
||||
|
||||
Les modules privés de `kb-pipeline` sont renommés selon leur domaine :
|
||||
|
||||
- `metadata_metaplex_token_metadata_*` pour Metaplex Token Metadata ;
|
||||
- `metadata_solana_program_*` pour Solana Program Metadata ;
|
||||
- `spl_ata_stateful` et `spl_elgamal_registry_stateful` pour les programmes SPL correspondants ;
|
||||
- `spl_token_stateful` et `spl_token_2022_*` pour SPL Token et Token-2022 ;
|
||||
- `solana_stateful` reste réservé aux primitives Solana Core transversales.
|
||||
|
||||
Les fonctions et structures publiques restent stables. La matrice et le test externe suivent la même nomenclature de fichier.
|
||||
|
||||
Le registre de replay desktop utilise désormais trois matérialiseurs metadata spécialisés : `MtMetadataMetaplexTokenMetadataMaterializer`, `MtMetadataSolanaProgramMaterializer` et `MtMetadataToken2022Materializer`. Aucun wrapper générique `MtMetadataMaterializer` n’est conservé.
|
||||
@@ -0,0 +1,54 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_008_SOLANA_PROGRAM_METADATA_HISTORICAL_REPLAY_FIX.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Correctif du replay historique Solana Program Metadata
|
||||
|
||||
## 1. Validation ayant révélé le défaut
|
||||
|
||||
La validation Mainnet a importé deux pages de 100 transactions du programme `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S`, puis extrait 200 transactions et rejoué 582 instructions. Le processeur `metadata.solana_program_metadata` a reconnu et dispatché 132 instructions, mais les 132 ont terminé en échec de décodage.
|
||||
|
||||
Les surfaces observées étaient :
|
||||
|
||||
| Instruction | Observations |
|
||||
|-----------------|-------------:|
|
||||
| `write` | 88 |
|
||||
| `allocate` | 15 |
|
||||
| `initialize` | 11 |
|
||||
| `set_authority` | 7 |
|
||||
| `close` | 5 |
|
||||
| `set_data` | 5 |
|
||||
| `set_immutable` | 1 |
|
||||
| `trim` | 0 |
|
||||
| `extend` | 0 |
|
||||
|
||||
Le fait que toutes les instructions observées échouent malgré des discriminants reconnus et des transactions réussies indique un défaut transversal après reconnaissance, et non sept défauts wire indépendants.
|
||||
|
||||
## 2. Cause
|
||||
|
||||
`MdCoreInstructionReplayInput.account_keys_json` est produit depuis l’en-tête du message Solana. Ses champs `signer` et `writable` représentent donc les privilèges globaux de chaque clé pour l’ensemble de la transaction.
|
||||
|
||||
Le décodeur comparait ces champs par égalité avec les privilèges minimaux documentés pour chaque position de l’instruction. Cette égalité est invalide : une autorité peut être readonly dans le contrat de l’instruction tout en étant writable au niveau du message parce qu’elle est aussi fee payer ou intervient comme compte writable dans une autre instruction.
|
||||
|
||||
Le programme on-chain vérifie les privilèges nécessaires, notamment `authority.is_signer()`, mais ne refuse pas un sur-ensemble de privilèges fourni par le message.
|
||||
|
||||
## 3. Correction
|
||||
|
||||
La validation applique désormais les règles suivantes :
|
||||
|
||||
- un compte déclaré signer doit être signer dans le message ;
|
||||
- un compte déclaré writable doit être writable dans le message ;
|
||||
- un compte déclaré readonly peut être globalement writable ;
|
||||
- un compte déclaré non-signer peut être globalement signer ;
|
||||
- les positions, rôles, placeholders optionnels et contrats de source restent vérifiés séparément ;
|
||||
- l’absence d’un privilège requis reste un échec fermé.
|
||||
|
||||
Une régression couvre les neuf instructions stables avec une autorité globalement signer et writable.
|
||||
|
||||
## 4. Validation à rejouer
|
||||
|
||||
Après application du correctif, relancer les 132 entrées en échec, de préférence avec le replay forcé limité au programme `ProgM6…`. Le résultat attendu est :
|
||||
|
||||
- `decoded = 132` ;
|
||||
- `failed = 0` ;
|
||||
- une matérialisation pour chaque instruction committée ;
|
||||
- `trim` et `extend` restent non observés dans cet échantillon et devront être couverts par les scénarios Devnet.
|
||||
@@ -0,0 +1,62 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_009_METADATA_SOLANA_PROGRAM_DEVNET_SCENARIOS_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit `0.4.8-pre.009` — scénarios Devnet Solana Program Metadata
|
||||
|
||||
## Frontière de crate
|
||||
|
||||
`kb-pipeline` conserve les contrats réutilisables indépendants du cluster. `kb-pipeline-demo-scenarios` possède les wallets de démonstration, fixtures, appels RPC Devnet et campagnes destructrices. Aucun lien inverse n’est ajouté.
|
||||
|
||||
`kb-app-demo-desktop` n’est pas modifié dans cette prerelease. Son intégration est réservée à `pre.010`.
|
||||
|
||||
## Fixture
|
||||
|
||||
La fixture utilise le wallet persistant du profil Devnet comme autorité et le System Program comme programme exécutable décrit. Deux PDA non canoniques uniques sont dérivés avec des seeds UTF-8 de seize octets :
|
||||
|
||||
- un PDA `Buffer` ;
|
||||
- un PDA `Metadata`.
|
||||
|
||||
Les deux adresses doivent être absentes avant préparation. Leur rent est calculé par RPC et les comptes sont préfinancés par des transferts System Program simulation-first, évalués par `ExSafetyChecker`, signés, soumis et confirmés.
|
||||
|
||||
## Parcours fermés
|
||||
|
||||
### Buffer
|
||||
|
||||
1. `Allocate` ;
|
||||
2. `Extend` ;
|
||||
3. `Write` ;
|
||||
4. `Trim` ;
|
||||
5. `Close`.
|
||||
|
||||
`Extend` précède `Write`, car l’allocation initiale ne réserve que l’en-tête et l’écriture exige la capacité du payload.
|
||||
|
||||
### Metadata
|
||||
|
||||
1. `Initialize` ;
|
||||
2. `SetData` ;
|
||||
3. `SetAuthority` ;
|
||||
4. `SetImmutable`.
|
||||
|
||||
Les neuf opérations stables sont couvertes exactement une fois. Les six opérations classées sensibles par l’exécuteur reçoivent une approbation explicite.
|
||||
|
||||
## Orchestration
|
||||
|
||||
Chaque étape passe par :
|
||||
|
||||
- construction d’un intent typé ;
|
||||
- construction et évaluation du plan ;
|
||||
- lectures stateful avant exécution ;
|
||||
- préflight et preuves de rent ;
|
||||
- transaction exacte et simulation RPC ;
|
||||
- readiness du pipeline ;
|
||||
- évaluation `ExSafetyChecker` avant envoi ;
|
||||
- signature, soumission et confirmation ;
|
||||
- lectures stateful après confirmation ;
|
||||
- postcondition ;
|
||||
- projection matérialisée pour les comptes existants.
|
||||
|
||||
`Close` exige une preuve d’absence et non un snapshot matérialisé.
|
||||
|
||||
## Statut réseau
|
||||
|
||||
Le code et la matrice sont livrés avec des statuts `not_run`. Aucune validation Devnet n’est déclarée avant l’exécution opérateur de `pre.010`.
|
||||
@@ -0,0 +1,128 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_010_METADATA_SOLANA_PROGRAM_DESKTOP_INTEGRATION_AUDIT.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Audit `0.4.8-pre.010` — structure Metadata et exécution Devnet
|
||||
|
||||
## Périmètre
|
||||
|
||||
La prerelease raccorde `demo_execution_metadata` à la campagne Devnet Solana Program Metadata réutilisable. Le correctif `pre.010-fix-001` traite trois écarts révélés pendant la validation réelle :
|
||||
|
||||
- la structure desktop ne distinguait pas explicitement les contrats communs, Metaplex Token Metadata et Solana Program Metadata ;
|
||||
- le pipeline stateful demandait une lecture complète de `10 MiB`, alors que l’adaptateur `getAccountInfo` borne une réponse complète à `65536` octets ;
|
||||
- l’endpoint public Devnet a renvoyé `429 Too Many Requests` pendant une seconde campagne rapprochée.
|
||||
|
||||
## Structure desktop normalisée
|
||||
|
||||
La surface Rust est séparée en trois modules :
|
||||
|
||||
```text
|
||||
kb-app-demo-desktop/src/demo_execution_metadata.rs
|
||||
kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs
|
||||
kb-app-demo-desktop/src/demo_execution_metadata_solana_program.rs
|
||||
```
|
||||
|
||||
`demo_execution_metadata.rs` contient uniquement les contrats partagés du panneau :
|
||||
|
||||
- options de profils Devnet ;
|
||||
- payload de progression Metadata ;
|
||||
- observer Metadata commun aux workflows Metaplex et Solana Program Metadata.
|
||||
|
||||
Le module Metaplex contient les contrats, préparateurs et commandes propres à `metadata.metaplex_token_metadata`. Le module Solana Program Metadata contient l’inventaire et l’adaptation de la campagne `metadata.solana_program_metadata`.
|
||||
|
||||
Les types et commandes Tauri Metaplex portent désormais explicitement le segment `metaplex_token_metadata`.
|
||||
|
||||
## Progression et verrou d’exécution
|
||||
|
||||
Le workflow Metaplex réutilisait auparavant `DemoExecutionSolanaCoreObserver`, ce qui ciblait la fenêtre `demo_execution_solana_core`. Les deux workflows Metadata utilisent désormais `DemoExecutionMetadataObserver`, qui émet exclusivement vers :
|
||||
|
||||
```text
|
||||
window = demo_execution_metadata
|
||||
event = demo-execution-metadata-progress
|
||||
```
|
||||
|
||||
Le garde RAII partagé est déplacé dans `demo_devnet_common.rs` sous le nom `DemoExecutionRunGuard`. Son identité ne dépend plus de Solana Core alors qu’il protège également les exécutions SPL et Metadata.
|
||||
|
||||
## Borne des lectures stateful
|
||||
|
||||
Deux limites distinctes sont conservées :
|
||||
|
||||
- `kb_lib::DC_METADATA_SPM_MAX_ACCOUNT_BYTES = 10 MiB` : capacité défensive du décodeur pour des octets déjà disponibles, notamment offline ou via un futur lecteur par tranches ;
|
||||
- `kb_onchain_transport::MAX_COMPLETE_ACCOUNT_DATA_BYTES = 65536` : maximum actuel d’une lecture complète via l’adaptateur d’exécution `getAccountInfo`.
|
||||
|
||||
`kb_pipeline::MAX_SOLANA_PROGRAM_METADATA_STATEFUL_ACCOUNT_BYTES` est alignée sur la seconde limite. Une lecture complète ne peut donc plus être construite avec une valeur que le transport refusera avant l’appel RPC.
|
||||
|
||||
## Résilience aux limites RPC
|
||||
|
||||
`kb-onchain-transport` réessaie désormais les réponses suivantes :
|
||||
|
||||
- statut HTTP `429` ;
|
||||
- erreur JSON-RPC de code `429`.
|
||||
|
||||
Le retry est borné à quatre tentatives supplémentaires. Le délai :
|
||||
|
||||
1. utilise `Retry-After` lorsqu’il contient un nombre de secondes ;
|
||||
2. utilise sinon `pause_after_rate_limit_ms` du rôle compatible ;
|
||||
3. applique un backoff exponentiel plafonné à `60000 ms`.
|
||||
|
||||
Le même objet JSON-RPC est réutilisé. Pour `sendTransaction`, la transaction signée et sa signature restent donc identiques pendant les retries.
|
||||
|
||||
## Validation attendue
|
||||
|
||||
Après application du correctif :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo clippy --all-targets
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo test -p kb-onchain-transport
|
||||
cargo test -p kb-pipeline
|
||||
cargo test -p kb-app-demo-desktop
|
||||
```
|
||||
|
||||
La campagne Devnet doit ensuite être relancée une seule fois. Les résultats attendus sont :
|
||||
|
||||
- aucun rejet local sur `max_data_bytes` ;
|
||||
- retry visible et borné en cas de `429` ;
|
||||
- deux préfinancements confirmés ;
|
||||
- neuf opérations confirmées ;
|
||||
- snapshots stateful présents, sauf preuve d’absence après `Close`.
|
||||
|
||||
## 8. Correctif de débit HTTP et cible de logging
|
||||
|
||||
La campagne Devnet a montré que les limites de rôle existaient dans la configuration, mais n’étaient pas appliquées par le client HTTP générique utilisé par les RPC d’exécution. Le backfill disposait de son propre pacer ; cette protection ne couvrait pas les lectures stateful, simulations, soumissions et confirmations du desktop.
|
||||
|
||||
Le client HTTP applique désormais avant chaque tentative :
|
||||
|
||||
1. un token bucket partagé fondé sur `requests_per_second` et `burst_capacity` ;
|
||||
2. un sémaphore partagé fondé sur `max_concurrent_requests` ;
|
||||
3. le rôle exact retenu par `HttpEndpointPool` lorsque plusieurs rôles acceptent le même type de requête ;
|
||||
4. un cooldown partagé après une réponse HTTP ou JSON-RPC `429`.
|
||||
|
||||
Le retry borné reste nécessaire : un endpoint public peut imposer un quota dynamique ou partagé avec d’autres processus, adresses IP ou utilisateurs. Un warning `retry_http_json_rpc_after_rate_limit` signifie que la seconde ligne de défense a été activée ; il ne signifie pas que la requête a été perdue.
|
||||
|
||||
La fixture Solana Program Metadata réutilise en outre la cible racine `kb-pipeline-demo-scenarios`. La crate ne déclare donc plus deux constantes de tracing lorsqu’elle possède une cible racine canonique.
|
||||
|
||||
## 9. Diagnostic Devnet du correctif suivant
|
||||
|
||||
La campagne réelle postérieure à l’activation des limiteurs confirme que les réponses `429` sont récupérées : chaque warning observé est suivi du même `request_id` avec `retry_index = 1` et d’une réponse `200 OK`. Elles ralentissent la campagne mais ne constituent pas sa cause terminale.
|
||||
|
||||
L’échec reproductible intervient sur `SetAuthority` après `Initialize` et `SetData` :
|
||||
|
||||
```text
|
||||
InstructionError[0] = InvalidAccountData
|
||||
```
|
||||
|
||||
La fixture initialisait un compte Metadata non canonique puis tentait de réaffirmer son autorité. Le processeur officiel refuse explicitement `SetAuthority` sur un compte Metadata dont le drapeau `canonical` est faux. La même opération est en revanche autorisée sur un Buffer non canonique dès lors qu’une nouvelle autorité non nulle est fournie.
|
||||
|
||||
La campagne corrigée couvre donc toujours les neuf opérations, mais selon l’ordre suivant :
|
||||
|
||||
```text
|
||||
Buffer:
|
||||
Allocate -> Extend -> Write -> SetAuthority -> Trim -> Close
|
||||
|
||||
Metadata non canonique:
|
||||
Initialize -> SetData -> SetImmutable
|
||||
```
|
||||
|
||||
Le profil public Devnet de l’exemple est également abaissé à `3 r/s` pour les lectures et `1 r/s` pour les transactions. Les anciens fichiers de configuration locaux doivent être ajustés manuellement.
|
||||
@@ -0,0 +1,64 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_011_TOKEN_2022_METADATA_COMPLETENESS_AUDIT.md -->
|
||||
<!-- version: 8 -->
|
||||
|
||||
# Audit `0.4.8-pre.011` — complétude Token-2022 Token Metadata
|
||||
|
||||
## Portée
|
||||
|
||||
Cette prerelease ferme les écarts d’exécution synthétique des cinq instructions `spl-token-metadata-interface` implémentées par Token-2022. Elle ne crée aucun Program ID metadata autonome et ne lance aucune campagne Devnet, réservée à `pre.012`.
|
||||
|
||||
## Résultat fonctionnel
|
||||
|
||||
| Instruction | Wire | Intent | Builder | Retour/stateful | Matérialisation |
|
||||
|-------------------|-----:|-------:|--------:|------------------------:|---------------------------------------:|
|
||||
| `Initialize` | oui | oui | oui | snapshot TLV | instruction + snapshot |
|
||||
| `UpdateField` | oui | oui | oui | snapshot TLV | instruction + snapshot |
|
||||
| `RemoveKey` | oui | oui | oui | snapshot TLV | instruction + snapshot |
|
||||
| `UpdateAuthority` | oui | oui | oui | postcondition authority | instruction + snapshot |
|
||||
| `Emit` | oui | oui | oui | `returnData` borné | fait d’instruction, sans faux snapshot |
|
||||
|
||||
Les cinq opérations sont présentes dans les intents, les builders et les capacités de l’exécuteur. `UpdateAuthority` encode l’autorité nullable avec le type officiel. `Emit` accepte la forme complète sans plage ou une plage explicite d’au plus 1 024 octets, refuse `start > end`, les plages trop grandes et `start` sans `end`, puis conserve le `returnData` JSON-RPC sans dépendre des logs.
|
||||
|
||||
Un retour complet est décodé avec consommation exacte du format `TokenMetadata`. Un retour partiel conserve seulement les octets et ne prétend pas représenter un état complet. La postcondition d’autorité lit le snapshot TLV autoritatif et couvre une nouvelle autorité ou sa suppression.
|
||||
|
||||
## Ownership des matérialisateurs
|
||||
|
||||
- `MtMetadataToken2022Materializer` possède les neuf faits instructionnels Token Metadata et Token Group ;
|
||||
- les snapshots `TokenMetadata`, `TokenGroup` et `TokenGroupMember` restent distincts sous `metadata/token_2022/` ;
|
||||
- `InitializeMetadataPointer` et `UpdateMetadataPointer` restent possédés par le matérialiseur d’administration ;
|
||||
- aucun fait n’est dupliqué entre le matérialiseur metadata spécialisé et le matérialiseur d’administration.
|
||||
|
||||
## Correctifs de la livraison
|
||||
|
||||
- `pre.011-fix-001` active `serde-json-impl` pour le derive TS-RS de `serde_json::Value` et refuse la plage `Emit` ouverte avec `start` sans `end` ;
|
||||
- `pre.011-fix-002` remplace le variant inexistant `ExApiExecutionBlockhashKind::Recent` par le variant canonique `Latest` dans la fixture `kb-pipeline` ;
|
||||
- `pre.011-fix-003` est remplacé et ne doit pas être appliqué comme base finale ;
|
||||
- `pre.011-fix-004` rattache `update_token_metadata_authority` et `emit_token_metadata` à la famille `token_metadata` par un helper testé directement ;
|
||||
- `pre.011-fix-005` clôt les documents et ajoute les régressions directes couvrant `new_authority = None`, `Emit` sans plage et la plage maximale exactement égale à 1 024 octets.
|
||||
- `pre.011-fix-006` corrige uniquement les deux assertions de `operation_code` introduites par `fix-005` : les plans utilisent la hiérarchie canonique `spl.token_2022.*`, pas les noms courts des opérations.
|
||||
|
||||
## Validation locale observée le 6 août 2026
|
||||
|
||||
Avant `fix-005`, l’état fonctionnel final de `fix-004` a réussi :
|
||||
|
||||
```text
|
||||
cargo fmt --all réussi
|
||||
cargo clippy --all-targets réussi
|
||||
cargo check --workspace réussi
|
||||
python3 scripts/audit_rust_workspace_rules.py propre
|
||||
cargo test -p kb-lib 693 unitaires + 8 intégration, 0 échec
|
||||
cargo test -p kb-onchain-transport 118 tests, 0 échec
|
||||
cargo test -p kb-pipeline 105 unitaires + 1 API externe, 0 échec
|
||||
cargo test -p kb-pipeline-demo-scenarios 62 + 1 CLI + 1 API externe, 0 échec
|
||||
cargo test --workspace 1 285 tests, 0 échec, doc-tests propres
|
||||
```
|
||||
|
||||
Le `cargo clean` préalable a supprimé les artefacts obsolètes qui empêchaient Cargo de reconstruire le fichier `executor.rs` extrait depuis l’archive. La recompilation complète a ensuite exécuté le test direct `token_metadata_operations_share_matrix_family` et le test général de parité de matrice avec succès.
|
||||
|
||||
`fix-005` ne modifie aucun code de production et ajoute deux tests unitaires. La première reprise locale a confirmé que tout le workspace compilait, passait Clippy et les audits, mais ces deux nouveaux tests échouaient uniquement parce qu’ils attendaient les noms courts `update_token_metadata_authority` et `emit_token_metadata` au lieu des codes canoniques `spl.token_2022.update_token_metadata_authority` et `spl.token_2022.emit_token_metadata`. `fix-006` corrige ces deux assertions sans modifier le comportement de production.
|
||||
|
||||
La reprise finale de `fix-006` a ensuite réussi `cargo check --workspace`, `cargo clippy --all-targets`, `python3 scripts/audit_rust_workspace_rules.py` et `cargo test --workspace`. La suite unitaire `kb-lib` compte **695 tests réussis, 0 échec**. `pre.011` est donc clôturée sans réserve fonctionnelle.
|
||||
|
||||
## Frontières maintenues
|
||||
|
||||
Les fixtures, soumissions Devnet, confirmations et preuves réseau Token-2022 restent dans `pre.012`. Le réaudit Metaplex et ses compléments Devnet sont insérés en `pre.013`, ce qui décale la finalisation desktop metadata en `pre.014`. Aucune résolution off-chain n’est ajoutée par cette phase.
|
||||
@@ -0,0 +1,72 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_012_TOKEN_2022_METADATA_DEVNET_CAMPAIGN_AUDIT.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Audit `0.4.8-pre.012` — campagne Devnet Token-2022 Token Metadata
|
||||
|
||||
## Statut
|
||||
|
||||
**Terminée et validée sur Devnet.**
|
||||
|
||||
Le delta initial omettait la dépendance directe `bs58` dans `kb-pipeline-demo-scenarios/Cargo.toml`, alors que deux modules de la crate l'utilisent directement. `pre.012-delta-fix-001` déclare `bs58.workspace = true` et enregistre les preuves de la campagne réelle exécutée le 7 août 2026.
|
||||
|
||||
`pre.011` avait fermé la complétude fonctionnelle des cinq instructions de `spl-token-metadata-interface`. `pre.012` confirme désormais leur orchestration réseau de bout en bout sur Token-2022 Devnet.
|
||||
|
||||
## Contrat couvert
|
||||
|
||||
La campagne couvre exactement, dans cet ordre :
|
||||
|
||||
1. `Initialize` ;
|
||||
2. `UpdateField` ;
|
||||
3. `Emit` ;
|
||||
4. `RemoveKey` ;
|
||||
5. `UpdateAuthority`.
|
||||
|
||||
La fixture crée un mint Token-2022 frais, initialise `MetadataPointer` avant `InitializeMint2`, pointe le metadata pointer sur le mint lui-même et préfinance le compte pour les réallocations TLV atteintes par la campagne.
|
||||
|
||||
## Fixture Devnet observée
|
||||
|
||||
```text
|
||||
mint=EHrx1evxgXLEgjb1sgc5dVTktXX1LiovXR9YFsZS3d4k
|
||||
preparation_signature=2yYciRNoTTNSGuuaBFE6925FPjSj4fXp8usSkpLhgEPuhQMJYdsEzkAzxBdjDhpPHCT3db9bLz9Ud4BU5QSQZzQ5
|
||||
initial_authority=J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH
|
||||
final_authority=3RVXUmVvY7quUUEVsDKh5EcenVygwv3mo1iagPa4Pmq7
|
||||
```
|
||||
|
||||
La préparation stateful impose que `metadata_pointer` soit présent, auto-référent et détenu par l'autorité attendue, tandis que `token_metadata` doit être absent avant `Initialize`.
|
||||
|
||||
## Résultats réseau
|
||||
|
||||
| Étape | Signature | Slot | Postcondition | Matérialisations | `returnData` |
|
||||
|--------------------------------|--------------------------------------------------------------------------------------------|----------:|---------------|-----------------------------------------:|-------------:|
|
||||
| `InitializeTokenMetadata` | `48kzJR5VW1ZYSRkVrg6qyNmuHdAUu7NXcnzaNsSmH7SSmRXK1X6RQdCeeSQi6LhrrkgRFRGVnxkwXPtkiqG6EgnY` | 481804441 | `Confirmed` | 1 | 0 |
|
||||
| `UpdateTokenMetadataField` | `JrdB9EqePS1FVWLo55w1Vhv2eTCTfCpXZgMvPCocW3m3iu4nk3BFBBRHoe2Nbcpq1rmnZoFrhnag5fG2WwmJrYs` | 481804454 | `Confirmed` | 1 | 0 |
|
||||
| `EmitTokenMetadata` | `3TKdBCJhM1VEZ9ti6EYR9gSLJnH5ni6FjW4kvoqwdYK1u14jdY6hS8hf2VGeLi21PbX5CWiyzjthJZERK5KHroWH` | 481804464 | `Confirmed` | 1 observée, non requise par la politique | 202 |
|
||||
| `RemoveTokenMetadataKey` | `3kDBV7pDFgoCQVJ9DYYuxvHFbCJzyA6J2rLg6ndGCETEhhFBZ1ipRQ52X2hZfwNPfdJwEFFBmbyMU6kFGnVXJNrm` | 481804473 | `Confirmed` | 1 | 0 |
|
||||
| `UpdateTokenMetadataAuthority` | `5aBpQsm6mAHA5xSN5tZ1ubALbfghGa7JScjXn9NGhg9agzEdjXydH6jgmn6UmxinGHmMvtqaGRLk9dgjmmz4HpU2` | 481804482 | `Confirmed` | 1 | 0 |
|
||||
|
||||
Le runner n'accepte une étape qu'après simulation réussie, confirmation, hydratation canonique, extraction Core, replay Token-2022, seconde passe d'idempotence propre et postcondition stateful confirmée. Les mutations exigent en plus une matérialisation instructionnelle. `Emit` n'en exige pas ; sa preuve spécialisée est le `returnData`, décodé ici sur 202 octets et comparé au snapshot TLV autoritatif.
|
||||
|
||||
## Matrice
|
||||
|
||||
`test-fixtures/contract-matrices/SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json` conserve exactement les cinq scénarios de la campagne. `pre.012-delta-fix-001` les promeut tous à `confirmed` et enregistre pour chacun toutes les catégories de preuve requises.
|
||||
|
||||
Le test de matrice est mis à jour pour exiger cet état confirmé et vérifier que chaque catégorie requise possède une preuve observée.
|
||||
|
||||
## Validation locale
|
||||
|
||||
Les contrôles opérateur réussissent :
|
||||
|
||||
```text
|
||||
cargo fmt --all réussi
|
||||
cargo check --workspace réussi
|
||||
cargo clippy --all-targets réussi
|
||||
python3 scripts/audit_rust_workspace_rules.py clean
|
||||
cargo test -p kb-pipeline-demo-scenarios 69 + 1 + 1 tests réussis
|
||||
cargo test --workspace réussi
|
||||
```
|
||||
|
||||
Le workspace complet de `pre.012` termine ses 34 suites de tests avec 1 294 tests réussis au total et aucun échec.
|
||||
|
||||
## Conclusion
|
||||
|
||||
`0.4.8-pre.012` est clôturée : les cinq instructions Token-2022 Token Metadata prévues sont confirmées sur Devnet avec preuves de simulation, transaction, replay et postcondition. La phase suivante est `0.4.8-pre.013`, consacrée au réaudit et à la complétude Metaplex Token Metadata et de ses campagnes Devnet.
|
||||
@@ -0,0 +1,333 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_013_METAPLEX_COMPLETENESS_AND_DEVNET_AUDIT.md -->
|
||||
<!-- version: 30 -->
|
||||
|
||||
# Audit `0.4.8-pre.013` — Metaplex Token Metadata et campagnes Devnet
|
||||
|
||||
## Objet
|
||||
|
||||
Ce réaudit vérifie si la surface Metaplex Token Metadata doit encore être complétée avant de reprendre les campagnes Devnet. Il sépare strictement la complétude du code, la disponibilité d’une fixture et la preuve réseau observée.
|
||||
|
||||
## Sources contrôlées
|
||||
|
||||
- IDL historique conservé `metadata.metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s.metaplex_token_metadata.V1_14_0.from_github_mpl-token-metadata.json` ;
|
||||
- `METAPLEX_TOKEN_METADATA_MATRIX.json` ;
|
||||
- intents, builders, exécuteur, préflight et matérialiseurs Metaplex de `kb-lib` et `kb-pipeline` ;
|
||||
- inventaires synthétiques et Devnet de `kb-pipeline-demo-scenarios` ;
|
||||
- surface Rust actuelle `mpl-token-metadata ^5.1`, résolue à `5.1.1` dans la base validée.
|
||||
|
||||
## Fermeture de la surface
|
||||
|
||||
L’IDL contient exactement 58 instructions et la matrice d’exécution contient exactement 58 lignes. Leur partition est :
|
||||
|
||||
- 20 `executable_current` ;
|
||||
- 15 `executable_deprecated` ;
|
||||
- 22 `decode_only_replaced` ;
|
||||
- 1 `decode_only_bridge_boundary`, `BubblegumSetCollectionSize`, hors frontière du programme Metaplex Token Metadata.
|
||||
|
||||
Les 20 opérations courantes sont : `CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow`, `Burn`, `Create`, `Mint`, `Delegate`, `Revoke`, `Lock`, `Unlock`, `Migrate`, `Transfer`, `Update`, `Use`, `Verify`, `Unverify`, `Collect`, `Print`, `Resize` et `CloseAccounts`.
|
||||
|
||||
Le réaudit ne met en évidence aucune instruction courante du SDK actuel absente des intents ou de l’exécuteur. Une seconde passe d’exactitude des builders a toutefois révélé que `Print` et `Resize` utilisaient encore les account metas figées de l’IDL historique : `Print` ne déclarait pas `edition_mint` ni `master_token_account_owner` signataires et `Resize` ne déclarait pas `payer` signataire. `pre.013-delta-fix-003` migre ces deux opérations vers les builders officiels `mpl-token-metadata 5.1.1` et verrouille leurs flags de comptes par tests directs. Les formes remplacées restent volontairement non exécutables et les formes obsolètes restent séparées, exécutables uniquement sous approbation explicite. Aucun élargissement de la frontière programme n’est requis.
|
||||
|
||||
## Écart initial de validation réseau
|
||||
|
||||
Au début de `0.4.8-pre.013`, la matrice Devnet contenait les 20 opérations courantes mais aucune preuve réseau durable n'était conservée dans la base reprise : les 20 statuts ont donc été rouverts à `not_run`. Cette décision constitue le point de départ du réaudit, pas son état final ; la consolidation en fin de document ferme la matrice à 15 `confirmed`, 5 `unavailable` et 0 `not_run`.
|
||||
|
||||
À ce même point de départ, les cinq familles de parcours existantes — NFT, SFT, token fongible, collection et pNFT — décrivaient une cible cohérente, mais leur préparation Devnet réellement automatisée restait limitée à `Create` et `UpdateAsUpdateAuthorityV2`. Les opérations spécialisées exigeaient encore des graphes de comptes dédiés, notamment :
|
||||
|
||||
- édition maître, édition imprimée, `Print`, `Mint` et `Burn` ;
|
||||
- collection parent/membre pour `Verify` et `Unverify` ;
|
||||
- pNFT avec token records, delegates et rule set lorsque requis ;
|
||||
- uses, escrow et opérations de maintenance selon leurs préconditions stateful.
|
||||
|
||||
Aucune disponibilité de builder ni preuve synthétique n’est promue en preuve Devnet.
|
||||
|
||||
## Écart du runner corrigé
|
||||
|
||||
Le runner Metaplex construisait déjà une policy exigeant, après soumission, l’insertion canonique, l’extraction Core, le replay et éventuellement la matérialisation. Pourtant, son exécution s’arrêtait après confirmation, lectures stateful et snapshots de comptes.
|
||||
|
||||
`pre.013` aligne le comportement sur le contrat déclaré :
|
||||
|
||||
1. confirmation de la signature ;
|
||||
2. lectures stateful de postcondition ;
|
||||
3. hydratation canonique bornée de la transaction ;
|
||||
4. extraction Core de la signature ;
|
||||
5. replay du décodeur Metaplex avec les matérialiseurs du domaine ;
|
||||
6. lecture des matérialisations instructionnelles ;
|
||||
7. seconde passe de replay avec état matérialisé, qui doit produire un skip idempotent propre ;
|
||||
8. diagnostic post-exécution agrégé.
|
||||
|
||||
Le nombre de retries `getTransaction` est borné à 20. Le desktop est seulement adapté à ce contrat commun ; l’ajout des nouveaux parcours UI reste réservé à `pre.014`.
|
||||
|
||||
## État de la prerelease
|
||||
|
||||
Le réaudit de complétude est terminé et le défaut de post-validation du runner est corrigé dans le delta initial. La prerelease reste **en cours** tant que les fixtures spécialisées ne sont pas complétées et que les 20 opérations courantes ne sont pas qualifiées par des preuves Devnet réelles ou une indisponibilité explicitement démontrée.
|
||||
|
||||
## Correctif `pre.013-delta-fix-001`
|
||||
|
||||
La première validation locale du delta initial a mis en évidence deux erreurs de compilation, sans remettre en cause le réaudit ni la frontière fonctionnelle :
|
||||
|
||||
- le test Devnet opt-in appelait `PostgresStoreOptions::new` avec l'ancienne signature à trois paramètres et fournissait une `Duration` alors que le contrat courant attend `connect_timeout_ms: u64` puis `auto_initialize_schema: bool` ;
|
||||
- le desktop injectait directement `BackfillSummary`, `CoreExtractionSummary` et `DecodeReplaySummary` dans `serde_json::json!`, alors que ces summaries internes ne dérivent volontairement pas `Serialize`.
|
||||
|
||||
`pre.013-delta-fix-001` corrige le premier point avec `5_000` ms et `false`, puis projette explicitement les trois summaries en `serde_json::Value` dans l'adaptateur desktop. Aucun derive public supplémentaire n'est ajouté à `kb-pipeline` et aucun contrat de production n'est élargi.
|
||||
|
||||
La validation locale de `pre.013-delta-fix-001` est ensuite réussie : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l'audit workspace sont propres ; `kb-pipeline-demo-scenarios` réussit 69 tests unitaires, 1 test CLI et 1 test d'API externe ; `kb-app-demo-desktop` réussit 135 tests unitaires.
|
||||
|
||||
## Correctif `pre.013-delta-fix-002` — cohérence des cinq fixtures Create
|
||||
|
||||
Le réaudit des cinq parcours Devnet a révélé un défaut latent indépendant du runner : le desktop adaptait le JSON `Create` à NFT, SFT, fungible, collection ou pNFT, tandis que `prepare_metaplex_create_fixture` préparait toujours un mint classique à zéro décimale. Cette séparation pouvait produire un parcours `Fungible` dont le `TokenStandard` annoncé ne correspondait pas aux décimales du mint réellement créé.
|
||||
|
||||
Le correctif déplace la spécialisation dans la fixture réutilisable :
|
||||
|
||||
- NFT, SFT, collection et pNFT préparent un mint classique à 0 décimale ;
|
||||
- fungible prépare un mint classique à 9 décimales ;
|
||||
- seules NFT, collection et pNFT demandent une master edition ;
|
||||
- SFT et fungible conservent `master_edition = null` et aucun `print_supply` ;
|
||||
- collection déclare `CollectionDetails::V1 { size: 0 }` ;
|
||||
- pNFT déclare `ProgrammableNonFungible` sans rule set pour le parcours de base ;
|
||||
- les cinq JSON produits sont vérifiés hors réseau par désérialisation vers `ExMetaplexTokenMetadataOperation::Create`.
|
||||
|
||||
Le desktop ne réécrit plus le contrat après préparation : le standalone Create reste NFT par défaut et chaque scénario transmet explicitement sa famille à la fixture commune.
|
||||
|
||||
La prerelease reste **en cours** : ce correctif ferme uniquement la cohérence des fixtures `Create`. Les token accounts/supply, éditions imprimées, collection parent-membre, token records pNFT, uses, escrow et la qualification réseau des 20 opérations restent à traiter.
|
||||
|
||||
## Correctif `pre.013-delta-fix-003` — exactitude `Print` et `Resize`
|
||||
|
||||
Le contrôle des graphes nécessaires aux éditions imprimées a mis en évidence un écart de builder avant même l’exécution Devnet. Le SDK `mpl-token-metadata 5.1.1` déclare pour `Print` `edition_mint`, `edition_mint_authority`, `payer` et `master_token_account_owner` comme signataires ; l’ancien builder issu de l’IDL ne déclarait que `edition_mint_authority` et `payer`. Pour `Resize`, le SDK courant déclare `payer` writable + signer, alors que l’ancien builder le rendait writable non signer.
|
||||
|
||||
Le correctif remplace uniquement ces deux constructions par `PrintBuilder` et `ResizeBuilder` du SDK officiel. Les types publics restent inchangés dans ce lot : les comptes actuellement obligatoires dans l’intent sont transmis aux comptes optionnels du SDK sous forme `Some(...)`. L’audit des optionalités courantes et des autres opérations encore construites depuis l’IDL brut reste explicitement ouvert avant leur qualification Devnet.
|
||||
|
||||
Deux tests de régression vérifient directement le discriminator, le nombre de comptes et les flags signer/writable critiques. Aucune ligne de la matrice Devnet n’est promue par ce correctif.
|
||||
|
||||
## Correctif `pre.013-delta-fix-004` — optionalités positionnelles courantes
|
||||
|
||||
La validation locale de `pre.013-delta-fix-003` a d’abord révélé deux violations `clippy::implicit-return` dans les deux closures ajoutées par ses tests. L’opérateur a ajouté les deux `return`, puis `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace ont réussi ; les deux régressions `Print`/`Resize` passent et la suite complète `kb-lib` atteint 697 tests unitaires réussis sans échec. `fix-004` incorpore ces deux corrections afin que l’archive reproduise exactement cet état validé.
|
||||
|
||||
Le réaudit d’optionalité montre ensuite que le décodeur et la matrice conservaient déjà le contrat Kinobi courant, mais que plusieurs intents/exécuteurs imposaient encore des comptes que le SDK accepte comme positions optionnelles. Le correctif aligne donc :
|
||||
|
||||
- `CreateEscrowAccount.authority` et `TransferOutOfEscrow.authority` sur l’autorité optionnelle de fin de liste ;
|
||||
- `Mint.token_owner`, `master_edition`, `token_record`, `delegate_record`, `authorization_rules_program` et `authorization_rules` sur les six positions optionnelles du wrapper courant ;
|
||||
- `Migrate.authorization_rules_program` et `authorization_rules` sur les deux positions optionnelles finales ;
|
||||
- `Use.delegate_record`, `token`, `edition`, `spl_token_program`, `authorization_rules_program` et `authorization_rules` sur les positions optionnelles courantes ;
|
||||
- `Print.edition_token_record`, `Resize.authority` et `Resize.token` sur les comptes optionnels exposés par les builders officiels.
|
||||
|
||||
Les positions optionnelles absentes sont encodées avec le Program ID Metaplex readonly et non signer, ce qui préserve le nombre et l’ordre des comptes positionnels attendus par le programme et par le décodeur. Les comptes système, sysvar et programmes dont le builder propose seulement une valeur par défaut restent explicitement fournis lorsque la struct d’instruction courante les porte comme comptes requis : une commodité de builder n’est pas reclassée comme optionalité positionnelle.
|
||||
|
||||
Deux régressions supplémentaires couvrent les placeholders de `CreateEscrowAccount`, `Mint`, `Migrate` et `Use`, puis l’absence de `edition_token_record` pour `Print` et de `authority`/`token` pour `Resize`. Aucune ligne Devnet n’est promue par ce correctif. La validation locale de `fix-004` réussit ensuite avec `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets`, l’audit workspace, 699 tests unitaires `kb-lib`, 70 tests unitaires `kb-pipeline-demo-scenarios` et 135 tests desktop.
|
||||
|
||||
## Correctif `pre.013-delta-fix-005` — socle token accounts/supply
|
||||
|
||||
La préparation des campagnes `Mint` exigeait encore un compte token classique réellement utilisable. Le correctif étend donc la fixture `Create` sans préconsommer la preuve Metaplex :
|
||||
|
||||
- la transaction native de préparation crée le mint SPL classique, exécute `InitializeMint2`, puis crée l’ATA canonique de l’opérateur ;
|
||||
- la dépense déclarée du plan inclut les minima de rent du mint et du compte token ;
|
||||
- après confirmation, le mint doit rester initialisé avec supply nulle et l’ATA doit être un compte classique de 165 octets, lié au mint et à l’opérateur, avec amount nul et état initialisé ;
|
||||
- le résumé expose l’ATA et produit un intent Metaplex `Mint` typé par famille ;
|
||||
- NFT, collection et pNFT réservent un montant brut de 1, SFT un montant de 10 et le fungible à 9 décimales un montant de 1_000_000_000 ;
|
||||
- pour pNFT, le token-record PDA canonique est dérivé et transmis à `Mint`, mais n’est pas créé par la fixture native.
|
||||
|
||||
Cette séparation est intentionnelle : la fixture prépare les préconditions mais laisse `Mint` produire la supply et, pour pNFT, l’état programmable que la campagne Devnet devra observer. Les 20 lignes réseau restent `not_run` jusqu’à réception de preuves réelles.
|
||||
|
||||
## Correctif `pre.013-delta-fix-006` — première campagne réseau `Create -> Mint`
|
||||
|
||||
La validation locale de `fix-005` est acquise : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace sont propres ; `kb-pipeline-demo-scenarios` réussit 72 tests unitaires, 1 test CLI et 1 test d’API externe, et `kb-app-demo-desktop` réussit 135 tests unitaires.
|
||||
|
||||
Le correctif ajoute une campagne Devnet volontairement bornée à une seule famille par invocation. Les valeurs acceptées sont NFT, SFT, fungible, collection et pNFT. La campagne :
|
||||
|
||||
1. prépare un mint SPL classique frais et l’ATA canonique opérateur avec supply/amount nuls ;
|
||||
2. exécute `Create` avec matérialisation et snapshots metadata/master edition selon la famille ;
|
||||
3. relit le mint et l’ATA au minimum au slot confirmé de `Create` et exige toujours supply=0/amount=0 ;
|
||||
4. exécute `Mint` avec les snapshots `Create` comme préflight confirmé ;
|
||||
5. relit les comptes Metaplex au slot confirmé de `Mint`, exige le token record pour pNFT et vérifie la transition SPL exacte vers `mint_amount_raw` ;
|
||||
6. exige pour les deux transactions hydratation canonique, extraction Core, premier replay, matérialisation instructionnelle et second replay idempotent.
|
||||
|
||||
Le runner générique est durci en parallèle : les requêtes de postcondition sont clonées après confirmation et leur `min_context_slot` devient le maximum entre la borne demandée et le slot confirmé. Une lecture `after_state` ne peut donc plus être satisfaite par un contexte antérieur à la transaction.
|
||||
|
||||
Aucune ligne de `METAPLEX_TOKEN_METADATA_DEVNET_EXECUTION_MATRIX.json` n’est promue par ce correctif. `Create` et `Mint` restent `not_run` jusqu’à réception de signatures et slots Devnet réels.
|
||||
|
||||
## Correctif `pre.013-delta-fix-007` — structure de crate et diagnostic de la première campagne NFT
|
||||
|
||||
La première tentative réelle `Create -> Mint` sur la famille NFT a été lancée après validation locale de `fix-006`. La suite hors réseau est propre avec 77 tests unitaires de `kb-pipeline-demo-scenarios` et 135 tests desktop. La campagne a ensuite atteint la validation post-exécution de `Create`, mais s'est arrêtée sur `metaplex_create_mint_post_execution_incomplete`. Le message de `fix-006` ne permettait pas de distinguer hydratation, extraction Core, replay, matérialisation instructionnelle ou snapshots manquants, et aucune signature n'a été imprimée avant le `panic`. Cette tentative ne constitue donc pas une preuve réseau durable et ne modifie aucun statut de matrice.
|
||||
|
||||
Le réaudit du chemin de matérialisation identifie en parallèle une sélection de preuve trop indirecte : les lignes matérialisées étaient retenues à partir du `source_decoder_name`, alors que la preuve recherchée appartient explicitement au matérialiseur Metaplex. `fix-007` sélectionne désormais les lignes par `processor_name`, dérivé directement de l'identité publique de `MtMetadataMetaplexTokenMetadataMaterializer`. Si aucune ligne n'est trouvée, le diagnostic post-exécution conserve le nom exact du matérialiseur et la signature concernée. La validation de campagne expose en outre les quatre drapeaux post-exécution, le nombre de lignes matérialisées, le nombre de snapshots et les diagnostics accumulés. Une nouvelle tentative pourra donc confirmer la correction ou localiser exactement l'étape encore défaillante sans interprétation implicite.
|
||||
|
||||
La même correction réorganise `kb-pipeline-demo-scenarios` avant l'ajout de nouvelles familles de fixtures. Les fichiers sont désormais groupés par domaine : `metadata/solana_program`, `metadata/metaplex_token_metadata`, `spl/associated_token_account`, `spl/memo`, `spl/token`, `spl/token_2022` et `spl/token_2022/metadata`. `solana.rs` conserve les primitives d'orchestration communes. Les exports publics restent au crate-root ; le refactor ne change donc pas le contrat externe de la crate.
|
||||
|
||||
## Correctif `pre.013-delta-fix-008` — borne RPC stateful Metaplex
|
||||
|
||||
La validation locale de `fix-007` réussit avec 78 tests unitaires de `kb-pipeline-demo-scenarios`, 135 tests desktop, `cargo check --workspace`, Clippy et l'audit workspace propres. La seconde tentative NFT localise ensuite l'échec avant hydratation : une lecture de postcondition transmet `MAX_METAPLEX_TOKEN_METADATA_ACCOUNT_BYTES = 1_048_576` à `GetAccountInfoConfig::new_with_data`, alors que le transport d'exécution borne une lecture complète à `MAX_COMPLETE_ACCOUNT_DATA_BYTES = 65536`.
|
||||
|
||||
`fix-008` aligne la constante stateful Metaplex sur cette borne de transport, valide la requête avant l'appel RPC et ajoute une régression analogue à celle déjà utilisée pour Solana Program Metadata. Les capacités propres aux décodeurs restent indépendantes pour des données déjà disponibles offline ou un futur lecteur par tranches. Le diagnostic de campagne inclut désormais aussi signature et slot lorsqu'une transaction confirmée échoue plus tard dans la post-validation.
|
||||
|
||||
Aucune ligne Devnet n'est promue par cette correction : la campagne doit être rejouée après validation locale.
|
||||
|
||||
## Correctif `pre.013-delta-fix-009` — compatibilité replay `Create` / `Mint`
|
||||
|
||||
La validation locale de `fix-008` est propre : `cargo check --workspace`, Clippy, 106 tests unitaires `kb-pipeline`, 78 tests unitaires `kb-pipeline-demo-scenarios` et 135 tests desktop réussissent. La troisième tentative NFT confirme ensuite réellement `Create` sur Devnet avec la signature `51MXdFFFNY7UTS23cmfCQBfrDKC99uk5Trtw2gkptvEug9aWJ2R1cE9Ta5QHbe4kS1NYgRUSSMmmdbqdhyxxnR4Y` au slot `481882374`. L’hydratation canonique et l’extraction Core réussissent, et deux snapshots stateful sont matérialisés. Le blocage restant est exclusivement instructionnel : `decode_replayed=false`, aucune ligne du matérialiseur Metaplex n’est produite et la seconde passe ne peut pas démontrer l’idempotence.
|
||||
|
||||
Le réaudit local identifie une incompatibilité entre l’exécuteur et son propre décodeur. `Create` permet officiellement de choisir si le mint et l’update authority signent, mais le décodeur imposait encore les deux signatures. La fixture prépare le mint dans une transaction antérieure et utilise donc légitimement `mint_as_signer=false`. En outre, les flags signer/writable de `MdCoreInstructionReplayInput` décrivent les privilèges globaux du message : lorsque authority, payer et update authority désignent le même wallet, un rôle readonly peut apparaître writable sans rendre l’instruction invalide. `Mint` présente la même propriété avec token owner, authority et payer.
|
||||
|
||||
`fix-009` valide donc pour `Create` et `Mint` uniquement les privilèges minimaux réellement requis, accepte les signataires dynamiques et les sur-ensembles transactionnels, tout en conservant writable obligatoire pour master edition/token record lorsqu’ils ne sont pas des placeholders Metaplex. Deux régressions verrouillent ces formes. Le runner ajoute aussi un diagnostic des compteurs par processor lorsque le premier replay n’est pas complet. Aucune ligne de matrice n’est promue avant un replay NFT complet avec matérialisation et idempotence.
|
||||
|
||||
## Correctif `pre.013-delta-fix-010` — fixture `Create -> Mint` fresh-only
|
||||
|
||||
La tentative suivant `fix-009` expose un mismatch entre l’autorité opérateur `J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH` et des mint/freeze authorities observées à `3RJXuBF6xB3j47EeAHEBbcwvV66rJrkhmJvYNr36jz8x`. L’interprétation initiale attribuait ce mismatch à une fixture persistante. `fix-010` durcit donc légitimement la préparation : alias à granularité nanoseconde, `TemporaryWalletStore::create` sans fallback de chargement, absence on-chain obligatoire avant préparation, puis relecture du mint et de l’ATA avec `minContextSlot` égal au slot confirmé de la transaction native.
|
||||
|
||||
Le réaudit complet effectué après la tentative suivante corrige toutefois l’interprétation causale : la validation qui produit ce message est exécutée après `validate_confirmed_execution("create", ...)`, donc après un `Create` confirmé et son pipeline post-exécution. Pour une famille NFT avec Master Edition, Token Metadata transfère normalement les mint/freeze authorities au PDA Master Edition. `fix-010` reste donc un durcissement fresh-only utile, mais il ne pouvait pas résoudre ce mismatch attendu.
|
||||
|
||||
## Correctif `pre.013-delta-fix-011` — postcondition d’autorité après `Create`
|
||||
|
||||
La tentative suivant `fix-010` reproduit le mismatch avec une nouvelle valeur fraîche `KsjdBdmUUM3r8Mw6cQESkXJfxmnPXSmR482963gmsyo`. La préparation fresh-only ayant été validée immédiatement avant `Create`, cette variation confirme qu’il ne s’agit pas d’un vieux mint réutilisé. Surtout, le chemin de code n’atteint cette vérification qu’après simulation, confirmation, hydratation canonique, extraction Core, replay, matérialisation, snapshots et seconde passe idempotente réussis pour `Create`. Signature et slot ne sont cependant pas imprimés à ce stade, donc cette tentative ne peut pas encore promouvoir la matrice.
|
||||
|
||||
`fix-011` applique la postcondition officielle : NFT, collection et pNFT attendent après `Create` le PDA Master Edition comme mint authority et freeze authority ; SFT et fungible continuent d’attendre l’autorité opérateur. La même attente est conservée après `Mint`. Les erreurs SPL postérieures à une exécution confirmée incluent désormais signature + slot, et le helper `read_token_account` devenu inutilisé est supprimé afin de retrouver un build sans warning.
|
||||
|
||||
## Correctif `pre.013-delta-fix-012` — première qualification opérationnelle confirmée
|
||||
|
||||
Après `fix-011`, la campagne NFT `Create -> Mint` termine enfin sans erreur. Le mint frais `J6pDBZR4nWpM8Mj61fEdwQXH3KLZE8RuKSEsyNofC7bp` utilise la metadata PDA `7JLnovNwuatxAessRcWCEuKb5jg2H93KVpmyHNzx4euG`, la Master Edition `GxUFL8ZfosThA4hUVGPKDT8Zvcc2b2VmdjEiw6WQQYNx` et l’ATA opérateur `9LBJCDFkJghjVDDeccTe4vjZH3dDNpzsWVXqyTKs81To`. La préparation native est confirmée par `2u4sdcFhmoeeYiAe8X761AmCnWyPLHSxYwzwwocLYkfVCan9W5i13ipvK4v3dWRe9E4iJRqBCHHFRh8MwR2o8FLh`.
|
||||
|
||||
`Create` est confirmé avec la signature `okwkuQFp7yVguYW7GEY9Chv6ochM8TWBEeCGV1gu5Waku9wQvMfrKjkig6JxXBwpKoFTECignzJ8HFf8dLtpsvS` au slot `481904731` et produit une matérialisation instructionnelle. `Mint` est confirmé avec la signature `3hSizfdvezyFpfhqX4Za4wmhdMHhSPAisFvYjFAxpRzYcUXhryxCzc9qpwod3Yjm6GmijXTGjjDUuVksfpBzLwz1` au slot `481904744`, produit une matérialisation instructionnelle et fait passer la supply du mint ainsi que l’amount de l’ATA de `0` à `1`. Le test retourne `ok` uniquement après les gardes de simulation, hydratation canonique, extraction Core, replay, matérialisation, snapshots stateful et seconde passe idempotente.
|
||||
|
||||
La matrice Devnet opérationnelle passe en version 2. Elle conserve désormais `observedFamilies` et une liste de preuves observées ; une ligne `confirmed` est refusée si elle ne couvre pas l’union de `requiredEvidence` et `submissionEvidence`. `Create` et `Mint` deviennent `confirmed` sur `nft`, tandis que les 18 autres opérations restent `not_run`. La largeur multi-famille n’est pas confondue avec cette qualification opérationnelle : SFT, fungible, collection et pNFT restent à exécuter pour confirmer que les variantes de comptes et d’arguments empruntent le même pipeline complet.
|
||||
|
||||
Pour éviter une nouvelle perte de télémétrie, le résumé d’exécution conserve aussi `simulation_context_slot` et le test opt-in imprime une ligne `METAPLEX_CREATE_MINT_EVIDENCE` machine-readable. Les futures campagnes peuvent ainsi fournir le genesis hash, le slot de simulation, le message hash, le fee, la confirmation et les compteurs de pipeline exacts sans nouvelle instrumentation.
|
||||
|
||||
## Correctif `pre.013-delta-fix-013` — qualification SFT et preuves par famille
|
||||
|
||||
La campagne SFT suivante termine également de bout en bout. `Create` est simulé au slot `481908023`, confirmé avec `2s81m6k9HYDLmCy7oXKaqBvtb62iyX4bJmij2f2VjHuoRQBBA6YqRuAsjyaM7JCaJXasbU3WCfiko91cZcDLLdxV` au slot `481908028`, puis matérialise une ligne. `Mint` est simulé au slot `481908034`, confirmé avec `4pAemhEJewuLAeqLkqwzPvVmsCRbkyLiN6uAUWBA5RMsofaEDt5Xr8YGiBEvLWQM7Qvxnd81mmCHpandmfmWuyAm` au slot `481908039`, matérialise une ligne et fait passer supply/amount de `0` à `10`. Les deux étapes conservent genesis hash, message hash, fee, hydratation canonique, extraction Core, replay et idempotence propres.
|
||||
|
||||
La seconde famille observée expose une faiblesse du schéma v2 : une seule liste `evidence` ne peut pas représenter proprement plusieurs signatures et slots tout en conservant des kinds uniques. La matrice passe donc en version 3 avec un bundle `familyEvidence` par famille observée. Le validateur exige une correspondance exacte entre `observedFamilies` et ces bundles, puis contrôle chaque bundle contre les six preuves de simulation et les dix preuves de soumission. `Create` et `Mint` restent les deux seules opérations `confirmed`, désormais sur NFT et SFT ; les 18 autres opérations restent `not_run`.
|
||||
|
||||
## Consolidation `pre.013-delta-fix-014` — troisième famille `Create -> Mint`
|
||||
|
||||
La campagne fungible confirme que le même pipeline fonctionne avec un mint SPL classique à 9 décimales et sans Master Edition. `Create` est confirmé avec `3J1Ebq9DVkupVFQmqQ84QaNeztwMuRwbtXj7CiyUWwZPj4KCAJpKLAbPvpYss5cdiY33foXXScWWZE98H7tvbZed` au slot `481910724`; `Mint` est confirmé avec `2GKiY9foLmioYtUHttA2D6nCSmsftRL1JDKxENHmCnb5w38FGSxwBiRNMfF7mH8htv3G45irmge6DVtr6VN359j2` au slot `481910734`. Les deux étapes terminent simulation exacte, confirmation, hydratation canonique, extraction Core, replay, matérialisation et idempotence. `Mint` fait passer supply et amount ATA de `0` à `1_000_000_000`.
|
||||
|
||||
La matrice v3 ajoute donc un troisième bundle `familyEvidence = fungible` à `Create` et `Mint`. Leur statut opérationnel reste `confirmed`; les 18 autres opérations courantes restent `not_run`. La couverture multi-famille de cette campagne n’est plus ouverte que pour collection et pNFT.
|
||||
|
||||
### Consolidation `pre.013-delta-fix-015`
|
||||
|
||||
La campagne collection `Create -> Mint` est confirmée avec `Create` signature `3jpxyWuxQVyBtbvqDnV1oahLyxCXMw1zQQZBGCKgQSfn2TNcBaUdfvJ6kFa8zL3XJLMpM6tahQTnu8AUsTkYc3Dy` slot `481911942`, puis `Mint` signature `4WEciqZA5vmtrh1ZeSbqcgVnezUy7qT6B6utySfkg7dbAmhH6SMwwY862zGbzPW3nBVFzMjFVGPEhnTt6TcAiEe2` slot `481911955`. Les deux étapes terminent le pipeline de preuve complet ; `Create` et `Mint` possèdent désormais quatre bundles indépendants `nft`, `sft`, `fungible` et `collection`. Le test de matrice est corrigé pour exiger ces quatre familles déjà qualifiées tout en autorisant l'ajout ultérieur de `programmable_nft` lorsqu'un bundle complet existe. pNFT reste la dernière variante `Create -> Mint` à exécuter.
|
||||
|
||||
## Correctif `pre.013-delta-fix-016` — état SPL pNFT après `Mint`
|
||||
|
||||
La première campagne `programmable_nft` franchit `Create`, puis confirme `Mint` avec la signature `Gkg5A6ehuSzkU1N318kYoRHm5RdxV2yBRpaK9Gwyt6vN1ToYwAS6Vzpk2rQYWS9NJz1rnUpypLCn7DqWFzQzxFc` au slot `481913765`. La relecture de l’ATA confirme le mint attendu `Ecn3v1u21DKNhpFX1tqKvieVn5CkhMMukxWgoN59Hf2E`, l’owner opérateur `J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH` et `amount=1`, mais observe `state=2`. Le runner exigeait encore `state=1` pour toutes les familles et échoue donc après une transaction `Mint` déjà confirmée.
|
||||
|
||||
Le contrat SPL encode `Initialized=1` et `Frozen=2`. Pour un programmable NFT, Token Metadata conserve l’ATA SPL gelé tandis que le Token Record porte l’état programmable et les délégations. `fix-016` rend cette distinction explicite : pNFT exige `Frozen` après `Mint`, les quatre autres familles exigent `Initialized`, et le Token Record reste une postcondition Metaplex séparée. Le résumé de campagne conserve aussi les états SPL bruts avant/après afin de produire la preuve `1 -> 2`.
|
||||
|
||||
Aucun bundle `programmable_nft` n’est ajouté à la matrice sur la base de cette tentative interrompue : la campagne doit être rejouée et retourner `ok` avec sa ligne `METAPLEX_CREATE_MINT_EVIDENCE` complète avant promotion.
|
||||
|
||||
## `pre.013-delta-fix-017` — fermeture `Create -> Mint` et ouverture collection parent-membre
|
||||
|
||||
Le rerun pNFT après `fix-016` ferme la qualification des cinq familles `Create -> Mint`. `Create` est confirmé au slot `481921980` et `Mint` au slot `481921994`; le Token Record est créé, supply/amount passent `0 -> 1` et l’ATA passe `Initialized(1) -> Frozen(2)`. La matrice v3 peut donc conserver cinq bundles familiaux indépendants pour `Create` et `Mint`. Les 18 autres opérations restent `not_run`.
|
||||
|
||||
Le lot suivant est borné à la collection courante :
|
||||
|
||||
- parent Collection NFT avec `CollectionDetails::V1 { size: 0 }` ;
|
||||
- membre NFT créé avec `collection.key=<parent mint>` et `verified=false` ;
|
||||
- `Verify` courant avec `VerificationArgs::CollectionV1` et comptes parent mint/metadata/master-edition ;
|
||||
- `Unverify` courant avec `VerificationArgs::CollectionV1` ;
|
||||
- postconditions member `verified false -> true -> false` ;
|
||||
- postconditions parent `size 0 -> 1 -> 0` ;
|
||||
- aucune utilisation de `SetCollectionSize` pour fabriquer l’état attendu.
|
||||
|
||||
Le runner réutilise le chemin confirmé `Create -> Mint`, puis exige pour `Verify` et `Unverify` le même contrat de preuve complet : simulation, confirmation, hydratation canonique, Core extraction, replay, matérialisation, snapshots et seconde passe idempotente. Aucun statut réseau de `Verify`/`Unverify` n’est promu avant exécution réelle.
|
||||
|
||||
## `pre.013-delta-fix-018` — compatibilité replay `Verify` / `Unverify`
|
||||
|
||||
La première campagne collection spécialisée confirme `Verify(CollectionV1)` sur Devnet avec la signature `2kR1VeWdCW3FcKRZdNuKMMc3kGUmWRBXxXX8W1NNi2rhWfZYTg9zsDvJHx7WvebgUfc2K382J14i66E2qPEnjsqR` au slot `481931061`. L’hydratation canonique, l’extraction Core et trois snapshots stateful réussissent, mais le décodeur retourne `failed=1`, `decoded=0`.
|
||||
|
||||
Le défaut est le même que pour `Create`/`Mint` : les flags Core sont les privilèges globaux du message. L’autorité est aussi fee payer et devient donc writable au niveau transactionnel, même si `Verify` ne requiert localement que sa signature. `fix-018` remplace les égalités négatives par des minima de privilèges pour `Verify` et `Unverify`, conserve l’exigence writable de la metadata cible et de la collection metadata réellement fournie, et aligne `Unverify` sur ses 7 positions courantes. La matrice conserve `Verify` et `Unverify` à `not_run` jusqu’au rerun complet.
|
||||
|
||||
## `pre.013-delta-fix-019` — qualification collection et préparation `Print -> Burn`
|
||||
|
||||
Le rerun suivant `fix-018` ferme la campagne collection spécialisée. `Verify(CollectionV1)` est confirmé avec la signature `39JwUoxF2WWYWvEEWzFxSmKZVwoqXD1wH78sgGaoXv2vZKYUsXDNNnKikeuyQcfCqDftEMHSKFDHnTeSQ3bRpFcJ` au slot `481936730`, puis `Unverify(CollectionV1)` avec `4Kfv42BGZtuBEwqTie5RWwdQ772hyxKiU9d3F3gfRnwVNrxCs2iZMvwL17Rwmuyr3XCVoHdaJVgaqCwv8y7bKoZY` au slot `481936742`. Les deux étapes terminent simulation, confirmation, hydratation canonique, extraction Core, decode replay, matérialisation, trois snapshots stateful et seconde passe idempotente. Le membre suit `verified=false -> true -> false` et le parent `CollectionDetails::V1.size=0 -> 1 -> 0`. La matrice Devnet peut donc promouvoir `Verify` et `Unverify` sur `collection`; elle contient désormais 4 opérations `confirmed` et 16 `not_run`.
|
||||
|
||||
Le lot suivant prépare `Print -> Burn` sans réutiliser les anciens wrappers d’édition. Le master est créé par le chemin courant `Create -> Mint` avec `PrintSupply::Limited(1)`. Le mint d’édition est un compte SPL classique frais, decimals 0, supply 0, avec ATA opérateur vide ; le builder courant `Print` reste responsable de la création de l’édition, du mint du token imprimé et du transfert d’autorité du nouveau mint. Le runner dérive Metadata, Edition et Edition Marker pour l’édition 1 et ajoute une lecture stateful `EditionMarker` dédiée.
|
||||
|
||||
`Print` exige un `edition_mint` signataire distinct du wallet opérateur. La surface Devnet ajoute donc une variante explicite multi-signataires : aucun signataire supplémentaire n’est implicitement accepté, chacun doit être déclaré dans `additional_authorized_signers`, fourni au moment de la signature et rester dans la borne du runner. L’API historique conserve son comportement profile-wallet-only. Pour la fixture fraîche limitée à une seule édition et possédée par le même opérateur que le master, `Burn` doit aussi ramener `MasterEdition.supply` de `1` à `0`, effacer l’unique bit d’Edition Marker puis fermer ce marker devenu vide, en plus de fermer le token account et l'Edition imprimée et de fermer sémantiquement Metadata selon le comportement du programme courant. La matrice garde `Print` et `Burn` à `not_run` tant que cette nouvelle campagne n’a pas terminé `ok` avec preuves réseau complètes.
|
||||
|
||||
## `pre.013-delta-fix-020` — correction de la précondition `Print` observée sur Devnet
|
||||
|
||||
La première tentative `Print -> Burn` de `fix-019` atteint le programme Metaplex pendant la simulation exacte mais n’est pas soumise. `Print` retourne `MetadataError::NotEnoughTokens` (`0x20`). L’erreur est produite avant toute preuve réseau qualifiante ; `Print` et `Burn` restent donc `not_run`.
|
||||
|
||||
Le réaudit du processeur courant ferme le diagnostic. `fix-019` précréait un mint SPL d’édition et son ATA opérateur avec `amount=0`. Pour `Print`, si le mint d’édition est absent, le programme le crée ; si le token account d’édition est absent, il crée l’ATA puis frappe exactement un token. En revanche, lorsqu’un token account est déjà présent, le chemin de validation exige `amount=1` et retourne `NotEnoughTokens` si cette quantité n’est pas satisfaite. La fixture de `fix-019` sélectionnait donc mécaniquement le mauvais chemin avec un ATA existant vide.
|
||||
|
||||
`fix-020` corrige le contrat de fixture sans contourner le programme :
|
||||
|
||||
- création persistante du **keypair** `edition_mint` uniquement ;
|
||||
- refus si le compte mint correspondant existe déjà on-chain ;
|
||||
- dérivation de l’ATA canonique puis refus si cet ATA existe déjà ;
|
||||
- validation séparée de l’ATA master, qui doit contenir exactement un token avant `Print` ;
|
||||
- `edition_mint` reste un signataire supplémentaire explicitement déclaré et réellement fourni ;
|
||||
- après confirmation, les mêmes postconditions fortes restent exigées sur mint/ATA, Metadata, Edition, Edition Marker et Master Edition.
|
||||
|
||||
Le `E0599` local signalé en parallèle ne correspond pas au contenu du delta `fix-019` archivé : ce delta contient bien `MetaplexTokenMetadataAccountKind::EditionMarker`. Il démontre néanmoins qu’un overlay partiel peut laisser `print_burn_campaign.rs` plus récent que `kb-pipeline`. `fix-020` réembarque donc le fichier stateful, valide ses pubkeys de dérivation et ajoute un test d’intégration externe crate-root construisant réellement le variant `EditionMarker`.
|
||||
|
||||
## `pre.013-delta-fix-021` — compte `Edition` compact et futur Tauri `Send`
|
||||
|
||||
La tentative Devnet suivant `fix-020` franchit désormais `Print` : la transaction `2199PTvKyTAfnGRHVaaCceW4u8phdJSu5ULkceD9uJeQyjjenEupK9osRZu1Pzx2NuQaJnAwngSAXMvr5oNtf5y6` est confirmée au slot `482068400`. La campagne s’arrête toutefois pendant les lectures stateful post-confirmation, avant hydratation canonique, extraction Core, replay et matérialisation. `Burn` n’est donc pas atteint et aucune promotion de matrice n’est effectuée.
|
||||
|
||||
Le compte `Edition` fraîchement créé expose la frontière de compatibilité liée à la réduction de taille Metaplex : la structure `Edition` du SDK Rust sérialise 41 octets, tandis que l’allocation on-chain compacte courante utilise 42 octets et l’allocation historique 241 octets. Le décodeur utilisait à tort `Edition::LEN` comme taille de compte stricte. `fix-021` accepte uniquement la longueur sérialisée exacte ou les allocations Metaplex connues avec padding nul, et refuse les longueurs ou paddings inconnus. Une régression `kb-pipeline` matérialise explicitement un compte `Edition` compact de 42 octets. Les erreurs stateful incluent désormais l’adresse et le `kind` du compte visé.
|
||||
|
||||
La validation workspace de `fix-020` révèle en parallèle que `&dyn solana_signer::Signer` est conservé au travers du futur async multi-signataires. Comme le trait `Signer` ne garantit pas `Sync`, la commande Tauri ne satisfait plus son obligation `Future + Send`. Le keypair concret est `Send + Sync`; `fix-021` conserve donc `TemporaryWallet::as_signer()` et ajoute une vue `as_sync_signer()` dédiée, puis borne la nouvelle API multi-signataires à `Signer + Sync`. Les règles d’autorisation des signataires supplémentaires restent inchangées. Le warning Clippy `cloned_ref_to_slice_refs` observé dans le test de garde des signataires est également corrigé.
|
||||
|
||||
## `pre.013-delta-fix-022` — fermeture Metadata, future `Send` et régression `Print`
|
||||
|
||||
Le rerun suivant `fix-021` atteint désormais `Burn` après validation confirmée de son exécution. La dernière garde rapporte `token=false, metadata=true, edition=false, edition_marker=false` : seuls les tests d’existence brute font encore apparaître Metadata. Le processeur courant ferme bien Metadata, mais son helper `close_program_account` réserve un traitement à `MetadataV1` : seul le rent minimum est retiré, car des fees peuvent rester. Lorsqu’un solde résiduel subsiste, le compte est réalloué à un octet `0x00` et devient `Uninitialized` au lieu de disparaître. `fix-022` accepte donc comme fermeture uniquement l’absence du compte ou ce tombstone exact (`owner=Token Metadata`, non exécutable, lamports positifs, `space=1`, `data=[0]`). Token account, Edition et Edition Marker vide doivent toujours être réellement absents.
|
||||
|
||||
La validation workspace expose aussi l’effacement incomplet du marqueur `Sync` : bien que l’API de `fix-021` accepte `Signer + Sync`, elle recréait ensuite une `Vec<&dyn Signer>` qui survivait jusqu’aux `await` suivants et rendait le futur Tauri non-`Send`. `fix-022` confine cette conversion dans un helper synchrone qui signe immédiatement la transaction ; aucun trait object non-`Sync` n’est désormais stocké dans l’état du futur.
|
||||
|
||||
Enfin, le seul échec `kb-lib` de la validation locale (`703/704`) provient du test `modern_print_accepts_transaction_privilege_supersets` : il construisait un `edition_token_record` présent mais readonly, contrairement au builder courant qui exige ce compte writable lorsqu’il est fourni. Le test est corrigé ; le décodeur n’est pas assoupli. La matrice reste à 4 opérations `confirmed` et 16 `not_run` jusqu’au rerun complet et à l’émission des signatures/slots/evidences `Print -> Burn`.
|
||||
|
||||
## `pre.013-delta-fix-023` — qualification `Print/Burn` et préparation du cycle pNFT
|
||||
|
||||
La validation locale de `fix-022` est entièrement propre : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace réussissent. `kb-lib` passe `704/704`, `kb-pipeline` `107/107` plus deux APIs externes, `kb-pipeline-demo-scenarios` `92/92` plus CLI/API externe et le desktop `135/135`.
|
||||
|
||||
La campagne Devnet `Print -> Burn` termine `ok`. `Print` est confirmé avec `REHAxUZrSdKDj8F8k27Amrg7hwuJFhVfstjc9xjn2XGfTX38fH4tFwPeNrF5zeMN5pc21PpUPSgaKXMMfTFd7TE` au slot `482087799`; `Burn` avec `4cNUxGGSWifuAtqdPJAtM3Cjpi4JATitRDqrvEAe85NLpMWcgWmC27bsfjNUmK6sEBDovNPtkPV3wDH1LpfGbhd1` au slot `482087818`. Les postconditions prouvent `MasterEdition.supply 0 -> 1 -> 0`, mint imprimé `0 -> 1 -> 0`, token `0 -> 1`, bit d’Edition Marker consommé, puis fermeture du token account, de l’Edition et du marker redevenu vide. Metadata subsiste uniquement comme tombstone fee exact et est donc sémantiquement fermée. Les preuves pipeline sont complètes et idempotentes. `Print` et `Burn` peuvent être promus : la matrice compte désormais 6 `confirmed` et 14 `not_run`.
|
||||
|
||||
Le sous-lot suivant prépare la campagne pNFT `Delegate(Staking) -> Lock -> Unlock -> Revoke(Staking) -> Delegate(Transfer) -> Transfer`. Cette séquence couvre cinq opérations courantes sans mélanger les rôles : le Staking delegate possède le droit de lock/unlock mais pas de transfer ; le Transfer delegate possède le droit de transfer mais pas le lock. Le runner vérifie à chaque étape le Token Record autoritatif et, après transfert, les deux ATA gelées ainsi que le Token Record destination sans delegate. Les cinq opérations restent `not_run` avant exécution réelle.
|
||||
|
||||
## `pre.013-delta-fix-029` — qualification du lifecycle pNFT et ouverture du probe `Use`
|
||||
|
||||
Le rerun `fix-028` ferme le cycle pNFT avec six transactions confirmées. `Delegate(StakingV1)` est confirmé au slot `482113803`, `Lock` au slot `482113812`, `Unlock` au slot `482113822`, `Revoke(StakingV1)` au slot `482113831`, `Delegate(TransferV1)` au slot `482113840` et `Transfer` au slot `482113850`. Les six étapes possèdent hydratation canonique, extraction Core, replay, matérialisation et idempotence propres. L’état suit `Unlocked -> Staking -> Locked -> Unlocked -> revoked -> Transfer`, puis le transfert laisse la source `amount=0/Initialized`, la destination `amount=1/Frozen` et le TokenRecord destination `Unlocked` sans delegate.
|
||||
|
||||
La matrice v3 promeut donc `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer` sur `programmable_nft`. Le bilan devient **11 opérations `confirmed` et 9 `not_run`**.
|
||||
|
||||
Le réaudit de la source officielle courante impose une précaution avant `Use`. Le client Rust généré expose bien l’instruction `Use` (discriminant 51), avec authority/payer signataires, Metadata writable et comptes token/edition optionnels writable. En revanche, le routeur du programme courant n’expose pas de branche nouvelle API `Use`; `Migrate` est même explicitement routé vers `MetadataError::Removed`. Le code legacy conserve `Utilize`, qui n’est pas la même instruction. Cette divergence client/runtime ne doit être transformée ni en succès supposé ni en indisponibilité supposée.
|
||||
|
||||
`fix-029` prépare donc un probe Devnet borné : NFT classique frais, `Uses::Multiple { remaining: 2, total: 2 }`, `Create -> Mint`, puis simulation exacte de `Use`. Si la simulation est refusée, le probe retourne une classification `runtime_unavailable` avec erreur et logs sans soumettre. Si elle réussit, la transaction est soumise et doit prouver `remaining 2 -> 1`, token inchangé à `amount=1/Initialized`, pipeline complet et idempotence. `Use` reste `not_run` jusqu’à ce probe réel.
|
||||
|
||||
## `pre.013-delta-fix-030` — classification runtime de `Use`
|
||||
|
||||
Le probe Devnet réel de `Use` termine `Create -> Mint`, puis la simulation exacte de l'instruction courante de discriminant 51 est refusée par Token Metadata avec `InstructionError[0]=InvalidInstructionData`. Les logs contiennent `Program log: Error: InvalidInstructionData`; la simulation consomme `12085` compute units et estime les frais à `5000` lamports. Aucune transaction `Use` n'est soumise.
|
||||
|
||||
La classification canonique devient donc `unavailable`, et non `not_run` ni `confirmed`. La matrice contient désormais 11 opérations `confirmed`, 1 `unavailable` et 8 `not_run`. Le premier harness de probe remontait encore la simulation négative comme erreur de readiness ; `fix-030` corrige ce point en autorisant l'observation d'une simulation échouée uniquement pour `submit=false`, sans jamais autoriser l'envoi.
|
||||
|
||||
## `pre.013-delta-fix-033` — qualification escrow et bornage de la maintenance finale
|
||||
|
||||
Le rerun `fix-032` qualifie les trois opérations Token Owned Escrow. `CreateEscrowAccount` est confirmé au slot `482147206`, `TransferOutOfEscrow` au slot `482147252` et `CloseEscrowAccount` au slot `482147267`. Chacune termine hydratation canonique, extraction Core, replay, matérialisation et idempotence. Le dépôt contrôlé d’une unité brute est intégralement restitué, l’ATA du PDA est fermée lorsqu’elle devient vide, le compte escrow est fermé et le NFT parent reste à `amount=1`. La matrice passe à **14 `confirmed`, 1 `unavailable`, 5 `not_run`**.
|
||||
|
||||
Le dernier lot distingue ensuite les opérations selon leurs autorités réelles. `Update` et `Resize` peuvent être exercés par un NFT frais détenu par l’opérateur. `Migrate` est explicitement routé vers `Removed` dans le programme courant. `Collect` est réservé à `FEE_AUTHORITY=Levytx9LLPzAtDJJD7q813Zsm8zg9e1pb53mGxTKpD7`, et `CloseAccounts` à `OWNERLESS_CLOSE_AUTHORITY=C1oseLQExhuEzeBhsVbLtseSpVgvpHDbBj3PTevBCEBh`. La campagne de maintenance ne tente donc pas de contourner ces autorités : elle soumet seulement `Update`/`Resize` et simule les trois autres pour obtenir une classification runtime observable.
|
||||
|
||||
## `pre.013-delta-fix-034` — classification `Resize` sur comptes courants
|
||||
|
||||
Le premier run de maintenance après `fix-033` franchit la transaction `Update` et sa postcondition `primary_sale_happened false -> true`, puis la simulation de `Resize` atteint `IX: Resize` et s'arrête avec `Account has already been resized`, `Custom(201)`, après `14643` compute units. La politique simulation-first empêche toute soumission `Resize`. La sortie du test étant émise seulement après le résumé complet, la signature et le slot du `Update` déjà validé en interne ne sont pas disponibles et cette opération n'est pas encore promue.
|
||||
|
||||
Cette réponse est conforme à la fonction de `Resize` : les créations courantes sont déjà au layout cible et une exécution positive nécessite un compte legacy sous-dimensionné que le scénario ne peut pas fabriquer comme compte Token Metadata program-owned. `Resize` est donc classé `unavailable` pour la qualification contrôlée actuelle. La matrice passe à **14 `confirmed`, 2 `unavailable` et 4 `not_run`**. Le rerun final soumet uniquement `Update` et simule `Resize(201)`, `Migrate(75)`, `Collect(7)` et `CloseAccounts(188)`.
|
||||
|
||||
## Clôture fonctionnelle `pre.013-delta-fix-036`
|
||||
|
||||
Le dernier rerun maintenance ferme toutes les lignes encore ouvertes. `Update` est confirmé sur NFT avec simulation au slot `482162408`, confirmation au slot `482162413`, signature `4hxxBnsCA1JBJPNHgXRDeq1W7rXbFj2yySscWJKxU1VTMDcPzPfoWy7QzANHSDsbQfxxcwuAQNDLqEukgsug6qoZ`, replay, matérialisation, idempotence et transition `primary_sale_happened false -> true`.
|
||||
|
||||
Les quatre probes finaux sont tous négatifs avant soumission et correspondent au contrat courant : `Resize -> AccountAlreadyResized(201)` au slot `482162417`, `Migrate -> Removed(75)` au slot `482162422`, `Collect -> UpdateAuthorityIncorrect(7)` au slot `482162426`, `CloseAccounts -> InvalidCloseAuthority(188)` au slot `482162430`. `Resize` requiert un état legacy contrôlé absent des fixtures modernes ; `Migrate` est retiré ; `Collect` et `CloseAccounts` sont réservés à des autorités Metaplex fixes.
|
||||
|
||||
La matrice v3 est donc intégralement classée : **15 `confirmed`, 5 `unavailable`, 0 `not_run`**. Aucun statut `confirmed` n'est dérivé d'une preuve synthétique ou d'une simple simulation. Le travail fonctionnel de réaudit Metaplex de `pre.013` est terminé ; la suite est une consolidation documentaire et de conformité avant `pre.014`.
|
||||
|
||||
## `pre.013-delta-fix-037` — consolidation finale du réaudit Metaplex
|
||||
|
||||
La validation locale suivant `fix-036` ferme la prerelease sans nouvelle modification fonctionnelle : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et `python3 scripts/audit_rust_workspace_rules.py` sont propres. `cargo test -p kb-pipeline-demo-scenarios` réussit avec 105 tests unitaires, 1 test CLI et 1 test API externe. Le warning `if_same_then_else` observé avant `fix-036` a disparu sans suppression de lint.
|
||||
|
||||
La matrice Devnet reste la source de vérité opérationnelle et contient exactement **15 opérations `confirmed`, 5 `unavailable` et 0 `not_run`**. Les cinq indisponibilités sont bornées et distinctes : `Use` est rejeté par le runtime courant avec `InvalidInstructionData`; `Resize` retourne `AlreadyResized(201)` sur les comptes créés au layout courant et nécessiterait un état legacy contrôlé pour un succès positif; `Migrate` retourne `Removed(75)`; `Collect` exige l'autorité Metaplex fixe de collecte; `CloseAccounts` exige l'autorité Metaplex ownerless-close fixe. Aucune de ces quatre dernières surfaces n'est soumise après une simulation négative.
|
||||
|
||||
Les formulations d'usage encore restées au statut préparatoire sont réconciliées : collection `Verify/Unverify`, lifecycle pNFT et Token Owned Escrow sont décrits comme campagnes qualifiées, le guide documente la campagne maintenance finale, et le TODO retire les tâches `pre.013` terminées. Les mentions `not_run` conservées dans les sections chronologiques de cet audit et des rapports intermédiaires restent intentionnelles : elles décrivent l'état observé au moment de chaque delta et ne constituent pas le statut courant.
|
||||
|
||||
`0.4.8-pre.013` est donc **terminée et validée**. Le prochain développement non-fix peut ouvrir `0.4.8-pre.014` pour la finalisation desktop metadata, sans rouvrir les campagnes Metaplex sauf régression ou changement de contrat explicitement observé.
|
||||
@@ -0,0 +1,182 @@
|
||||
<!-- file: docs/audits/V0_4_8_PRE_015_METADATA_TRANSVERSAL_RECONCILIATION_AUDIT.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Audit transversal Metadata `0.4.8-pre.015`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Cet audit ouvre la réconciliation transversale après la clôture de la finalisation desktop en `pre.014`. Il compare les trois domaines Metadata actifs :
|
||||
|
||||
- Metaplex Token Metadata ;
|
||||
- Token-2022 Token Metadata ;
|
||||
- Solana Program Metadata.
|
||||
|
||||
Le contrôle porte sur le stockage, les matrices contractuelles et réseau, les registres runtime, les exports publics et la nomenclature IDL. Il n’ouvre aucune nouvelle campagne Devnet et ne réinterprète pas les matrices historiques fermées.
|
||||
|
||||
## 2. Stockage
|
||||
|
||||
Les trois matérialisateurs Metadata produisent des `MtApiMaterializedOutput` de famille `Metadata`. Leur persistance passe par le stockage générique introduit par `0004_decode_materialization_store.sql` :
|
||||
|
||||
- `kb_sol_decode_events` conserve les observations décodées et leur payload JSONB ;
|
||||
- `kb_sol_mat_events` conserve les sorties matérialisées avec `processor_name`, `processor_version`, `input_key`, `output_key`, `materialized_family` et `payload_jsonb` ;
|
||||
- l’identité d’une projection est bornée par l’index unique `(processor_name, processor_version, input_key, output_key)` ;
|
||||
- les requêtes de lecture savent filtrer les sorties matérialisées par `processor_name`, `materialized_family` et signature.
|
||||
|
||||
Metaplex Token Metadata, Token-2022 Token Metadata et Solana Program Metadata n’exigent donc pas trois schémas spécialisés. Le nom du matérialiseur identifie le producteur et `payload_jsonb` conserve les projections hétérogènes sans perdre leur contrat métier.
|
||||
|
||||
**Conclusion stockage : aucune migration PostgreSQL n’est requise en `pre.015`.** Une table spécialisée ne serait justifiée que par un futur besoin de requête ou de contrainte relationnelle impossible à exprimer proprement avec le store générique actuel.
|
||||
|
||||
## 3. Matrices
|
||||
|
||||
### 3.1 Solana Program Metadata
|
||||
|
||||
Les matrices actives restent cohérentes entre elles :
|
||||
|
||||
| Matrice | Inventaire |
|
||||
|---|---:|
|
||||
| comptes | 3 entrées |
|
||||
| instructions stables | 9 |
|
||||
| exécuteur | 9 opérations |
|
||||
| matérialisation | 2 snapshots + 9 faits d’instruction |
|
||||
| pipeline | 9 opérations |
|
||||
| validation Devnet | 9 opérations `confirmed` |
|
||||
|
||||
La matrice Devnet conserve `0.4.8-pre.009` comme milestone propriétaire du contrat de scénarios, tandis que ses preuves pointent vers la campagne réelle `pre.010`. Ce découpage est codé explicitement dans le validateur et n’est pas une divergence à corriger.
|
||||
|
||||
### 3.2 Token-2022 Token Metadata
|
||||
|
||||
`SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json` contient exactement les cinq opérations de `spl-token-metadata-interface` :
|
||||
|
||||
- `Initialize` ;
|
||||
- `UpdateField` ;
|
||||
- `Emit` ;
|
||||
- `RemoveKey` ;
|
||||
- `UpdateAuthority`.
|
||||
|
||||
Les cinq lignes sont `confirmed` sous le Program ID Token-2022. La matrice générale `SPL_TOKEN_2022_VALIDATION_MATRIX.json` reste un document historique du périmètre `0.4.6` et conserve volontairement ses scénarios réseau non exécutés de cette époque ; elle ne doit pas être réécrite à partir des preuves Metadata de `0.4.8`.
|
||||
|
||||
### 3.3 Metaplex Token Metadata
|
||||
|
||||
La matrice réseau courante ferme les 20 opérations :
|
||||
|
||||
- 15 `confirmed` ;
|
||||
- 5 `unavailable` ;
|
||||
- 0 `not_run`.
|
||||
|
||||
La matrice contractuelle `METAPLEX_TOKEN_METADATA_MATRIX.json` ferme parallèlement les 58 discriminateurs historiques :
|
||||
|
||||
- 20 `executable_current` ;
|
||||
- 15 `executable_deprecated` ;
|
||||
- 22 `decode_only_replaced` ;
|
||||
- 1 `decode_only_bridge_boundary`.
|
||||
|
||||
Son inventaire métier est correct, mais ses métadonnées de progression étaient restées sur l’ancien état `pre.003/pre.007` : phase d’exécution partielle, builders encore annoncés comme futurs et orchestration stateful encore listée dans `next_proofs`. Ces champs sont réconciliés avec la clôture réelle de `pre.013`, sans modifier les 58 lignes du contrat.
|
||||
|
||||
Les matrices `METAPLEX_TOKEN_METADATA_VALIDATION_MATRIX.json` (`0.4.7`) et `METAPLEX_TOKEN_METADATA_CROSS_VALIDATION_MATRIX.json` (`0.4.7-pre.010`) sont des corpus historiques fermés dont le validateur impose explicitement le milestone. Leurs statuts ne sont donc pas promus artificiellement à partir de la matrice réseau `pre.013`.
|
||||
|
||||
## 4. Registres runtime et exports
|
||||
|
||||
Le replay desktop enregistre explicitement les trois matérialisateurs :
|
||||
|
||||
- `MtMetadataMetaplexTokenMetadataMaterializer` ;
|
||||
- `MtMetadataSolanaProgramMaterializer` ;
|
||||
- `MtMetadataToken2022Materializer`.
|
||||
|
||||
Il enregistre également les décodeurs Metaplex Token Metadata, Solana Program Metadata et Token-2022. Les trois matérialisateurs sont exportés depuis la façade `kb-lib`.
|
||||
|
||||
`kb-pipeline` possède déjà des tests d’API externe dédiés aux pipelines Metaplex Token Metadata et Solana Program Metadata. La surface Token-2022 expose bien ses contrats stateful et Metadata depuis la racine de crate, mais ne possédait pas de test externe dédié dans `kb-pipeline/tests`.
|
||||
|
||||
**Correction `pre.015-delta` :** ajout d’un test externe Token-2022 Token Metadata couvrant la construction de la requête stateful et l’accessibilité crate-root des contrats `Emit` et de postcondition d’autorité.
|
||||
|
||||
## 5. IDL et nomenclature
|
||||
|
||||
Deux écarts documentaires actifs sont constatés :
|
||||
|
||||
1. le fichier Metaplex présent sous `idls/` est `metadata.metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s.metaplex_token_metadata.V1_14_0.from_github_mpl-token-metadata.json`, alors que `IDL_AUDIT.md` et `IDL_TO_KB_LIB_NOMENCLATURE.md` utilisaient encore le suffixe historique `from_solscan.json` ;
|
||||
2. la ligne Solana Program Metadata de la nomenclature indiquait encore « implémenter l’exécuteur », alors que modèles, décodeurs, matérialiseur, exécuteur, pipeline, scénarios et validation Devnet sont déjà présents.
|
||||
|
||||
Les deux documents sont corrigés. L’IDL JSON elle-même reste inchangée.
|
||||
|
||||
## 6. Changements du premier delta
|
||||
|
||||
Le premier delta de `pre.015` :
|
||||
|
||||
- passe `workspace.package.version` à `0.4.8-pre.15` ;
|
||||
- n’ajoute aucune migration de stockage ;
|
||||
- corrige les deux documents IDL ;
|
||||
- réconcilie uniquement les métadonnées de progression de la matrice contractuelle Metaplex active ;
|
||||
- ajoute le test d’API externe Token-2022 Token Metadata dans `kb-pipeline` ;
|
||||
- conserve les matrices historiques fermées sans réécriture ;
|
||||
- prépare la suite de la réconciliation avant `pre.016`.
|
||||
|
||||
## 7. Validation du delta initial
|
||||
|
||||
La validation locale du 9 août 2026 est complète :
|
||||
|
||||
- `cargo fmt --all` : terminé ;
|
||||
- `cargo check --workspace` : terminé ;
|
||||
- `cargo clippy --all-targets` : terminé sans warning ;
|
||||
- `python3 scripts/audit_rust_workspace_rules.py` : audits général, exports et workspace propres ;
|
||||
- `cargo test -p kb-pipeline` : 109 tests unitaires, trois tests d’API Metadata externes et doc-tests réussis ;
|
||||
- `cargo test -p kb-lib` : 706 tests unitaires, huit tests d’intégration et doc-tests réussis ;
|
||||
- `cargo test -p kb-pipeline-demo-scenarios` : 105 tests unitaires, 1 test CLI, 1 test API externe et doc-tests réussis.
|
||||
|
||||
Le test externe Token-2022 ajouté par le delta compile donc bien uniquement contre la façade publique de `kb-pipeline`.
|
||||
|
||||
## 8. Seconde passe — registres, exports et documentation active
|
||||
|
||||
Le registre desktop `demo_decode_replay` contient les trois matérialiseurs spécialisés et leurs identités exactes : Metaplex Token Metadata, Solana Program Metadata et Token-2022. Le test externe générique `kb-lib/tests/external_materializer_api.rs` impose déjà que les trois types satisfassent les façades publiques `MtApiEventMaterializer` et `MtMaterializer`.
|
||||
|
||||
L’exécuteur Solana Program Metadata possède déjà un test externe dédié, tandis que Metaplex n’avait qu’une couverture interne et des consommateurs workspace. `pre.015-delta-fix-001` ajoute donc un test downstream-style qui parcourt les 35 opérations supportées Metaplex via `ExMetadataMetaplexTokenMetadataExecutor`, vérifie les 15 codes deprecated et le refus d’un Program ID étranger, en n’utilisant que les exports crate-root.
|
||||
|
||||
Trois écarts de documentation active sont corrigés simultanément :
|
||||
|
||||
- le rustdoc public `EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES` ne prétend plus être borné à `0.4.7-pre.005` ;
|
||||
- `kb-pipeline-demo-scenarios/USAGE.md` reflète les neuf opérations Solana Program Metadata `confirmed` de `pre.010` au lieu de présenter encore la matrice comme `not_run` ;
|
||||
- `docs/DEVNET_EXECUTION_GUIDE.md` remplace la « future fenêtre Metadata » et la section Metaplex hors ordre par un scénario Metadata on-chain unique, couvrant les trois domaines réellement exposés dans `demo_execution_metadata` et interdisant toujours le fetch off-chain.
|
||||
|
||||
`docs/README.md` référence désormais les rapports Devnet Token-2022 `pre.012`, Metaplex final `pre.013`, desktop Metadata `pre.014` et le présent audit `pre.015`.
|
||||
|
||||
La réconciliation des TODO retire également uniquement les tâches déjà closes : les validations Solana Program Metadata mainnet/Devnet/desktop et les anciennes fixtures Metaplex spécialisées. Les dettes `0.5.x`, ElGamal conditionnelles et les évolutions générales restent ouvertes.
|
||||
|
||||
## 9. Contrôles du correctif
|
||||
|
||||
Après application de `pre.015-delta-fix-001`, les validations locales minimales sont :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo clippy --all-targets
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo test -p kb-lib
|
||||
```
|
||||
|
||||
Aucune campagne Devnet n’est requise : les changements réseau sont nuls et les statuts corrigés proviennent de matrices et rapports déjà qualifiés.
|
||||
|
||||
## 10. Validation de `pre.015-delta-fix-001`
|
||||
|
||||
La validation locale du 9 août 2026 confirme la seconde passe :
|
||||
|
||||
- `cargo fmt --all` : terminé ;
|
||||
- `cargo check --workspace` : terminé ;
|
||||
- `cargo clippy --all-targets` : terminé sans warning ;
|
||||
- `python3 scripts/audit_rust_workspace_rules.py` : audits général, exports et workspace propres ;
|
||||
- `cargo test -p kb-lib` : 706 tests unitaires, neuf tests d’intégration et doc-tests réussis.
|
||||
|
||||
Le nouveau test externe Metaplex `external_metaplex_token_metadata_executor_api` passe donc depuis un consommateur downstream-style utilisant uniquement les exports crate-root.
|
||||
|
||||
## 11. Conclusion
|
||||
|
||||
La réconciliation transversale ne laisse plus de divergence fonctionnelle active entre les trois domaines Metadata :
|
||||
|
||||
- aucune migration PostgreSQL spécialisée n’est requise ;
|
||||
- les matrices réseau courantes concordent avec les preuves qualifiées ;
|
||||
- les matrices historiques restent volontairement figées à leur milestone ;
|
||||
- les trois décodeurs et matérialiseurs sont présents dans les registres runtime attendus ;
|
||||
- les exports publics disposent de contrats externes cohérents ;
|
||||
- les références IDL et la nomenclature active sont réconciliées ;
|
||||
- les README, USAGE, TODO et guides actifs ne portent plus de statut Metadata devenu faux.
|
||||
|
||||
Deux tâches de niveau release restent volontairement hors de `pre.015` : transformer le ROADMAP pour refléter l’état final de `0.4.8` et mettre à jour le CHANGELOG général uniquement après la validation finale. Elles appartiennent explicitement à `pre.016` et ne constituent pas une divergence des contrats Metadata.
|
||||
|
||||
**Statut de `0.4.8-pre.015` : terminé et validé.** La suite peut ouvrir `0.4.8-pre.016`, réservé à la conformité finale, à la documentation de release, au nettoyage/archivage et à la préparation du prompt suivant.
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,45 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_008_SOLANA_PROGRAM_METADATA_MAINNET_REPLAY_REPORT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Validation mainnet du replay Solana Program Metadata — `0.4.8-pre.008`
|
||||
|
||||
## Résultat
|
||||
|
||||
La campagne historique exécutée après `pre.008-fix-002` valide le parcours réel `backfill → Core extraction → decode replay → matérialisation` pour les instructions Solana Program Metadata observées.
|
||||
|
||||
```text
|
||||
dispatched = 132
|
||||
decoded = 132
|
||||
failed = 0
|
||||
processingErrors = 0
|
||||
materializedOutputs = 132
|
||||
materializationRefused = 0
|
||||
```
|
||||
|
||||
## Répartition observée
|
||||
|
||||
| Opération | Observée | Décodée | Matérialisée |
|
||||
|----------------|---------:|--------:|-------------:|
|
||||
| `Write` | 88 | 88 | 88 |
|
||||
| `Allocate` | 15 | 15 | 15 |
|
||||
| `Initialize` | 11 | 11 | 11 |
|
||||
| `SetAuthority` | 7 | 7 | 7 |
|
||||
| `Close` | 5 | 5 | 5 |
|
||||
| `SetData` | 5 | 5 | 5 |
|
||||
| `SetImmutable` | 1 | 1 | 1 |
|
||||
| `Trim` | 0 | 0 | 0 |
|
||||
| `Extend` | 0 | 0 | 0 |
|
||||
|
||||
Les sept opérations rencontrées sont validées sur données mainnet réelles. `Trim` et `Extend` restent validées synthétiquement mais non observées dans cet échantillon. Elles doivent être exercées par les scénarios Devnet de `pre.009` et `pre.010`.
|
||||
|
||||
## Anomalies non liées
|
||||
|
||||
Quatre entrées Loader v4 ont été classées `Unsupported` sans échec de traitement. Elles proviennent de transactions déjà échouées avec `ProgramAccountNotFound` et ne concernent pas `ProgM6…`.
|
||||
|
||||
## Preuve conservée
|
||||
|
||||
- archive : `docs/validation/evidence/v0.4.8/pre.008/mainnet/mainnet_research_v0.4.8-pre.008-01.zip` ;
|
||||
- SHA-256 : `16b3722d4b75afdef304f7b16af4baa3b51713d87a74e9e4a7aac0c0f0411335` ;
|
||||
- manifeste : `docs/validation/evidence/v0.4.8/pre.008/mainnet/SHA256SUMS`.
|
||||
|
||||
Cette archive est une preuve documentaire immuable. Le code, les tests et les runners ne doivent jamais en dépendre.
|
||||
@@ -0,0 +1,59 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_010_SOLANA_PROGRAM_METADATA_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Validation Devnet Solana Program Metadata — `0.4.8-pre.010`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Ce rapport clôt la validation réseau de l’intégration Solana Program Metadata livrée entre `0.4.8-pre.009` et `0.4.8-pre.010`.
|
||||
|
||||
La preuve brute est conservée sous [`evidence/v0.4.8/pre.010/devnet/`](evidence/v0.4.8/pre.010/devnet/README.md).
|
||||
|
||||
## 2. Environnement observé
|
||||
|
||||
- cluster : Solana Devnet ;
|
||||
- endpoint : profil `local_devnet` ;
|
||||
- autorité opérateur : wallet temporaire persistant du profil ;
|
||||
- programme : `ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S` ;
|
||||
- mode : simulation obligatoire, soumission contrôlée, confirmation, lecture stateful et postcondition.
|
||||
|
||||
Le nom source de l’archive indiquait `mainnet_research`, mais son contenu utilise Devnet. Elle est donc classée comme preuve Devnet.
|
||||
|
||||
## 3. Campagnes exécutées
|
||||
|
||||
Deux fixtures indépendantes ont été préparées. Chaque campagne a exécuté :
|
||||
|
||||
1. deux transferts de préfinancement ;
|
||||
2. le parcours Buffer `Allocate → Extend → Write → SetAuthority → Trim → Close` ;
|
||||
3. le parcours Metadata `Initialize → SetData → SetImmutable`.
|
||||
|
||||
Le corpus contient ainsi :
|
||||
|
||||
- 2 campagnes complètes ;
|
||||
- 22 transactions confirmées ;
|
||||
- 18 opérations Solana Program Metadata ;
|
||||
- 18 postconditions `Confirmed` ;
|
||||
- 0 fichier d’erreur non vide ;
|
||||
- 0 réponse HTTP ou JSON-RPC `429` observée.
|
||||
|
||||
## 4. Résultats par opération
|
||||
|
||||
| Opération | Exécutions confirmées | Postconditions confirmées |
|
||||
|----------------|----------------------:|--------------------------:|
|
||||
| `Allocate` | 2 | 2 |
|
||||
| `Extend` | 2 | 2 |
|
||||
| `Write` | 2 | 2 |
|
||||
| `SetAuthority` | 2 | 2 |
|
||||
| `Trim` | 2 | 2 |
|
||||
| `Close` | 2 | 2 |
|
||||
| `Initialize` | 2 | 2 |
|
||||
| `SetData` | 2 | 2 |
|
||||
| `SetImmutable` | 2 | 2 |
|
||||
|
||||
`Close` est validée par l’absence finale du compte. Les autres opérations sont validées par une lecture stateful et une projection cohérente avec la postcondition attendue.
|
||||
|
||||
## 5. Conclusion
|
||||
|
||||
La couverture exécutable des neuf instructions stables Solana Program Metadata est validée sur Devnet, y compris `Extend` et `Trim`, qui n’avaient pas été observées dans l’échantillon historique mainnet de `pre.008`.
|
||||
|
||||
La surface Solana Program Metadata peut être considérée comme complète pour `0.4.8`, sous réserve des réconciliations transversales et documentaires prévues avant la clôture finale de la version.
|
||||
@@ -0,0 +1,86 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_012_TOKEN_2022_METADATA_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.012` — Token-2022 Token Metadata
|
||||
|
||||
## Statut
|
||||
|
||||
**Confirmée sur Devnet le 7 août 2026.**
|
||||
|
||||
La campagne complète a été exécutée sur un mint Token-2022 frais. Les cinq opérations prévues ont produit une transaction confirmée et une postcondition `Confirmed`.
|
||||
|
||||
## Program ID
|
||||
|
||||
```text
|
||||
TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
|
||||
```
|
||||
|
||||
## Fixture
|
||||
|
||||
```text
|
||||
mint=EHrx1evxgXLEgjb1sgc5dVTktXX1LiovXR9YFsZS3d4k
|
||||
preparation_signature=2yYciRNoTTNSGuuaBFE6925FPjSj4fXp8usSkpLhgEPuhQMJYdsEzkAzxBdjDhpPHCT3db9bLz9Ud4BU5QSQZzQ5
|
||||
initial_authority=J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH
|
||||
final_authority=3RVXUmVvY7quUUEVsDKh5EcenVygwv3mo1iagPa4Pmq7
|
||||
```
|
||||
|
||||
La préparation crée `MetadataPointer` avant `InitializeMint2`, le fait pointer vers le mint lui-même et vérifie statefully l'absence initiale de `TokenMetadata`.
|
||||
|
||||
## Transactions confirmées
|
||||
|
||||
| Scénario | Opération canonique | Signature | Slot | Postcondition |
|
||||
|----------------------------------------|--------------------------------------------------|--------------------------------------------------------------------------------------------|----------:|---------------|
|
||||
| `token_2022_metadata_initialize` | `spl.token_2022.initialize_token_metadata` | `48kzJR5VW1ZYSRkVrg6qyNmuHdAUu7NXcnzaNsSmH7SSmRXK1X6RQdCeeSQi6LhrrkgRFRGVnxkwXPtkiqG6EgnY` | 481804441 | `Confirmed` |
|
||||
| `token_2022_metadata_update_field` | `spl.token_2022.update_token_metadata_field` | `JrdB9EqePS1FVWLo55w1Vhv2eTCTfCpXZgMvPCocW3m3iu4nk3BFBBRHoe2Nbcpq1rmnZoFrhnag5fG2WwmJrYs` | 481804454 | `Confirmed` |
|
||||
| `token_2022_metadata_emit` | `spl.token_2022.emit_token_metadata` | `3TKdBCJhM1VEZ9ti6EYR9gSLJnH5ni6FjW4kvoqwdYK1u14jdY6hS8hf2VGeLi21PbX5CWiyzjthJZERK5KHroWH` | 481804464 | `Confirmed` |
|
||||
| `token_2022_metadata_remove_key` | `spl.token_2022.remove_token_metadata_key` | `3kDBV7pDFgoCQVJ9DYYuxvHFbCJzyA6J2rLg6ndGCETEhhFBZ1ipRQ52X2hZfwNPfdJwEFFBmbyMU6kFGnVXJNrm` | 481804473 | `Confirmed` |
|
||||
| `token_2022_metadata_update_authority` | `spl.token_2022.update_token_metadata_authority` | `5aBpQsm6mAHA5xSN5tZ1ubALbfghGa7JScjXn9NGhg9agzEdjXydH6jgmn6UmxinGHmMvtqaGRLk9dgjmmz4HpU2` | 481804482 | `Confirmed` |
|
||||
|
||||
## Preuves spécialisées
|
||||
|
||||
### `Initialize`
|
||||
|
||||
Le snapshot final confirme les champs initiaux du `TokenMetadata` et une matérialisation instructionnelle est observée.
|
||||
|
||||
### `UpdateField`
|
||||
|
||||
La paire additionnelle `campaign=0.4.8-pre.012` est présente dans le snapshot autoritatif et une matérialisation instructionnelle est observée.
|
||||
|
||||
### `Emit`
|
||||
|
||||
La simulation retourne **202 octets** de `returnData`. Le runner décode la valeur complète et exige son égalité avec le `TokenMetadata` relu depuis le TLV du mint. La politique n'exige pas de matérialisation instructionnelle pour cette opération non mutante.
|
||||
|
||||
### `RemoveKey`
|
||||
|
||||
La paire `campaign` est absente du snapshot final et une matérialisation instructionnelle est observée.
|
||||
|
||||
### `UpdateAuthority`
|
||||
|
||||
L'autorité finale du snapshot TLV est :
|
||||
|
||||
```text
|
||||
3RVXUmVvY7quUUEVsDKh5EcenVygwv3mo1iagPa4Pmq7
|
||||
```
|
||||
|
||||
Elle correspond exactement à l'autorité fraîche préparée avant la campagne.
|
||||
|
||||
## Matrice machine-readable
|
||||
|
||||
`test-fixtures/contract-matrices/SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json` porte désormais les cinq scénarios au statut `confirmed` et conserve toutes les catégories de preuve requises.
|
||||
|
||||
## Validations locales
|
||||
|
||||
Avant et après la campagne :
|
||||
|
||||
```text
|
||||
cargo fmt --all réussi
|
||||
cargo check --workspace réussi
|
||||
cargo clippy --all-targets réussi
|
||||
python3 scripts/audit_rust_workspace_rules.py clean
|
||||
cargo test -p kb-pipeline-demo-scenarios 69 + 1 + 1 tests réussis
|
||||
cargo test --workspace réussi
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
La campagne `0.4.8-pre.012` fournit une preuve Devnet complète pour les cinq instructions de `spl-token-metadata-interface` intégrées à Token-2022. Aucune des cinq lignes de la matrice ne reste `not_run`.
|
||||
@@ -0,0 +1,139 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_COLLECTION_VERIFY_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — collection Metaplex `Verify -> Unverify`
|
||||
|
||||
## Statut
|
||||
|
||||
**Confirmé sur Devnet — `Verify` et `Unverify` qualifiés avec transition collection complète.**
|
||||
|
||||
Le rerun après `pre.013-delta-fix-018` termine le parcours `Verify(CollectionV1) -> Unverify(CollectionV1)` de bout en bout. Les deux opérations passent simulation exacte, confirmation, hydratation canonique, extraction Core, decode replay, matérialisation instructionnelle, snapshots stateful et seconde passe idempotente. La matrice Devnet v3 les promeut donc à `confirmed` sur la famille `collection`.
|
||||
|
||||
## Première tentative réseau après `fix-017`
|
||||
|
||||
La transaction `Verify(CollectionV1)` est confirmée sur Devnet :
|
||||
|
||||
- signature : `2kR1VeWdCW3FcKRZdNuKMMc3kGUmWRBXxXX8W1NNi2rhWfZYTg9zsDvJHx7WvebgUfc2K382J14i66E2qPEnjsqR` ;
|
||||
- slot confirmé : `481931061` ;
|
||||
- hydratation canonique : réussie ;
|
||||
- extraction Core : réussie ;
|
||||
- snapshots stateful : `3` ;
|
||||
- replay : `selected=1`, `started=1`, `completed=1`, `failed_inputs=1`, `decoded=0` ;
|
||||
- matérialisation instructionnelle : absente à cause de l’échec du décodeur ;
|
||||
- `Unverify` : non atteint.
|
||||
|
||||
Le diagnostic est local au replay. L’autorité de collection est également le fee payer : le message Solana la projette donc `signer + writable`, alors que l’AccountMeta `Verify` courant exige seulement `signer`. Le décodeur comparait encore les flags de façon exacte au lieu de traiter les privilèges transactionnels comme des sur-ensembles. Le même audit révèle que `Unverify` était artificiellement borné à 8 positions alors que le wrapper courant en construit 7. `fix-018` corrige ces deux défauts sans promouvoir la preuve partielle.
|
||||
|
||||
## Rerun réussi après `fix-018`
|
||||
|
||||
### `Verify(CollectionV1)`
|
||||
|
||||
- signature : `39JwUoxF2WWYWvEEWzFxSmKZVwoqXD1wH78sgGaoXv2vZKYUsXDNNnKikeuyQcfCqDftEMHSKFDHnTeSQ3bRpFcJ` ;
|
||||
- simulation context slot : `481936725` ;
|
||||
- confirmation slot : `481936730` ;
|
||||
- simulation : succès, `5` logs ;
|
||||
- message hash : `9o3WH8iWDcpoTuwx582MGPHAYiRmy36SRm1579Zrh9tw` ;
|
||||
- fee : `5000` lamports ;
|
||||
- snapshots avant/après : `3 / 3` ;
|
||||
- hydratation canonique, extraction Core, decode replay, matérialisation et idempotence : réussis ;
|
||||
- matérialisations instructionnelles : `1` ; snapshots matérialisés : `3`.
|
||||
|
||||
### `Unverify(CollectionV1)`
|
||||
|
||||
- signature : `4Kfv42BGZtuBEwqTie5RWwdQ772hyxKiU9d3F3gfRnwVNrxCs2iZMvwL17Rwmuyr3XCVoHdaJVgaqCwv8y7bKoZY` ;
|
||||
- simulation context slot : `481936738` ;
|
||||
- confirmation slot : `481936742` ;
|
||||
- simulation : succès, `4` logs ;
|
||||
- message hash : `HcrUFoW8i7oRVxbmyt8cpHpwgL2nSTaH9WuPsLEYfSKT` ;
|
||||
- fee : `5000` lamports ;
|
||||
- snapshots avant/après : `3 / 3` ;
|
||||
- hydratation canonique, extraction Core, decode replay, matérialisation et idempotence : réussis ;
|
||||
- matérialisations instructionnelles : `1` ; snapshots matérialisés : `3`.
|
||||
|
||||
### Transition autoritative observée
|
||||
|
||||
- membre : `collection.verified = false -> true -> false` ;
|
||||
- parent : `CollectionDetails::V1.size = 0 -> 1 -> 0`.
|
||||
|
||||
Ces preuves remplissent les seize dimensions réseau exigées par opération dans la matrice v3.
|
||||
|
||||
## Contrat de fixture
|
||||
|
||||
Le parcours prépare deux actifs frais :
|
||||
|
||||
1. un Collection NFT parent avec `CollectionDetails::V1 { size: 0 }`, Master Edition et supply `1` ;
|
||||
2. un NFT membre avec une relation `collection = { key: <parent mint>, verified: false }`, Master Edition et supply `1`.
|
||||
|
||||
Les deux actifs passent d’abord par le runner qualifié `Create -> Mint`. Le parcours spécialisé refuse donc de commencer `Verify` si le membre n’est pas explicitement non vérifié ou si la taille du parent n’est pas exactement `0`.
|
||||
|
||||
## Opérations qualifiées par le runner
|
||||
|
||||
Le runner spécialisé exécute ensuite exactement :
|
||||
|
||||
1. `metadata.metaplex_token_metadata.verify` avec `VerificationArgs::CollectionV1` ;
|
||||
2. `metadata.metaplex_token_metadata.unverify` avec `VerificationArgs::CollectionV1`.
|
||||
|
||||
`Verify` reçoit le mint, la metadata et la Master Edition du parent. `Unverify` reçoit le mint et la metadata du parent conformément au wrapper courant `mpl-token-metadata 5.1.1`.
|
||||
|
||||
## Postconditions obligatoires
|
||||
|
||||
Le runner lit après chaque confirmation :
|
||||
|
||||
- la Metadata PDA du membre ;
|
||||
- la Metadata PDA du parent ;
|
||||
- la Master Edition PDA du parent.
|
||||
|
||||
Les transitions attendues sont fermées :
|
||||
|
||||
| État | `collection.verified` membre | `CollectionDetails::V1.size` parent |
|
||||
|------------------|-----------------------------:|------------------------------------:|
|
||||
| avant `Verify` | `false` | `0` |
|
||||
| après `Verify` | `true` | `1` |
|
||||
| après `Unverify` | `false` | `0` |
|
||||
|
||||
La taille est validée comme état dérivé du programme ; le runner n’utilise pas l’opération obsolète `SetCollectionSize` pour fabriquer la postcondition.
|
||||
Le snapshot stateful provient de `serde_json::to_value(mpl_token_metadata::accounts::Metadata)` ; le champ lu est donc exactement `collection_details.V1.size`, conformément au nom Rust sérialisé par le SDK 5.1.1.
|
||||
|
||||
Chaque opération doit également démontrer :
|
||||
|
||||
- simulation exacte réussie ;
|
||||
- confirmation `Confirmed` ou `Finalized` avec slot ;
|
||||
- hydratation canonique ;
|
||||
- extraction Core ;
|
||||
- decode replay Metaplex ;
|
||||
- au moins une matérialisation instructionnelle ;
|
||||
- snapshots stateful matérialisés ;
|
||||
- seconde passe idempotente sans nouvel output ni refus.
|
||||
|
||||
## Commande opérateur
|
||||
|
||||
```bash
|
||||
KB_DEVNET_METAPLEX_COLLECTION_VERIFY_CAMPAIGN_TEST=1 \
|
||||
KB_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
cargo test -p kb-pipeline-demo-scenarios \
|
||||
optional_devnet_metaplex_collection_verify_campaign_from_env \
|
||||
-- --nocapture
|
||||
```
|
||||
|
||||
`KB_DEVNET_PROFILE` et `KB_DEVNET_CONFIG_PATH` restent optionnels comme pour les autres campagnes Devnet.
|
||||
|
||||
## Sortie à conserver
|
||||
|
||||
Une exécution réussie imprime :
|
||||
|
||||
```text
|
||||
METAPLEX_COLLECTION_VERIFY_FIXTURE ...
|
||||
METAPLEX_COLLECTION_VERIFY_STEP operation=metadata.metaplex_token_metadata.verify ...
|
||||
METAPLEX_COLLECTION_VERIFY_STEP operation=metadata.metaplex_token_metadata.unverify ...
|
||||
METAPLEX_COLLECTION_VERIFY_STATE verified_before=false verified_after_verify=true verified_after_unverify=false size_before=0 size_after_verify=1 size_after_unverify=0
|
||||
METAPLEX_COLLECTION_VERIFY_EVIDENCE verify={...} unverify={...}
|
||||
```
|
||||
|
||||
Les objets de la dernière ligne conservent les mêmes dimensions machine-readable que la campagne `Create -> Mint` : genesis hash, slot/logs de simulation, message hash, fee, signature, statut/slot de confirmation, snapshots, hydratation, Core, replay, matérialisation et idempotence.
|
||||
|
||||
## Promotion de matrice
|
||||
|
||||
`Verify` et `Unverify` sont désormais `confirmed` sur la famille `collection`, avec un bundle `familyEvidence` de seize preuves chacun. Avec `Create` et `Mint`, la matrice compte donc `4` opérations courantes confirmées et `16` opérations `not_run`.
|
||||
|
||||
La prochaine campagne spécialisée est `Print -> Burn`; aucune preuve de cette campagne n’est promue avant une exécution Devnet complète terminée par `ok`.
|
||||
@@ -0,0 +1,268 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_CREATE_MINT_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 12 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — Metaplex `Create -> Mint`
|
||||
|
||||
## Statut
|
||||
|
||||
**Campagne multi-famille `Create -> Mint` fermée : NFT, SFT, fungible, collection et pNFT sont confirmés sur Devnet.**
|
||||
|
||||
Le 7 août 2026, une première campagne NFT a atteint la post-validation de `Create` puis s’est arrêtée sur un diagnostic agrégé. Après `fix-007`, la seconde tentative localise une borne stateful incompatible avec le transport ; `fix-008` l’aligne sur `65536` octets. La troisième tentative franchit alors la lecture stateful, l’hydratation canonique et l’extraction Core : `Create` est réellement confirmée avec la signature `51MXdFFFNY7UTS23cmfCQBfrDKC99uk5Trtw2gkptvEug9aWJ2R1cE9Ta5QHbe4kS1NYgRUSSMmmdbqdhyxxnR4Y` au slot `481882374`, puis `fix-009` corrige le replay executor -> decoder. Les quatrième et cinquième tentatives atteignent ensuite une postcondition SPL qui exigeait à tort que l’autorité opérateur survive à `Create` pour un NFT Master Edition. `fix-011` corrige cette attente vers le PDA Master Edition. La sixième tentative termine enfin la campagne : `Create` est confirmé au slot `481904731` avec une matérialisation, puis `Mint` au slot `481904744` avec une matérialisation et la transition SPL exacte supply/amount `0 -> 1`. `fix-012` promeut donc les deux opérations à `confirmed` sur la famille NFT dans la matrice opérationnelle. La campagne SFT suivante réussit également de bout en bout : `Create` est confirmé au slot `481908028`, `Mint` au slot `481908039`, chaque étape produit une matérialisation, et la supply ainsi que l’amount passent de `0` à `10`. `fix-013` conserve donc `Create` et `Mint` comme seules opérations `confirmed`, désormais observées sur NFT et SFT ; les 18 autres opérations restent `not_run` et fungible/collection/pNFT restent à exécuter. La campagne fungible suivante réussit à son tour : `Create` est confirmé au slot `481910724`, puis `Mint` au slot `481910734`, avec une matérialisation par étape et la transition brute supply/amount `0 -> 1_000_000_000` sur un mint à 9 décimales. `fix-014` ajoute donc `fungible` aux bundles de preuves de `Create` et `Mint`. La campagne collection suivante réussit également de bout en bout : `Create` est confirmé au slot `481911942`, puis `Mint` au slot `481911955`, avec une matérialisation instructionnelle par étape, deux snapshots stateful et la transition supply/amount `0 -> 1`. `fix-015` ajoute `collection` aux bundles de preuves, corrige la régression du test de matrice resté figé sur NFT/SFT et laisse pNFT comme dernière variante `Create -> Mint` à exécuter.
|
||||
|
||||
## Première tentative NFT
|
||||
|
||||
Contrôles locaux avant réseau : `cargo fmt --all`, `cargo check --workspace`, audit workspace et tests de crate réussis ; `cargo clippy --all-targets` ne signalait qu'un warning `cloned_ref_to_slice_refs`, corrigé dans `fix-007`. La campagne réseau a ensuite échoué avec :
|
||||
|
||||
```text
|
||||
metaplex_create_mint_post_execution_incomplete:
|
||||
Metaplex create did not complete canonical replay and materialization
|
||||
```
|
||||
|
||||
Ce message ne permettait pas d'identifier le sous-état fautif. Aucune promotion de matrice n'est autorisée à partir de cette tentative.
|
||||
|
||||
|
||||
## Seconde tentative NFT
|
||||
|
||||
La validation locale de `fix-007` réussit avec `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets`, l'audit workspace, 78 tests unitaires `kb-pipeline-demo-scenarios` et 135 tests desktop. La nouvelle campagne NFT échoue ensuite avec le diagnostic exact :
|
||||
|
||||
```text
|
||||
canonical_inserted=false, core_extracted=false, decode_replayed=false, materialized=false,
|
||||
materialization_rows=0, materialized_snapshots=0,
|
||||
diagnostics=["Metaplex stateful postcondition read failed: configuration error: getAccountInfo complete data limit must be between 1 and 65536 bytes"]
|
||||
```
|
||||
|
||||
L'échec précède donc hydratation, extraction et replay : il provient du contrat de lecture stateful, pas d'une preuve que `Create` aurait échoué on-chain. La constante pipeline historique de `1 MiB` dépassait la borne du transport. `fix-008` remplace cette borne par `kb_onchain_transport::MAX_COMPLETE_ACCOUNT_DATA_BYTES`.
|
||||
|
||||
## Troisième tentative NFT
|
||||
|
||||
La validation locale de `fix-008` réussit avec `cargo check --workspace`, `cargo clippy --all-targets`, 106 tests unitaires `kb-pipeline`, 78 tests unitaires `kb-pipeline-demo-scenarios` et 135 tests desktop. La campagne produit ensuite :
|
||||
|
||||
```text
|
||||
signature=51MXdFFFNY7UTS23cmfCQBfrDKC99uk5Trtw2gkptvEug9aWJ2R1cE9Ta5QHbe4kS1NYgRUSSMmmdbqdhyxxnR4Y
|
||||
slot=481882374
|
||||
canonical_inserted=true
|
||||
core_extracted=true
|
||||
decode_replayed=false
|
||||
materialized=false
|
||||
materialization_rows=0
|
||||
materialized_snapshots=2
|
||||
```
|
||||
|
||||
La transaction `Create` est donc confirmée et les deux snapshots stateful sont disponibles, mais le décodeur instructionnel refuse encore le replay. Le réaudit du contrat local montre que `validate_accounts("create")` imposait historiquement le mint signer et l’update authority signer, alors que l’exécuteur courant expose respectivement `mint_as_signer` et `update_authority_as_signer`. La fixture utilise volontairement `mint_as_signer=false` car le mint est créé dans la transaction de préparation. De plus, authority, payer et update authority partagent le même wallet : leurs flags visibles dans le replay sont des privilèges globaux de transaction et peuvent donc être des sur-ensembles des metas positionnelles. Le même problème aurait touché `Mint`, où token owner, authority et payer partagent également le wallet opérateur.
|
||||
|
||||
`fix-009` remplace pour `Create` et `Mint` l’égalité stricte des flags par des minima de privilèges requis, tout en conservant writable obligatoire sur les comptes optionnels réellement présents. Il ajoute aussi les compteurs détaillés du premier replay au diagnostic. Cette correction doit être validée localement puis la campagne NFT rejouée avant toute promotion de matrice.
|
||||
|
||||
## Frontière de la campagne
|
||||
|
||||
Une invocation couvre une seule famille afin de limiter le nombre de transactions et de rendre les diagnostics attribuables :
|
||||
|
||||
- `nft` ;
|
||||
- `sft` ;
|
||||
- `fungible` ;
|
||||
- `collection` ;
|
||||
- `programmable_nft` ou `pnft`.
|
||||
|
||||
Chaque invocation exécute trois transactions au maximum : préparation native du mint + ATA, `Create`, puis `Mint`.
|
||||
|
||||
## Preuves exigées
|
||||
|
||||
Pour `Create` et `Mint` :
|
||||
|
||||
- simulation réussie liée au message exact ;
|
||||
- signature et confirmation Devnet ;
|
||||
- slot de confirmation ;
|
||||
- `after_state` Metaplex relu avec `minContextSlot >= confirmation_slot` ;
|
||||
- hydratation canonique de la transaction ;
|
||||
- extraction Core ;
|
||||
- replay du décodeur Metaplex ;
|
||||
- matérialisation instructionnelle ;
|
||||
- seconde passe de replay sans nouvel output ;
|
||||
- snapshot metadata et master edition lorsqu’elle est requise ;
|
||||
- token record après `Mint` pour pNFT ;
|
||||
- supply du mint et amount de l’ATA à zéro après `Create` ;
|
||||
- supply du mint et amount de l’ATA exactement égaux à `mint_amount_raw` après `Mint`.
|
||||
|
||||
## Commande initiale — NFT
|
||||
|
||||
```bash
|
||||
KB_DEVNET_METAPLEX_CREATE_MINT_CAMPAIGN_TEST=1 \
|
||||
KB_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \
|
||||
KB_DEVNET_METAPLEX_CREATE_MINT_FAMILY=nft \
|
||||
KB_POSTGRES_TEST_URL='postgresql://…' \
|
||||
cargo test -p kb-pipeline-demo-scenarios \
|
||||
optional_devnet_metaplex_create_mint_campaign_from_env \
|
||||
-- --nocapture
|
||||
```
|
||||
|
||||
`KB_DEVNET_PROFILE` et `KB_DEVNET_CONFIG_PATH` restent disponibles lorsque le profil par défaut n’est pas celui à utiliser.
|
||||
|
||||
## Sortie attendue
|
||||
|
||||
La campagne imprime :
|
||||
|
||||
```text
|
||||
METAPLEX_CREATE_MINT_FIXTURE ...
|
||||
METAPLEX_CREATE_MINT_STEP operation=metadata.metaplex_token_metadata.create ...
|
||||
METAPLEX_CREATE_MINT_STEP operation=metadata.metaplex_token_metadata.mint ...
|
||||
```
|
||||
|
||||
Les cinq chemins NFT, SFT, fungible, collection et pNFT sont désormais qualifiés dans les bundles `familyEvidence` de `Create` et `Mint`. La campagne multi-famille `Create -> Mint` est donc fermée ; les opérations spécialisées suivantes restent qualifiées séparément.
|
||||
|
||||
## Quatrième tentative NFT — mismatch d’autorité initialement mal interprété
|
||||
|
||||
La tentative suivant `fix-009` observe `3RJXuBF6xB3j47EeAHEBbcwvV66rJrkhmJvYNr36jz8x` comme mint authority et freeze authority au lieu de l’opérateur `J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH`. Cette sortie a d’abord été interprétée comme une réutilisation de fixture. `pre.013-delta-fix-010` a donc rendu la préparation fresh-only avec `TemporaryWalletStore::create`, absence on-chain préalable et relectures liées au slot confirmé. Ce durcissement reste souhaitable, mais l’interprétation « stale fixture » était incomplète.
|
||||
|
||||
Le réaudit du chemin exact montre que ce contrôle d’autorité est exécuté seulement après `validate_confirmed_execution("create", ...)`. Le mismatch est donc postérieur à `Create`, et non antérieur à toute instruction Metaplex.
|
||||
|
||||
## Cinquième tentative NFT — pipeline `Create` complet, postcondition SPL incorrecte
|
||||
|
||||
Après `fix-010`, la validation locale confirme 701 tests unitaires `kb-lib`, 106 tests unitaires `kb-pipeline`, 78 tests unitaires de scénarios et 135 tests desktop ; un unique warning `dead_code` subsiste pour `read_token_account`. La campagne fresh-only observe ensuite `KsjdBdmUUM3r8Mw6cQESkXJfxmnPXSmR482963gmsyo` comme mint authority et freeze authority après `Create`, face à l’attente opérateur `J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH`.
|
||||
|
||||
Cette erreur intervient après `validate_confirmed_execution("create", ...)`. Par construction, `Create` a donc déjà franchi simulation, confirmation, hydratation canonique, extraction Core, replay, matérialisation instructionnelle, snapshots stateful et seconde passe idempotente. La campagne ne copiait cependant pas signature + slot dans cette erreur SPL, donc la preuve n’est pas encore exploitable pour promouvoir `Create` dans la matrice.
|
||||
|
||||
Le contrat officiel Token Metadata explique la mutation : lors de la création d’un NFT avec Master Edition, mint authority et freeze authority sont transférées au PDA Edition/Master Edition. `pre.013-delta-fix-011` attend donc ce PDA pour NFT, collection et pNFT, et conserve l’opérateur pour SFT et fungible. Il maintient cette attente après `Mint`, ajoute signature + slot aux erreurs SPL post-confirmation et supprime le helper mort.
|
||||
|
||||
## Sixième tentative NFT — campagne complète confirmée
|
||||
|
||||
Après application de `pre.013-delta-fix-011`, la campagne NFT fresh-only termine les deux étapes et imprime les preuves suivantes :
|
||||
|
||||
```text
|
||||
fixture family=Nft
|
||||
mint=J6pDBZR4nWpM8Mj61fEdwQXH3KLZE8RuKSEsyNofC7bp
|
||||
metadata=7JLnovNwuatxAessRcWCEuKb5jg2H93KVpmyHNzx4euG
|
||||
master_edition=GxUFL8ZfosThA4hUVGPKDT8Zvcc2b2VmdjEiw6WQQYNx
|
||||
token_account=9LBJCDFkJghjVDDeccTe4vjZH3dDNpzsWVXqyTKs81To
|
||||
preparation_signature=2u4sdcFhmoeeYiAe8X761AmCnWyPLHSxYwzwwocLYkfVCan9W5i13ipvK4v3dWRe9E4iJRqBCHHFRh8MwR2o8FLh
|
||||
|
||||
Create:
|
||||
signature=okwkuQFp7yVguYW7GEY9Chv6ochM8TWBEeCGV1gu5Waku9wQvMfrKjkig6JxXBwpKoFTECignzJ8HFf8dLtpsvS
|
||||
slot=481904731
|
||||
materializations=1
|
||||
|
||||
Mint:
|
||||
signature=3hSizfdvezyFpfhqX4Za4wmhdMHhSPAisFvYjFAxpRzYcUXhryxCzc9qpwod3Yjm6GmijXTGjjDUuVksfpBzLwz1
|
||||
slot=481904744
|
||||
materializations=1
|
||||
supply_before=0
|
||||
supply_after=1
|
||||
token_before=0
|
||||
token_after=1
|
||||
```
|
||||
|
||||
Le test ne peut retourner `ok` qu’après simulation réussie, confirmation, hydratation canonique, extraction Core, replay Metaplex, matérialisation instructionnelle, snapshots stateful et seconde passe idempotente pour chacune des deux transactions. La postcondition SPL vérifie en plus que la supply et l’ATA restent à zéro après `Create`, puis deviennent exactement `1` après `Mint`. Cette sortie constitue donc la première preuve complète de `pre.013`.
|
||||
|
||||
`METAPLEX_TOKEN_METADATA_DEVNET_EXECUTION_MATRIX.json` passe en version 2 afin de conserver les familles observées et les preuves réellement qualifiantes. `Create` et `Mint` sont `confirmed` avec `observedFamilies=["nft"]`. Les 18 autres opérations restent `not_run`. Les valeurs de télémétrie qui n’étaient pas imprimées par `fix-011` sont explicitement marquées comme telles au lieu d’être inventées.
|
||||
|
||||
`fix-012` ajoute enfin une ligne `METAPLEX_CREATE_MINT_EVIDENCE` contenant, pour les prochaines familles, le genesis hash, le slot de simulation, le message hash, le fee, la signature, le statut/slot de confirmation et les états du pipeline. Les campagnes fungible, collection et pNFT pourront ainsi être reportées sans nouvelle perte de preuve ; la campagne SFT utilise déjà ce format machine-readable.
|
||||
|
||||
## Campagne SFT confirmée
|
||||
|
||||
Après application de `fix-012`, la campagne SFT termine sans erreur. La fixture fraîche utilise le mint `UhymxTPkMQJW3rzTa5Ff1FMhxgB4dPNRzqZM2ghgwZD`, la metadata PDA `47jQ8Smucco7QjSRa1gzR8pF7RXiugZW69eF2825svjt` et l’ATA opérateur `EF2epsY37WqXrGPuqB4VKCyMGMYcN1wVSkDZ2JNqQuJt`. La préparation native est confirmée par `53w14jG5KDEcDTfCAd2GDKmoJG1pRcVp9a9JZgzQ9DgFyPV8v5iYEGLdfAhS8jmhT8DaVodjDc4AqnrA6H7Fc5XM`.
|
||||
|
||||
### `Create` SFT
|
||||
|
||||
- genesis hash : `EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG` ;
|
||||
- simulation context slot : `481908023` ;
|
||||
- logs de simulation : `12` ;
|
||||
- message hash : `6zv9nc8ARRfKx29LhiwAfzME8a5LSQ6AF4BUYdy1D3WE` ;
|
||||
- fee : `5000` lamports ;
|
||||
- signature : `2s81m6k9HYDLmCy7oXKaqBvtb62iyX4bJmij2f2VjHuoRQBBA6YqRuAsjyaM7JCaJXasbU3WCfiko91cZcDLLdxV` ;
|
||||
- statut : `Confirmed` ;
|
||||
- slot : `481908028` ;
|
||||
- matérialisation instructionnelle : `1` ;
|
||||
- snapshots : `0 -> 1` ;
|
||||
- hydratation canonique, extraction Core, replay et seconde passe idempotente : réussis.
|
||||
|
||||
### `Mint` SFT
|
||||
|
||||
- simulation context slot : `481908034` ;
|
||||
- logs de simulation : `7` ;
|
||||
- message hash : `65VBkcnKtZdxLZgwTu669Voz57PqYJbQ8iAUe5jWUKJ6` ;
|
||||
- fee : `5000` lamports ;
|
||||
- signature : `4pAemhEJewuLAeqLkqwzPvVmsCRbkyLiN6uAUWBA5RMsofaEDt5Xr8YGiBEvLWQM7Qvxnd81mmCHpandmfmWuyAm` ;
|
||||
- statut : `Confirmed` ;
|
||||
- slot : `481908039` ;
|
||||
- matérialisation instructionnelle : `1` ;
|
||||
- snapshots : `1 -> 1` ;
|
||||
- supply : `0 -> 10` ;
|
||||
- amount ATA : `0 -> 10` ;
|
||||
- hydratation canonique, extraction Core, replay et seconde passe idempotente : réussis.
|
||||
|
||||
La validation locale qui suit est propre : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets`, audit workspace, 79 tests unitaires `kb-pipeline-demo-scenarios` et 135 tests desktop.
|
||||
|
||||
## Matrice v3 — preuves par famille
|
||||
|
||||
La campagne SFT montre qu’une simple liste `evidence` par opération n’est plus suffisante : NFT et SFT ont leurs propres signatures, slots, message hashes et états. `fix-013` passe donc la matrice en version 3 et ajoute un bundle `familyEvidence` complet pour chaque entrée de `observedFamilies`. Une opération `confirmed` est refusée si une famille observée n’a pas exactement son bundle de preuves de simulation et de soumission. L’ancien champ `evidence` reste réservé aux états non familiaux comme `unavailable`.
|
||||
|
||||
À ce stade, `Create` et `Mint` sont `confirmed` sur `nft` et `sft`. Les familles `fungible`, `collection` et `programmable_nft` restent à qualifier.
|
||||
|
||||
## Consolidation `pre.013-delta-fix-014` — qualification fungible
|
||||
|
||||
La campagne fungible `Create -> Mint` est confirmée de bout en bout sur Devnet. La fixture fraîche prépare le mint `8jBAXGfU95qVRSVeW67NxmuR6RaQxcRrqrDWF1kKn8u7` à 9 décimales et l’ATA opérateur `HHY3zXSjcmH4dkzNzE83NWrknGqWX4DFSPj8UeSNMLYT` avec supply et amount nuls ; la préparation native est confirmée par `39DyQvbnbgKaQxb36f4S1rjEuhH385jNAFe8CWH8j3dCgwSjgdkQMPJv2bzGLFLPet6GuC352QNGQLZ5vGhRT7wg`.
|
||||
|
||||
`Create` est simulé au slot `481910719` avec 12 logs, message hash `Ddpy6aTtWL4X1zvxmDxrU8W4uwBZMzLTs3AM5am5Wj9g` et fee `5000`, puis confirmé avec la signature `3J1Ebq9DVkupVFQmqQ84QaNeztwMuRwbtXj7CiyUWwZPj4KCAJpKLAbPvpYss5cdiY33foXXScWWZE98H7tvbZed` au slot `481910724`. La metadata PDA `J6W5PF4SESMvCoTw9H4uAoUvn6tZBWjiG9QWN3CZLyRE` est validée sans Master Edition, comme attendu pour cette famille. Hydratation canonique, extraction Core, replay, matérialisation et seconde passe idempotente sont propres.
|
||||
|
||||
`Mint` est simulé au slot `481910730` avec 7 logs, message hash `8SqHD12gMuaLXHcYLDx72eA2GVokTDDNnnxnuJPfaACh` et fee `5000`, puis confirmé avec `2GKiY9foLmioYtUHttA2D6nCSmsftRL1JDKxENHmCnb5w38FGSxwBiRNMfF7mH8htv3G45irmge6DVtr6VN359j2` au slot `481910734`. Une matérialisation instructionnelle et un snapshot stateful sont observés ; supply et amount ATA passent exactement de `0` à `1_000_000_000`, et le second replay est idempotent.
|
||||
|
||||
La matrice v3 conserve donc `Create` et `Mint` comme seules opérations `confirmed`, avec trois bundles familiaux indépendants `nft`, `sft` et `fungible`. Les 18 autres opérations restent `not_run`; les variantes `collection` et `programmable_nft` restent à exécuter avant les fixtures spécialisées.
|
||||
## Consolidation `pre.013-delta-fix-015` — qualification collection
|
||||
|
||||
La campagne collection `Create -> Mint` est confirmée de bout en bout sur Devnet. La fixture fraîche prépare le mint `AhUDoidUMXy1aqMDQWBGTbdA8SFurXSfU665NbdyMoMZ`, la metadata PDA `D2BFXXDC3p9uy4YEaoRiPq65cu6XrSsVjZccJrxzqYFm`, la Master Edition PDA `HmqeFdeVPmetDRiscWnSfQT7QpJ7cQs4GbJJUfH6Jou8` et l'ATA opérateur `B12BbbZoeDjbJnTn8vods5ZSc9tPiUPuny3yjNV6Vj2b`. La préparation native est confirmée par `4MMxFf1c9crksnr1npyWp4u88TCWgABitS7HQcKbmejfhiBDL6Sacaum8UMUoj3iNdrp61abBoVWjhDRKw6iGK5J`.
|
||||
|
||||
`Create` est simulé au slot `481911937` avec 27 logs, message hash `GxWcLJaiA2zTMQBp6NuKCgHbnC1taCyAjuDH5n2375Ci` et fee `5000`, puis confirmé avec la signature `3jpxyWuxQVyBtbvqDnV1oahLyxCXMw1zQQZBGCKgQSfn2TNcBaUdfvJ6kFa8zL3XJLMpM6tahQTnu8AUsTkYc3Dy` au slot `481911942`. Une matérialisation instructionnelle et deux snapshots stateful sont observés ; metadata et Master Edition sont validées, la supply et l'amount ATA restent à zéro, et le second replay est idempotent.
|
||||
|
||||
`Mint` est simulé au slot `481911950` avec 7 logs, message hash `JCx6XUnQ7e7Veh4K8xtN8KrGFHafdQMRxAK9jej7nNZs` et fee `5000`, puis confirmé avec la signature `4WEciqZA5vmtrh1ZeSbqcgVnezUy7qT6B6utySfkg7dbAmhH6SMwwY862zGbzPW3nBVFzMjFVGPEhnTt6TcAiEe2` au slot `481911955`. Une matérialisation instructionnelle et deux snapshots stateful sont observés ; supply et amount ATA passent exactement de `0` à `1`, et le second replay est idempotent.
|
||||
|
||||
La matrice v3 conserve donc `Create` et `Mint` comme seules opérations `confirmed`, avec quatre bundles familiaux indépendants `nft`, `sft`, `fungible` et `collection`. Les 18 autres opérations restent `not_run`; seule la variante `programmable_nft` reste à exécuter avant les fixtures spécialisées.
|
||||
|
||||
## Première tentative pNFT — `Mint` confirmé, postcondition SPL trop stricte
|
||||
|
||||
La campagne `programmable_nft` confirme `Mint` avec la signature `Gkg5A6ehuSzkU1N318kYoRHm5RdxV2yBRpaK9Gwyt6vN1ToYwAS6Vzpk2rQYWS9NJz1rnUpypLCn7DqWFzQzxFc` au slot `481913765`. Après confirmation, le compte token classique relu contient exactement le mint `Ecn3v1u21DKNhpFX1tqKvieVn5CkhMMukxWgoN59Hf2E`, l’owner `J12WA6c42oqpWkLu1dFa4pCegkQJPpxabwc3RRCMSUuH`, `amount=1` et `state=2`. Le test échoue uniquement parce que la postcondition commune attendait encore `state=1`.
|
||||
|
||||
`state=2` est `Frozen` dans le contrat SPL Token et correspond au comportement attendu d’un pNFT : les opérations passent par Token Metadata et son Token Record au lieu de permettre les mutations SPL directes. `pre.013-delta-fix-016` exige donc `Initialized(1)` avant `Mint`, `Frozen(2)` après `Mint` uniquement pour pNFT, et conserve `Initialized(1)` pour NFT/SFT/fungible/collection.
|
||||
|
||||
La sortie machine-readable ajoute `tokenAccountStateBeforeMint` et `tokenAccountStateAfterMint`. Le rerun pNFT doit retourner `ok`, prouver la présence du Token Record et produire `1 -> 2` avant ajout du bundle `programmable_nft` à la matrice v3.
|
||||
|
||||
|
||||
## Deuxième tentative pNFT — campagne complète confirmée
|
||||
|
||||
Après `pre.013-delta-fix-016`, le rerun `programmable_nft` termine sans erreur et ferme la qualification des cinq familles. La fixture fraîche utilise :
|
||||
|
||||
- mint : `4tu87iUVr8hyGGRTVkYMGww7HLMZRuLLFn7MLXxzkmXA` ;
|
||||
- metadata : `Hvjpj78kPgWb14xoEuLPzqckYbjPRJaBxVRNDNX3qMBL` ;
|
||||
- Master Edition : `cVJELihnvgWeRmwQ3HHJAoXyrTPtZjpMZjwWMsJPzAx` ;
|
||||
- ATA opérateur : `BP8AAcqfAM4bKAZxnifKpTQM29QKXmkiDBoAYLT69ahS` ;
|
||||
- Token Record : `Ejp6LTUMxXTFPDDM7iZGsutnCUriQBnArofikMsc97op` ;
|
||||
- préparation native : `gHNtnEwTdw8Uyb32Fjkg9rdJ6ET9mg6Svnc1vXSZjBLYk7upcJUUBXu7VbtvXqYttGbvKQgnGEC2aLDkosvDjxw`.
|
||||
|
||||
### `Create` pNFT
|
||||
|
||||
- simulation context slot : `481921975` ;
|
||||
- logs de simulation : `27` ;
|
||||
- message hash : `4v1opnPoD8YRYr5ti5dFXjSBGbZ6gWCLfg4DwCErwtee` ;
|
||||
- fee : `5000` lamports ;
|
||||
- signature : `515t45UC5KPpGocEHtAuDf2qEqzeJt9TUrbUD1zhAFaXRHnefcu3MHAZTc1JqjSuscDgDSYFVTBX3AKJLk1G8fsN` ;
|
||||
- statut : `Confirmed` ;
|
||||
- slot : `481921980` ;
|
||||
- matérialisation instructionnelle : `1` ;
|
||||
- snapshots : `0 -> 2` ;
|
||||
- hydratation canonique, extraction Core, replay et seconde passe idempotente : réussis.
|
||||
|
||||
### `Mint` pNFT
|
||||
|
||||
- simulation context slot : `481921989` ;
|
||||
- logs de simulation : `19` ;
|
||||
- message hash : `86raN9VfGwk1BymXf2wfkeQmMxnyPTyx49s3gxYoRH6b` ;
|
||||
- fee : `5000` lamports ;
|
||||
- signature : `2kYgd9zpY5xjWSfpoDzvLkjy6KhKFNPsXWfxK4jFrnD15z2BaVUgaxKNpJNiJXP275YgR8LAEzn32Lc7qPq9GdH8` ;
|
||||
- statut : `Confirmed` ;
|
||||
- slot : `481921994` ;
|
||||
- matérialisation instructionnelle : `1` ;
|
||||
- snapshots : `2 -> 3` ;
|
||||
- supply : `0 -> 1` ;
|
||||
- amount ATA : `0 -> 1` ;
|
||||
- état ATA : `Initialized(1) -> Frozen(2)` ;
|
||||
- Token Record absent avant `Mint`, présent et validé après `Mint` ;
|
||||
- hydratation canonique, extraction Core, replay et seconde passe idempotente : réussis.
|
||||
|
||||
`pre.013-delta-fix-017` ajoute donc `programmable_nft` comme cinquième bundle `familyEvidence` de `Create` et `Mint`. Les deux opérations restent les seules entrées `confirmed`, mais leur qualification multi-famille est désormais complète `5/5`. Les 18 autres opérations restent `not_run`.
|
||||
@@ -0,0 +1,81 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_ESCROW_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — Token Owned Escrow
|
||||
|
||||
## Statut
|
||||
|
||||
**Qualifié sur Devnet — les trois opérations escrow sont `confirmed`.**
|
||||
|
||||
Le rerun du 8 août 2026 après `pre.013-delta-fix-032` termine entièrement la campagne et les checks/tests locaux annoncés restent sans régression. La correction du compte terminal `authority=None` permet aux builders escrow de produire les formes officielles sans faux signer.
|
||||
|
||||
La matrice passe de 11 `confirmed`, 1 `unavailable`, 8 `not_run` à **14 `confirmed`, 1 `unavailable`, 5 `not_run`**.
|
||||
|
||||
## Fixture qualifiée
|
||||
|
||||
```text
|
||||
parent_mint=7Sib8Ugxi3urHBRAWCqdGGXD24em7QN3Vdp69f2RJNRM
|
||||
parent_metadata=8rg9f1wt9wbqNsVBwv4PPiUSLFgsLrLPSzs6cddPhtyo
|
||||
parent_edition=5j7kV9eFuFzXaAzqF6Gb6pG2sLG9iAsWq2YSMUn1kUGJ
|
||||
parent_token=EEGchyQPrimCWs2DyQq7BDRYvrsVs5ctMa4sTjZu6erk
|
||||
attribute_mint=C5e4ecUGoo1y94uXDNVTDfmiMGjGtir27YtLxnv8kfKU
|
||||
attribute_token=2kPTg6P48zgDbefZKWMSpmVjXXv2aH61ah2DRZwpsne1
|
||||
escrow=274ZJGXGVZ1ra2tkUvewtB21kNxSmALaHdHg8K35tVof
|
||||
escrow_attribute_token=3bnvR635UFZm52DA38VXXjZzQ547f4kWzp47d9VBV6q6
|
||||
```
|
||||
|
||||
Préparation SPL confirmée :
|
||||
|
||||
- ATA attribut escrow : `51K4hDTvyBCTZ1MNXhU7WZzgZiRbQyZbdbHAdVs1WfADB7PMhVszCeKqxGUvx3vEatXGJs2r15tHZ7T1ubxGoU1v`, slot `482147222` ;
|
||||
- dépôt d’une unité brute : `37B86chCnjWXPEbjExHry8XELn7UC8VbuTQnYe4KeKeYDikwXHZF4dPhNS27NSGXpzCJZjzUWyuJeRccz6DJWV2E`, slot `482147239`.
|
||||
|
||||
## Preuves Metaplex
|
||||
|
||||
### `CreateEscrowAccount`
|
||||
|
||||
- simulation : slot `482147200`, 13 logs, succès ;
|
||||
- message hash : `E1V2rLgFqjxpyBVu1dkp32VNkZ3V3iTdBTPv4j8mmwSj` ;
|
||||
- frais : `5000` lamports ;
|
||||
- signature : `Yy7NEvme3obuiGRBbBjpuk3dv4aPMPBQ2ns7KmbY4K5EtyAimaaM8qN5SQzEqr9SPbRu1VDCFkNpAjnRGW4JYuj` ;
|
||||
- confirmation : `Confirmed`, slot `482147206` ;
|
||||
- hydratation canonique, extraction Core, decode replay, matérialisation et idempotence : réussies ;
|
||||
- matérialisation : 1 ligne instructionnelle et 2 snapshots.
|
||||
|
||||
### `TransferOutOfEscrow`
|
||||
|
||||
- simulation : slot `482147246`, 10 logs, succès ;
|
||||
- message hash : `FnDRr8JPNPzyECSYjzgN1ABuhyS88iQNxcrS7M4L21WD` ;
|
||||
- frais : `5000` lamports ;
|
||||
- signature : `4yJcSvnZJqihfZTE7nxN3o7H7zBCacpdZdwNQ1ueWQMmQZxR7KRb99C7rfrXX2AyezfSStMdqqTYL3oEWgwRhFi3` ;
|
||||
- confirmation : `Confirmed`, slot `482147252` ;
|
||||
- hydratation canonique, extraction Core, decode replay, matérialisation et idempotence : réussies ;
|
||||
- matérialisation : 1 ligne instructionnelle et 2 snapshots.
|
||||
|
||||
### `CloseEscrowAccount`
|
||||
|
||||
- simulation : slot `482147262`, 4 logs, succès ;
|
||||
- message hash : `8y6uvagE6Z1k2adnR7Vrk8MDQDPCPx1jY9fQ9TMyb95L` ;
|
||||
- frais : `5000` lamports ;
|
||||
- signature : `3GuwmGf9JEK7rHE3tudnxskvruyML9AGVXQimxii3uHdXqNEF4ScpmDXwhU3y2eaUnTbBPPhVkhRfLwJr9jbGJBo` ;
|
||||
- confirmation : `Confirmed`, slot `482147267` ;
|
||||
- hydratation canonique, extraction Core, decode replay, matérialisation et idempotence : réussies ;
|
||||
- matérialisation : 1 ligne instructionnelle et 1 snapshot.
|
||||
|
||||
## Postconditions stateful
|
||||
|
||||
```text
|
||||
deposit_amount=1
|
||||
operator_before=1000000000
|
||||
operator_after_deposit=999999999
|
||||
escrow_after_deposit=1
|
||||
operator_after_transfer_out=1000000000
|
||||
escrow_attribute_closed=true
|
||||
escrow_closed=true
|
||||
parent_amount_after_close=1
|
||||
```
|
||||
|
||||
Le Token Owned Escrow est donc créé avec son parent NFT, reçoit indirectement l’unité SPL via son ATA, restitue exactement cette unité, ferme l’ATA devenue vide, puis se ferme lui-même sans modifier la possession du NFT parent.
|
||||
|
||||
## Décision
|
||||
|
||||
`CreateEscrowAccount`, `TransferOutOfEscrow` et `CloseEscrowAccount` sont promus à `confirmed` pour la famille `nft`. La campagne escrow n’est plus un blocant de `pre.013`.
|
||||
@@ -0,0 +1,74 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_FINAL_VALIDATION_REPORT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Validation finale `0.4.8-pre.013` — Metaplex Token Metadata
|
||||
|
||||
## Statut
|
||||
|
||||
**Terminée et validée.**
|
||||
|
||||
La prerelease ferme le réaudit fonctionnel et réseau de Metaplex Token Metadata. La matrice canonique `test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_DEVNET_EXECUTION_MATRIX.json` contient les 20 opérations courantes et ne possède plus aucune ligne `not_run`.
|
||||
|
||||
| Statut | Nombre |
|
||||
|---------------|-------:|
|
||||
| `confirmed` | 15 |
|
||||
| `unavailable` | 5 |
|
||||
| `not_run` | 0 |
|
||||
| total | 20 |
|
||||
|
||||
## Opérations `confirmed`
|
||||
|
||||
Les opérations suivantes disposent de preuves Devnet qualifiantes avec simulation, confirmation, hydratation canonique, extraction Core, replay, matérialisation et idempotence selon leur parcours :
|
||||
|
||||
- `Create` et `Mint` sur NFT, SFT, fungible, collection et pNFT ;
|
||||
- `Verify` et `Unverify` sur collection parent-membre ;
|
||||
- `Print` et `Burn` sur NFT imprimable ;
|
||||
- `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer` sur lifecycle pNFT ;
|
||||
- `CreateEscrowAccount`, `TransferOutOfEscrow` et `CloseEscrowAccount` sur Token Owned Escrow ;
|
||||
- `Update` sur NFT, avec `primary_sale_happened: false -> true`.
|
||||
|
||||
Les signatures, slots, message hashes et postconditions détaillés restent enregistrés dans la matrice et les rapports de campagne spécialisés de `docs/validation/`.
|
||||
|
||||
## Opérations `unavailable`
|
||||
|
||||
Les cinq statuts négatifs sont issus de preuves runtime réelles et ne sont pas des suppositions :
|
||||
|
||||
- `Use` : simulation Devnet rejetée par `InvalidInstructionData`; aucune soumission ;
|
||||
- `Resize` : `Custom(201)` / compte déjà redimensionné sur le layout courant ; une réussite positive exige un état legacy sous-dimensionné contrôlé ;
|
||||
- `Migrate` : `Custom(75)` / instruction retirée ;
|
||||
- `Collect` : `Custom(7)` avec l'opérateur contrôlé ; la surface exige l'autorité Metaplex fixe de collecte ;
|
||||
- `CloseAccounts` : `Custom(188)` avec l'opérateur contrôlé ; la surface exige l'autorité Metaplex ownerless-close fixe.
|
||||
|
||||
Aucune simulation négative n'est soumise. Aucun actif tiers ni aucune autorité réservée n'est imité pour fabriquer une validation positive.
|
||||
|
||||
## Validation locale finale
|
||||
|
||||
Après `pre.013-delta-fix-036`, l'état réel du workspace fourni par l'opérateur a produit :
|
||||
|
||||
```text
|
||||
cargo fmt --all OK
|
||||
cargo check --workspace OK
|
||||
cargo clippy --all-targets OK, aucun warning
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
General Rust rule audit clean
|
||||
Rust export completeness audit 0 candidate(s)
|
||||
Khadhroony workspace rule audit clean
|
||||
cargo test -p kb-pipeline-demo-scenarios
|
||||
unitaires 105 passed
|
||||
CLI 1 passed
|
||||
API externe 1 passed
|
||||
```
|
||||
|
||||
La campagne maintenance opt-in a également terminé `ok` avec `Update` confirmé et les quatre probes négatifs aux codes attendus.
|
||||
|
||||
## Frontières conservées
|
||||
|
||||
- Metaplex Token Metadata reste distinct de Token-2022 Token Metadata et de Solana Program Metadata ;
|
||||
- les opérations historiques, remplacées ou dépréciées ne sont pas réinterprétées comme opérations courantes ;
|
||||
- les statuts réseau ne sont jamais promus depuis une preuve synthétique ;
|
||||
- le fetch off-chain reste hors périmètre de `0.4.8` ;
|
||||
- les campagnes Devnet restent réexécutables pour détecter une régression ou un changement de runtime, mais ne constituent plus des tâches ouvertes de `pre.013`.
|
||||
|
||||
## Passage à `0.4.8-pre.014`
|
||||
|
||||
Le prochain développement non-fix peut ouvrir `0.4.8-pre.014`, consacré à la finalisation desktop metadata. Il doit réutiliser les contrats et statuts désormais fermés de `pre.013` sans rouvrir une campagne Metaplex fondamentale sauf régression observée.
|
||||
@@ -0,0 +1,91 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_MAINTENANCE_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — maintenance Metaplex finale
|
||||
|
||||
## Statut
|
||||
|
||||
**Qualifiée — aucune opération courante ne reste `not_run`.**
|
||||
|
||||
Le rerun `fix-035` ferme les cinq dernières lignes de maintenance. La matrice Devnet Metaplex Token Metadata contient désormais **15 opérations `confirmed`, 5 `unavailable` et 0 `not_run`**.
|
||||
|
||||
## Fixture qualifiante
|
||||
|
||||
- mint NFT : `66RQj9bF5MhzE7ZBwxvxpPTj9yaGMLmBAbTmysjrsDix` ;
|
||||
- Metadata : `9e3wJgmRQnZGogM5c8yPSB5kNtg9EYYaCfqjaMB1vzXn` ;
|
||||
- Master Edition : `3sCz28QcwqwrN5h5siJ617JbDCk7PwSDBUWgcpgXPTaM` ;
|
||||
- token account opérateur : `12aMBDfAUvoPXA6qKY7p5nHb6Tck63TUSxZ4zKoCjnuw`.
|
||||
|
||||
## `Update` — `confirmed`
|
||||
|
||||
L'opération courante `metadata.metaplex_token_metadata.update` est exécutée sur le NFT frais et prouve la transition bornée `primary_sale_happened: false -> true`.
|
||||
|
||||
- simulation : succès au slot `482162408`, 5 logs ;
|
||||
- message hash : `EakKCkQNhGyGivUcaeuSJx5F9PFVSpJY4JBsDFKNvRo8` ;
|
||||
- fee : `5000` lamports ;
|
||||
- signature : `4hxxBnsCA1JBJPNHgXRDeq1W7rXbFj2yySscWJKxU1VTMDcPzPfoWy7QzANHSDsbQfxxcwuAQNDLqEukgsug6qoZ` ;
|
||||
- confirmation : `Confirmed`, slot `482162413` ;
|
||||
- hydratation canonique, extraction Core, replay et matérialisation : réussis ;
|
||||
- matérialisation : 1 instruction et 1 snapshot ;
|
||||
- seconde passe idempotente : propre.
|
||||
|
||||
`Update` passe donc à `confirmed` sur la famille `nft`.
|
||||
|
||||
## `Resize` — `unavailable`
|
||||
|
||||
La simulation au slot `482162417` atteint `IX: Resize`, consomme `14643` CU et retourne :
|
||||
|
||||
```text
|
||||
InstructionError[0] = Custom(201)
|
||||
Program log: Account has already been resized
|
||||
```
|
||||
|
||||
Les comptes créés par l'API actuelle sont déjà au layout cible. Une confirmation positive demanderait un compte legacy sous-dimensionné contrôlé ; la campagne ne fabrique pas artificiellement un compte program-owned et ne revendique pas un actif tiers. Aucune transaction `Resize` n'est soumise.
|
||||
|
||||
## `Migrate` — `unavailable`
|
||||
|
||||
La simulation au slot `482162422` consomme `7614` CU et retourne `Custom(75)` :
|
||||
|
||||
```text
|
||||
This instruction was deprecated in a previous release and is now removed
|
||||
```
|
||||
|
||||
L'opération est retirée du runtime courant. Aucune transaction n'est soumise.
|
||||
|
||||
## `Collect` — `unavailable`
|
||||
|
||||
La simulation au slot `482162426` consomme `2286` CU et retourne `Custom(7)` / `UpdateAuthorityIncorrect` :
|
||||
|
||||
```text
|
||||
Update Authority given does not match
|
||||
```
|
||||
|
||||
La surface courante est réservée à l'autorité de collecte Metaplex fixe. Le scénario contrôlé n'essaie pas de l'usurper et ne soumet aucune transaction.
|
||||
|
||||
## `CloseAccounts` — `unavailable`
|
||||
|
||||
La simulation au slot `482162430` consomme `4625` CU et retourne `Custom(188)` / `InvalidCloseAuthority` :
|
||||
|
||||
```text
|
||||
IX: Close Accounts
|
||||
The close authority needs to be revoked by the Utility Delegate
|
||||
```
|
||||
|
||||
La surface courante exige l'autorité ownerless-close fixe. Le scénario ne l'usurpe pas et ne soumet aucune transaction.
|
||||
|
||||
## Validation locale observée
|
||||
|
||||
Le même état de travail a produit :
|
||||
|
||||
- `cargo fmt --all` : OK ;
|
||||
- `cargo check --workspace` : OK ;
|
||||
- `cargo clippy --all-targets` : OK, aucun warning ;
|
||||
- `python3 scripts/audit_rust_workspace_rules.py` : `General Rust rule audit: clean`, `Rust export completeness audit: 0 candidate(s)`, `Khadhroony workspace rule audit: clean` ;
|
||||
- `cargo test -p kb-pipeline-demo-scenarios` : 105 unitaires, 1 CLI et 1 test API externe, tous réussis ;
|
||||
- campagne maintenance opt-in : réussie intégralement.
|
||||
|
||||
## Conclusion
|
||||
|
||||
La matrice des 20 opérations courantes est fermée : **15 `confirmed`, 5 `unavailable`, 0 `not_run`**. Les statuts `unavailable` restent explicitement qualifiés par leur cause : divergence runtime pour `Use`, besoin d'un état legacy contrôlé pour `Resize`, retrait runtime pour `Migrate`, et autorités Metaplex réservées pour `Collect` et `CloseAccounts`.
|
||||
|
||||
La consolidation documentaire et de conformité de `0.4.8-pre.013` est désormais effectuée. Le prochain développement non-fix peut ouvrir `0.4.8-pre.014` pour la finalisation desktop metadata ; aucune campagne Metaplex fondamentale supplémentaire n'est requise par l'état courant.
|
||||
@@ -0,0 +1,201 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_PNFT_LIFECYCLE_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 8 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — cycle pNFT delegates
|
||||
|
||||
## Statut
|
||||
|
||||
**Qualifié sur Devnet — le rerun `fix-028` termine les six exécutions, toutes les postconditions stateful et le bundle pipeline/idempotence. `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer` sont promus par `pre.013-delta-fix-029`.**
|
||||
|
||||
La validation locale de `fix-023` n'avait exécuté aucune transaction pNFT : `cargo check --workspace` passait, mais `cargo clippy --all-targets` et `cargo test -p kb-pipeline-demo-scenarios` échouaient pendant la compilation du bloc `#[cfg(test)]`. `fix-024` a corrigé ce harness sans modifier le graphe.
|
||||
|
||||
Le premier rerun Devnet de `fix-024` atteint ensuite une vraie transaction `Create` pNFT :
|
||||
|
||||
- signature : `PNkQAnykFoHVsREkhDbTW9eccyJoQvQbRZ2MJ4FsFRXyXint9fj6FT9PW45W5R3yi9HJ2aiSZXssH6B6pmyCcEz` ;
|
||||
- slot confirmé : `482096529` ;
|
||||
- mint frais : `DtBji9AYCqLRB48LFEFir427Kwy4Ehie6Pc8S87aQ6Rb` ;
|
||||
- Master Edition : `HmCuW752ukBChVTbXtKJphUwJTJDdSycrgs6SrU2nT4B`.
|
||||
|
||||
La transaction est confirmée mais le bundle de post-validation s'arrête avant hydratation/replay/matérialisation complets, sur `MetaplexTokenMetadataAccountKind::Edition`: le décodeur rejette la Master Edition compacte pNFT avec `metadata account layout is invalid`.
|
||||
|
||||
Le diagnostic officiel est exact : `MAX_MASTER_EDITION_LEN=20`, les 18 premiers octets portent la sérialisation Borsh maximale, l'avant-dernier octet est réservé au fee flag et le dernier au `TokenStandard`. `Create` écrit explicitement `ProgrammableNonFungible` dans ce dernier byte pour une pNFT. `fix-021` avait à tort soumis ces deux octets au contrôle générique de padding nul. `fix-025` sépare donc le padding réservé du trailer sémantique et valide ce dernier de manière bornée.
|
||||
|
||||
Le rerun `fix-025` valide ensuite localement l’ensemble du workspace (`cargo check`, Clippy, audit, `kb-lib` 705/705, `kb-pipeline` 107/107, scénarios 96/96 et desktop 135/135) et franchit complètement `Create -> Mint`. La première opération lifecycle est alors soumise et confirmée :
|
||||
|
||||
- opération : `Delegate(StakingV1)` ;
|
||||
- signature : `4S2uDrVU6ci59eKeGtGeXoUCbsUr4WxzSa7uYhmBxLtjKfNwpBkZRqm59V9cfbD99oE9AugMYppiQqnMbV2Hy1p7` ;
|
||||
- slot confirmé : `482106293` ;
|
||||
- hydratation canonique : réussie ;
|
||||
- extraction Core : réussie ;
|
||||
- replay : `failed_inputs=1`, aucune observation décodée ;
|
||||
- matérialisation : non atteinte.
|
||||
|
||||
Le défaut est la même frontière conceptuelle que celle déjà corrigée pour `Create`, `Mint`, `Print`, `Verify` et `Unverify` : les flags du Core replay sont les privilèges globaux du message, pas une copie stricte des `AccountMeta` de l’instruction. Dans cette campagne, l’autorité pNFT est aussi le fee payer ; la position `Delegate.authority`, officiellement signer readonly, apparaît donc signer+writable dans le replay. `fix-026` valide uniquement les minima obligatoires et conserve writable obligatoire pour les comptes optionnels réellement mutés.
|
||||
|
||||
Le même correctif est appliqué préventivement à `Lock/Unlock` : leur `token_owner` est le wallet opérateur et donc également le fee payer, ce qui lui donne légitimement des privilèges globaux signer+writable alors que l’AccountMeta positionnel est readonly.
|
||||
|
||||
Le rerun `fix-026` valide ensuite entièrement la base locale :
|
||||
|
||||
```text
|
||||
cargo fmt --all : ok
|
||||
cargo check --workspace : ok
|
||||
cargo clippy --all-targets : ok
|
||||
audit_rust_workspace_rules.py : clean
|
||||
kb-lib : 706/706
|
||||
kb-pipeline : 107/107 + 2 API externes
|
||||
kb-pipeline-demo-scenarios : 96/96 + CLI + API externe
|
||||
kb-app-demo-desktop : 135/135
|
||||
```
|
||||
|
||||
La campagne franchit alors `Delegate(StakingV1)` beaucoup plus loin que lors du run précédent. `validate_confirmed_execution("delegate-staking", ...)` réussit, ce qui prouve pour cette nouvelle transaction : simulation réussie, confirmation avec slot, hydratation canonique, extraction Core, replay décodé, matérialisation, snapshots stateful et seconde passe idempotente propre. La sortie échoue seulement ensuite sur la postcondition du TokenRecord :
|
||||
|
||||
```text
|
||||
expected state=Unlocked
|
||||
expected delegate=Some("CPZBygeMzo6WVXiHoc4xarkFP3xo92hD1tuDZU4SCG5t")
|
||||
expected role=Some("Staking")
|
||||
observed state=Some("Unlocked")
|
||||
observed delegate=None
|
||||
observed role=Some("Staking")
|
||||
```
|
||||
|
||||
La nouvelle signature et son slot ne sont pas imprimés avant cette erreur terminale ; ils ne sont donc pas inventés ni utilisés pour une promotion. La signature `4S2uDr...` au slot `482106293` reste la preuve partielle du run `fix-025`, pas celle du rerun `fix-026`.
|
||||
|
||||
Le programme Metaplex courant écrit pourtant `token_record.delegate = Some(delegate)` et `token_record.delegate_role = Some(role)` ensemble pour les token delegates pNFT. Le défaut est local à notre projection : `DcMetadataMtmTokenRecordAccountSnapshot` calcule déjà `delegate` et `locked_transfer` comme chaînes base58, mais son `payload_json` était obtenu indépendamment par `serde_json::to_value(&mpl_token_metadata::accounts::TokenRecord)`. La couche stateful republie ce JSON brut ; la campagne cherche ensuite `payload_json["delegate"].as_str()`. Cette dépendance à la représentation Serde interne de `Pubkey` est incorrecte.
|
||||
|
||||
`fix-027` construit donc explicitement le JSON canonique du TokenRecord à partir des champs normalisés : `state`, `delegate`, `delegate_role`, `locked_transfer`, `rule_set_revision` et `bump`. Les adresses optionnelles deviennent des chaînes base58 ou `null`. La régression `token_record_decodes_programmable_state_delegate_and_exact_pda` vérifie désormais aussi les valeurs JSON `delegate`, `delegate_role` et `locked_transfer`. La postcondition lifecycle reste stricte : elle exige toujours l’adresse exacte du delegate et ne transforme pas cet incident de projection en tolérance fonctionnelle.
|
||||
|
||||
À l’issue du rerun `fix-026`, la campagne cible toujours cinq opérations courantes `not_run` : `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer`. `Delegate(StakingV1)` possède alors une preuve de confirmation partielle mais ne peut pas être promu avant un bundle final imprimé ; les quatre autres opérations ne sont pas encore atteintes à ce stade historique.
|
||||
|
||||
Le rerun `fix-027` franchit ensuite la projection TokenRecord corrigée et poursuit toute la campagne jusqu’au `Transfer`. L’erreur terminale est désormais :
|
||||
|
||||
```text
|
||||
source pNFT token account is invalid after Transfer:
|
||||
expected amount=0 and state=Frozen;
|
||||
observed amount=0 and state=1 (Initialized)
|
||||
```
|
||||
|
||||
Cette erreur survient après `validate_confirmed_execution("transfer", ...)` et après validation du TokenRecord destination `Unlocked` sans delegate. Elle prouve donc que les étapes `Delegate(Staking)`, `Lock`, `Unlock`, `Revoke(Staking)`, `Delegate(Transfer)` et `Transfer` ont toutes franchi leur exécution et leur pipeline post-exécution interne lors de ce run. Les signatures et slots ne sont cependant imprimés qu’après construction complète du résumé ; l’échec final empêche donc encore l’émission du bundle `METAPLEX_PNFT_LIFECYCLE_STEP/EVIDENCE` et aucune promotion n’est effectuée.
|
||||
|
||||
Le comportement observé est celui du programme Metaplex courant : `frozen_transfer` thaw le compte source, effectue le transfert SPL, puis freeze uniquement le compte destination. Il n’existe pas de second freeze du compte source. Après transfert intégral d’une pNFT, l’état attendu est donc :
|
||||
|
||||
```text
|
||||
source ATA amount = 0
|
||||
source ATA state = Initialized
|
||||
destination ATA amount = 1
|
||||
destination ATA state = Frozen
|
||||
```
|
||||
|
||||
`fix-028` corrige uniquement cette postcondition et expose explicitement les deux états token dans `METAPLEX_PNFT_LIFECYCLE_STATE`.
|
||||
|
||||
## Graphe de campagne
|
||||
|
||||
```text
|
||||
Create pNFT -> Mint pNFT
|
||||
|
|
||||
v
|
||||
Delegate(StakingV1)
|
||||
|
|
||||
v
|
||||
Lock -> Unlock
|
||||
|
|
||||
v
|
||||
Revoke(StakingV1)
|
||||
|
|
||||
v
|
||||
Delegate(TransferV1)
|
||||
|
|
||||
v
|
||||
Transfer(delegate -> fresh destination)
|
||||
```
|
||||
|
||||
Le Staking delegate et le Transfer delegate sont volontairement deux rôles successifs : le premier qualifie le cycle lock/unlock, le second le transfert délégué.
|
||||
|
||||
## Postconditions requises
|
||||
|
||||
Token Record source :
|
||||
|
||||
```text
|
||||
after Mint : Unlocked, no delegate
|
||||
after Delegate Staking : Unlocked, delegate=<fresh>, role=Staking
|
||||
after Lock : Locked, delegate=<fresh>, role=Staking
|
||||
after Unlock : Unlocked, delegate=<fresh>, role=Staking
|
||||
after Revoke : Unlocked, no delegate
|
||||
after Delegate Transfer : Unlocked, delegate=<fresh>, role=Transfer
|
||||
```
|
||||
|
||||
Après `Transfer` :
|
||||
|
||||
```text
|
||||
source ATA amount = 0
|
||||
destination ATA amount = 1
|
||||
source ATA state = Initialized
|
||||
destination ATA state = Frozen
|
||||
destination TokenRecord = Unlocked
|
||||
destination delegate = none
|
||||
```
|
||||
|
||||
Chaque étape doit aussi fournir simulation réussie, signature, confirmation, hydratation canonique, extraction Core, replay, au moins une matérialisation, snapshots stateful et seconde passe idempotente.
|
||||
|
||||
## Commande opérateur
|
||||
|
||||
```bash
|
||||
KB_DEVNET_METAPLEX_PNFT_LIFECYCLE_CAMPAIGN_TEST=1 \
|
||||
KB_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
cargo test -p kb-pipeline-demo-scenarios \
|
||||
optional_devnet_metaplex_pnft_lifecycle_campaign_from_env \
|
||||
-- --nocapture
|
||||
```
|
||||
|
||||
Une exécution complète doit produire :
|
||||
|
||||
```text
|
||||
METAPLEX_PNFT_LIFECYCLE_FIXTURE ...
|
||||
METAPLEX_PNFT_LIFECYCLE_STEP ... x6
|
||||
METAPLEX_PNFT_LIFECYCLE_STATE ...
|
||||
METAPLEX_PNFT_LIFECYCLE_EVIDENCE ...
|
||||
```
|
||||
## Qualification finale `fix-028`
|
||||
|
||||
Le rerun final retourne `ok` avec la fixture suivante :
|
||||
|
||||
```text
|
||||
mint = 9oEszJo4wT4Qodyrq7D3wD723ZFckiqYL4yB3JcJXFYc
|
||||
metadata = AWQwkdHkqjRiJ3Mz3CzmHgBwEUK7BSmpLTLr47S5799H
|
||||
master edition = E2QV5v91hvHnYDgnwQ3dh6deWYt5CtEMEYYEUsGAqZoN
|
||||
source token = 8ityw4e6phC1mdHYrvG2rrwTRmXa47W3e1tpXTfz2JcB
|
||||
source token record = FkbrKCQMejNwXVfuAqkfcM6Cw2LDsGJ5nYPUs2GEUhgp
|
||||
delegate = GcfXJhbS4fKaANtqt7pAuiFTR4S3jRAFGXv83bqbbWcQ
|
||||
destination owner = ForoAqqvXENKD7GStMb9kEpe2dKGeHCZBxRkcmSaSiui
|
||||
destination token = HbrpZbTu9MzQJrmnv3DYJQRXEkNMKNDFQCRxHxhzePEb
|
||||
destination token record = 3FcpMMmLUAyHW7duEqPFQiYCuSiLtvWkbBcZtfYGyaLo
|
||||
```
|
||||
|
||||
Les six exécutions réelles sont :
|
||||
|
||||
| Étape | Signature | Simulation | Confirmation |
|
||||
|------------------------|--------------------------------------------------------------------------------------------|-----------:|-------------:|
|
||||
| `Delegate(StakingV1)` | `mFpH2SUipx4E5fJod1kgypRBrD5HE7M6ZJen5T3KYvXPqNcU64KiAktSakZ7tB8oezrAiZTn6FdUkx48aheZN5N` | 482113797 | 482113803 |
|
||||
| `Lock` | `676sMvUwBg3G2JZK9gWYFgoHfRJ3sSyVR5Luk3AqtqgQeLnFvQZptqdUSG27Np1qiVj8vB2XncjfLCJ3hzCQN2EZ` | 482113807 | 482113812 |
|
||||
| `Unlock` | `4suhVVV6QwdSW1P1BX3NBMpRF4Yd7xVEUQ9KnvSLEUVygs4Yb4K5psKgxpod6m3Q4S28KAjPTp575ERRVbovYSTr` | 482113816 | 482113822 |
|
||||
| `Revoke(StakingV1)` | `4Av8nq4QNTjqGGLAwGe9Syn6YVtpvNbjJ1JGfrK8CGyweG8mt5N9Ua5WFFwYGYcj3Nbu1pCu7bVCJ8hiopiuMKfk` | 482113826 | 482113831 |
|
||||
| `Delegate(TransferV1)` | `KR44kRqhN8rS8bktvFDSjJnKDFGUwr6hmtTTocMBfhvuqXP8aeCQ78jTPXvianbi4wPRxQig2fFNt1ffqDTKhx1` | 482113836 | 482113840 |
|
||||
| `Transfer` | `35ABd7yhmtL6YiA4UHoxM3TvGsJu1nZDvHXy2geLyNPj25ASaE7918SswSfEKRMMNLQ8TvagQehzR8F4Z4R8JHXa` | 482113845 | 482113850 |
|
||||
|
||||
Chaque étape possède `simulationSuccess=true`, `canonicalHydration=true`, `coreExtraction=true`, `decodeReplay=true`, `materialized=true`, exactement une matérialisation instructionnelle, un snapshot matérialisé et `idempotenceReplayClean=true`. Les fees observés sont 5000 lamports pour les deux `Delegate` et `Revoke`, puis 10000 lamports pour `Lock`, `Unlock` et `Transfer`.
|
||||
|
||||
La transition finale est :
|
||||
|
||||
```text
|
||||
initial = Unlocked
|
||||
staking role = Staking
|
||||
after Lock = Locked / Staking
|
||||
after Unlock = Unlocked / Staking
|
||||
after Revoke = no delegate
|
||||
transfer role = Transfer
|
||||
source ATA after Transfer = amount 0 / Initialized
|
||||
destination ATA after Transfer = amount 1 / Frozen
|
||||
destination TokenRecord = Unlocked / no delegate
|
||||
```
|
||||
|
||||
Cette preuve ferme le lot pNFT et autorise la promotion des cinq opérations canoniques `Delegate`, `Revoke`, `Lock`, `Unlock` et `Transfer` sur la famille `programmable_nft`. La matrice Devnet passe ainsi de 6/20 à 11/20 opérations confirmées.
|
||||
@@ -0,0 +1,117 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_PRINT_BURN_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — Metaplex `Print -> Burn`
|
||||
|
||||
## Statut
|
||||
|
||||
**Qualifié sur Devnet.**
|
||||
|
||||
Le rerun suivant `pre.013-delta-fix-022` termine `ok` et fournit les preuves complètes de `Print` et `Burn`. La matrice Devnet v3 peut promouvoir les deux opérations sur la famille `nft`.
|
||||
|
||||
## Fixture qualifiée
|
||||
|
||||
```text
|
||||
master_mint = EjKZta4BpT1r9p8ECcANdxi7XQ6pjCBjuRpRp69EDCRc
|
||||
master_metadata = GHiG8W9eBbBQpw7efLX59wLnKwyMTSLJ7xPZ8x1ZDWr5
|
||||
master_edition = ARfpaZm4avbTRsFKFCHvr2xQQNepHV3QQ5uoR7hG1A86
|
||||
master_token_account = 375W7k6uMhwQKJTywyi5tSdaGD1hJvzZWScVoEYqQ1qi
|
||||
edition_mint = 5ZNDeUKSWRbWTppEk5v3Rk6pd5ETrgjCzKbM55JfM1nU
|
||||
edition_metadata = BHPfP3RJqfw8jYXm8uoKz2QpHqs9tpj6apC6bZeVgoWt
|
||||
edition = Cb7m21Kok8tWrzymtbY87T6WP6x2Eo4yLmkkthGL7mQG
|
||||
edition_token = E94rbzWFgYRPXGEdmHXrLHnHXzHYyobzMKpgvchCf3vV
|
||||
edition_marker = 2fnAxCkT76N7XDd7eB8z5zJAPGtVA9ToXssh65ZuTmdT
|
||||
```
|
||||
|
||||
Le mint d’édition et son ATA sont tous deux absents avant `Print`.
|
||||
|
||||
## `Print`
|
||||
|
||||
```text
|
||||
cluster = devnet
|
||||
genesis hash = EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG
|
||||
simulation slot = 482087795
|
||||
simulation success = true
|
||||
simulation logs = 61
|
||||
message hash = Hvue5NZTK2UauCHGcFmSLTBrLMioNwDWFMNhX8kCuPA9
|
||||
fee = 10000 lamports
|
||||
signature = REHAxUZrSdKDj8F8k27Amrg7hwuJFhVfstjc9xjn2XGfTX38fH4tFwPeNrF5zeMN5pc21PpUPSgaKXMMfTFd7TE
|
||||
confirmation = Confirmed
|
||||
confirmation slot = 482087799
|
||||
before snapshots = 2
|
||||
after snapshots = 5
|
||||
instruction materialized = 1
|
||||
materialized snapshots = 5
|
||||
canonical hydration = true
|
||||
core extraction = true
|
||||
decode replay = true
|
||||
idempotence replay clean = true
|
||||
```
|
||||
|
||||
Postconditions :
|
||||
|
||||
```text
|
||||
MasterEdition.supply : 0 -> 1
|
||||
max_supply : 1
|
||||
printed mint supply : 0 -> 1
|
||||
printed ATA amount : 0 -> 1
|
||||
Edition.edition : 1
|
||||
Edition Marker bit : false -> true
|
||||
```
|
||||
|
||||
## `Burn`
|
||||
|
||||
```text
|
||||
cluster = devnet
|
||||
genesis hash = EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG
|
||||
simulation slot = 482087813
|
||||
simulation success = true
|
||||
simulation logs = 10
|
||||
message hash = 8YpMC3PdLYZAssqAsyt6GzqjNnZFPVqAEErFo8gZ9dwF
|
||||
fee = 5000 lamports
|
||||
signature = 4cNUxGGSWifuAtqdPJAtM3Cjpi4JATitRDqrvEAe85NLpMWcgWmC27bsfjNUmK6sEBDovNPtkPV3wDH1LpfGbhd1
|
||||
confirmation = Confirmed
|
||||
confirmation slot = 482087818
|
||||
before snapshots = 5
|
||||
after snapshots = 2
|
||||
instruction materialized = 1
|
||||
materialized snapshots = 2
|
||||
canonical hydration = true
|
||||
core extraction = true
|
||||
decode replay = true
|
||||
idempotence replay clean = true
|
||||
```
|
||||
|
||||
Postconditions finales :
|
||||
|
||||
```text
|
||||
MasterEdition.supply : 1 -> 0
|
||||
MasterEdition.max_supply : 1
|
||||
printed mint supply : 1 -> 0
|
||||
printed ATA : absent
|
||||
printed Edition : absente
|
||||
Edition Marker : absent
|
||||
printed Metadata RPC : présente
|
||||
printed Metadata fee tombstone : true
|
||||
printed Metadata semantic closed : true
|
||||
```
|
||||
|
||||
Le compte Metadata restant est exactement le tombstone officiel attendu : owner Token Metadata, non exécutable, lamports résiduels positifs, `space=1`, donnée `[0x00]`. Il ne s’agit pas d’une Metadata active.
|
||||
|
||||
## Qualification
|
||||
|
||||
Les deux opérations satisfont les 16 preuves réseau requises par famille : simulation, contexte réseau, message/fee, signature, confirmation, états avant/après, hydratation canonique, extraction Core, decode replay, matérialisation et idempotence.
|
||||
|
||||
La matrice après promotion est :
|
||||
|
||||
```text
|
||||
confirmed : 6
|
||||
not_run : 14
|
||||
|
||||
Create : confirmed
|
||||
Mint : confirmed
|
||||
Verify : confirmed
|
||||
Unverify : confirmed
|
||||
Print : confirmed
|
||||
Burn : confirmed
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_013_METAPLEX_USE_DEVNET_VALIDATION_REPORT.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Validation Devnet `0.4.8-pre.013` — disponibilité `Use`
|
||||
|
||||
## Statut
|
||||
|
||||
**Indisponible au runtime Devnet avec la surface courante du programme.**
|
||||
|
||||
Le probe réel du 8 août 2026 a préparé un NFT classique frais avec `Uses::Multiple { remaining: 2, total: 2 }`, terminé le chemin qualifié `Create -> Mint`, puis simulé l'instruction courante `Use` de discriminant 51 sans soumettre de transaction.
|
||||
|
||||
La simulation a été exécutée et refusée par Token Metadata :
|
||||
|
||||
```text
|
||||
simulated = true
|
||||
success = false
|
||||
cluster = devnet
|
||||
blockhash_kind = latest
|
||||
blockhash_age_slots = 1
|
||||
units_consumed = 12085
|
||||
estimated_fee_lamports = 5000
|
||||
error = {"InstructionError":[0,"InvalidInstructionData"]}
|
||||
```
|
||||
|
||||
Logs observés :
|
||||
|
||||
```text
|
||||
Program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s invoke [1]
|
||||
Program log: Error: InvalidInstructionData
|
||||
Program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s consumed 12085 of 200000 compute units
|
||||
Program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s failed: invalid instruction data
|
||||
```
|
||||
|
||||
Aucune signature `Use` n'a été produite et aucune soumission n'a été tentée.
|
||||
|
||||
## Interprétation
|
||||
|
||||
Le client Rust généré expose bien `Use`, mais le routeur du programme courant ne possède pas de branche nouvelle API correspondante. La surface legacy conserve `Utilize`, qui est une instruction distincte. La réponse Devnet confirme donc la divergence client/runtime : l'instruction actuelle est rejetée avant exécution avec `InvalidInstructionData`.
|
||||
|
||||
La matrice `METAPLEX_TOKEN_METADATA_DEVNET_EXECUTION_MATRIX.json` classe désormais `Use` comme `unavailable`, avec la raison runtime et les logs observés. Cette classification ne doit pas être transformée en `confirmed` et ne doit pas être confondue avec `not_run`.
|
||||
|
||||
## Correction du probe
|
||||
|
||||
Le premier probe a terminé le processus de test en erreur parce que la readiness générale exigeait encore une simulation réussie avant même de retourner le résumé en mode `submit=false`.
|
||||
|
||||
`pre.013-delta-fix-030` distingue désormais les deux contrats :
|
||||
|
||||
- une simulation exacte échouée peut être retournée comme donnée lorsque `submit=false`, afin d'alimenter un probe de disponibilité ;
|
||||
- une soumission reste strictement impossible tant que la simulation exacte n'a pas réussi ;
|
||||
- `send_authorized` reste donc faux pour toute simulation négative.
|
||||
|
||||
Le probe `Use` peut ainsi imprimer proprement `status=runtime_unavailable` et terminer `ok` sans affaiblir la politique simulation-first de soumission.
|
||||
|
||||
## Commande de contrôle
|
||||
|
||||
```bash
|
||||
KB_DEVNET_METAPLEX_USE_PROBE_TEST=1 \
|
||||
KB_DEVNET_METAPLEX_OPERATOR_CONFIRMED=1 \
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
cargo test -p kb-pipeline-demo-scenarios \
|
||||
optional_devnet_metaplex_use_probe_from_env \
|
||||
-- --nocapture
|
||||
```
|
||||
|
||||
Après `fix-030`, la sortie attendue pour la surface runtime observée est :
|
||||
|
||||
```text
|
||||
METAPLEX_USE_PROBE_FIXTURE ...
|
||||
METAPLEX_USE_PROBE_SIMULATION success=false ...
|
||||
METAPLEX_USE_PROBE_STATUS status=runtime_unavailable ...
|
||||
METAPLEX_USE_PROBE_LOGS [...]
|
||||
```
|
||||
## Rerun qualifiant après correction du probe
|
||||
|
||||
Le rerun `fix-030` du 8 août 2026 confirme le contrat sans erreur d’orchestration. La fixture observée est :
|
||||
|
||||
```text
|
||||
mint = G9cXdhB1MjmbKk4Ari9Y9kposL7JJfDaQEWomz8xxfpM
|
||||
metadata = D1snmEtU8BroQHV2fVBfw7CcWVqT4BhqBD8MQTyfSciY
|
||||
master_edition = 8pV6JRefP5QQB4UYLSwaJCzEkHFfU8mLGjEb8i5TDR7z
|
||||
token = BfEGL81HW9KMVodVf4oXeK4mX6KMNTAZaYqCsb4NF3BW
|
||||
uses_before = 2
|
||||
simulation_context_slot = 482133665
|
||||
```
|
||||
|
||||
La simulation reste `success=false`, conserve exactement quatre logs et `InstructionError[0]=InvalidInstructionData`. La ligne `METAPLEX_USE_PROBE_STATUS` expose `status=runtime_unavailable`, `uses_after=null` et la raison complète ; aucune ligne de soumission n’est produite. Le test opt-in termine `ok`. Les autres checks et suites de tests rejoués localement restent sans régression.
|
||||
@@ -0,0 +1,209 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_014_DESKTOP_METADATA_VALIDATION_REPORT.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Validation desktop Metadata `0.4.8-pre.014`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Ce rapport consolide la première validation réelle du panneau `demo_execution_metadata` après l’ouverture de `0.4.8-pre.014`, puis motive le correctif de câblage Metaplex du delta suivant.
|
||||
|
||||
Il ne remplace pas les matrices réseau spécialisées. Il qualifie uniquement l’intégration desktop et la correspondance entre l’UI Tauri et les campagnes réutilisables déjà validées.
|
||||
|
||||
## 2. Contrôles locaux
|
||||
|
||||
Le 8 août 2026, après application du premier delta `pre.014`, les contrôles suivants réussissent :
|
||||
|
||||
```text
|
||||
cargo fmt --all OK
|
||||
cargo check --workspace OK
|
||||
cargo clippy --all-targets OK, aucun warning
|
||||
scripts/audit_rust_workspace_rules.py clean
|
||||
kb-pipeline-demo-scenarios 105 unitaires + 1 CLI + 1 API externe
|
||||
kb-app-demo-desktop 139 unitaires
|
||||
cargo tauri dev démarrage OK
|
||||
```
|
||||
|
||||
La validation visuelle confirme :
|
||||
|
||||
- trois accordéons distincts pour Metaplex Token Metadata, Token-2022 Token Metadata et Solana Program Metadata ;
|
||||
- synchronisation du profil sélectionné entre les trois domaines ;
|
||||
- journal d’exécution placé sous la grille et utilisant toute la largeur disponible.
|
||||
|
||||
## 3. Token-2022 Token Metadata
|
||||
|
||||
Les logs fournis montrent une campagne desktop complète sur Devnet, sans entrée dans les fichiers `error*.jsonl`.
|
||||
|
||||
| Opération | Signature | Slot |
|
||||
|-------------------|--------------------------------------------------------------------------------------------|------------:|
|
||||
| `Initialize` | `23aXutFc25Ncn4XPSGdaaEzvUpe9s57gYunmMq1asmFRK9xe1tAYVCnV3dePUn2a7qqq51JDsPKp5ep5d1DLypNv` | `482188064` |
|
||||
| `UpdateField` | `Uws9Laene33LxYet4562LKXMkKjAhW5FPweQSnFJv7NG6s4u7LqB7zbppQgQgaNFE52d9UVZKLmjZoK9zb4xdHK` | `482188079` |
|
||||
| `Emit` | `2uNggBbSvvj7Cqdddt1KtFgFhTJ9jsyvobeGUaAU6kPNY9o4hJ6BVHEhpELe1sRSyrMrgbeGLoRiWAMU2UrisMcE` | `482188091` |
|
||||
| `RemoveKey` | `3xUUnjQvWzSkMQPe2VFDyYX14K3STxBzFjzWEawrK28UsoJzXTPYA61nzkL5JvLwCfhNRg7YpBcsSSjhTJqtqNYT` | `482188102` |
|
||||
| `UpdateAuthority` | `2U9zrLL9NusrhpW9Dx15SqZ8c4d6zbcgSyM4hBcewr1Ar7JwLYwvBduLKs4HKEgZP9i5YQQ9cyTC977mjJGLWqXW` | `482188113` |
|
||||
|
||||
Les traces `kb-pipeline` et `kb-lib` montrent le décodage Token-2022, la matérialisation `materializer.metadata.token_2022` et la seconde passe de replay/idempotence. La fixture a été préparée auparavant par la signature `LNwzGfvCgZdumbYXfAecCxU6hfHYGa6XRwC7E71jc4JTDUj5QyVBVV5Sd9KLWmX2t72XfKBiYUEV6WYCvPqC4wd`, slot `482188053`.
|
||||
|
||||
**Statut desktop : validé.**
|
||||
|
||||
## 4. Solana Program Metadata
|
||||
|
||||
La campagne desktop `ProgM6…` traverse les deux parcours complets après préparation des PDA frais `buffer=6bs5XpPsNUqw8FatFJTJu81ZAUBCvxmyxptNyUY4RnDU` et `metadata=7fTTM3KCtFMjxmTaWa6xJ3nSbK9Yi7MdVXP3dSa8ahPB`.
|
||||
|
||||
Les neuf opérations atteignent toutes l’état `Confirmed` dans les contrôles post-exécution :
|
||||
|
||||
```text
|
||||
Buffer : Allocate -> Extend -> Write -> SetAuthority -> Trim -> Close
|
||||
Metadata : Initialize -> SetData -> SetImmutable
|
||||
```
|
||||
|
||||
Signatures observées :
|
||||
|
||||
```text
|
||||
Allocate Je6SLx7wfNqEJ8Huo64r177Sm9bda4uFXUPwuxfZG89jvb1zKMKLnzTJEL5DFHxftMq38FsLNF3TZ14kaWSZ7Hb
|
||||
Extend LYgf8ac4CKYdY2xkw9bCS8J9cvkphWA8ueCUJ5CBKZ11jEdS2NZLykF4jwmSA5AnVYWxzNABeXhGV4d1dFvzMnM
|
||||
Write 4p7XEXeGgWQjJkWTfrBB5rL9XwDadxzoCPreLfaB3fBGACyrPqZYmnBGqVYVgyR9zDZNxuKhimEwMtmiGdJseKa2
|
||||
SetAuthority 5kPi3eBqbq92kV5ZFWPur9afAokGxhgr84PPQh7s5btyQYayC3TDuaH9TCcspxbd4YEYDD4icFYnSWX2Ej9XdLgA
|
||||
Trim 5YG7Yp6x1waZZKoRjb37q7QUNJL5CMEUgu8gs7ehDwHUx1TXvV5BaeUJFT1Pb58z4hSo4woey2qaKQggAzaNtYEG
|
||||
Close 2jyZzegSM2v7LJ15nF3r9zXsLXd7ZDsWsCEWHLXQdoBrLkrbuSuwVrGx1jTqYejw195FVhWHeJQYY6BaN98s5R6
|
||||
Initialize 2kzvpRi8KwRhaQPiwtR8PCQGTuQ3e3kUNSi7EVsMXqJzhQkZanjNwxsL2sGeB8Xs56N22TaaYbS6LJimQpHZWk6J
|
||||
SetData 3WZHzYno9M6uHLMiXsNBtBHoN3yAp7ftd3pSNWpuj2jATk1g29nvUccgUu6wuRijv9oBo2Vi1dHKury2DFfDQ1ib
|
||||
SetImmutable XTAj5NNVXRkhKe4DxsXGm2xtxocrwHwkkpEun4ib6h4qs6DX5f2K6gfgQ2jppkTWoC83MyoPgrpvdKes1b72gbJ
|
||||
```
|
||||
|
||||
**Statut desktop : validé.**
|
||||
|
||||
## 5. Metaplex Token Metadata : écart constaté
|
||||
|
||||
Les logs de la même session ne montrent pas une campagne Metaplex spécialisée. Le desktop prépare uniquement une fixture classique avec :
|
||||
|
||||
```text
|
||||
operation = metadata.metaplex_token_metadata.prepare_native_mint_and_ata
|
||||
signature = 5YdSQHZ5T3VV8zefmQS4GVHNXPbvCijiLyHdgwvqcmGMMpobAJcM4HvdeQ7vex9qDqcYtAVUtj4U3bDCeQ2Hfzvv
|
||||
slot = 482188642
|
||||
```
|
||||
|
||||
Aucune séquence qualifiée `Create -> Mint`, collection, Print/Burn, lifecycle pNFT, escrow, maintenance ou Use ne suit cette préparation avant la fin des logs.
|
||||
|
||||
La cause est un écart de câblage desktop : le panneau utilisait encore le workflow générique `scénario -> étape -> intent JSON`, alors que `pre.013` a qualifié des campagnes spécialisées cohérentes et indivisibles.
|
||||
|
||||
## 6. Correctif `pre.014-delta-fix-001`
|
||||
|
||||
Le correctif conserve les scénarios synthétiques pour l’inspection de contrats mais remplace le parcours Devnet principal par 11 campagnes spécialisées :
|
||||
|
||||
1. `Create -> Mint` NFT ;
|
||||
2. `Create -> Mint` SFT ;
|
||||
3. `Create -> Mint` fungible ;
|
||||
4. `Create -> Mint` collection ;
|
||||
5. `Create -> Mint` pNFT ;
|
||||
6. collection `Verify -> Unverify` ;
|
||||
7. `Print -> Burn` ;
|
||||
8. lifecycle pNFT ;
|
||||
9. Token Owned Escrow ;
|
||||
10. maintenance ;
|
||||
11. probe Use.
|
||||
|
||||
Maintenance et Use conservent les opérations `unavailable` comme probes simulation-only ; elles ne sont pas converties en soumissions isolées.
|
||||
|
||||
La carte **Exigences** est également déplacée en bas de la colonne droite, après **Postconditions et preuves**. Le journal reste en pleine largeur sous la grille.
|
||||
|
||||
## 7. Validation de `fix-001`
|
||||
|
||||
Après application de `fix-001` :
|
||||
|
||||
- les 11 campagnes Metaplex sont bien exposées par le nouveau contrat desktop ;
|
||||
- les contrôles statiques `fmt`, `check`, Clippy et audit workspace sont propres ;
|
||||
- `kb-pipeline-demo-scenarios` conserve 105 tests unitaires + 1 CLI + 1 API externe réussis ;
|
||||
- `kb-app-demo-desktop` exécute 144 tests, avec 143 réussites et un échec du test historique qui cherche encore les anciens IDs de contrôles supprimés ;
|
||||
- deux tentatives réelles de `Create -> Mint — NFT` provoquent un arrêt brutal du processus pendant la post-validation du `Create`.
|
||||
|
||||
## 8. Correctif `pre.014-delta-fix-002`
|
||||
|
||||
Après application de `fix-001`, deux essais successifs de `Create -> Mint — NFT` provoquent un arrêt brutal de l'application au même endroit. Le second essai est effectué après purge des logs.
|
||||
|
||||
Le journal réseau prouve que la fixture SPL classique est créée, puis que l'opération Metaplex `Create` est simulée, envoyée et confirmée :
|
||||
|
||||
```text
|
||||
fixture signature : 64kLtKRiCewywr1CdNbcznHZS7PtNnfh5vxzZLte2VoUexkoyGtRdVozMxsATpP97Sp1fjv2fmPwGUj9hM6VFH8d
|
||||
fixture slot : 482323101
|
||||
Create signature : NAXZephC3aPqe8KA4Ly81o56xkhUEEMMLdZKnYEqiifvRGuQmf997nM29EtWZESyC97qu75v4UzyRWt4aqxRN1d
|
||||
Create slot : 482323110
|
||||
```
|
||||
|
||||
Le dernier événement écrit est le lancement de l'hydratation canonique de cette signature puis l'envoi de `getTransaction`. Aucun panic Rust, aucune erreur applicative structurée et aucune erreur JSONL ne sont enregistrés avant la disparition du processus. Le défaut n'est donc pas classé comme un échec Metaplex de la transaction : il appartient à l'intégration desktop/runtime du nouveau dispatch de campagnes.
|
||||
|
||||
`fix-002` :
|
||||
|
||||
- conserve les campagnes réutilisables intactes ;
|
||||
- boxe explicitement le futur du dispatch Metaplex avant l'attente Tauri, afin d'éviter de conserver le gros state machine des onze branches directement dans le futur de commande ;
|
||||
- journalise `campaign_start`, `campaign_completed` et `campaign_failed` ;
|
||||
- réaligne le test frontend historique sur les contrôles de campagne qualifiés introduits par `fix-001`.
|
||||
|
||||
La validation Tauri doit reprendre par `Create -> Mint — NFT`. Si un arrêt brutal subsiste, les nouveaux marqueurs et la dernière étape réseau permettront de distinguer un défaut du runtime desktop d'un défaut d'hydratation HTTP.
|
||||
|
||||
## 9. Validation réelle de `fix-002`
|
||||
|
||||
La validation locale du 9 août 2026 est propre :
|
||||
|
||||
```text
|
||||
cargo fmt --all OK
|
||||
cargo check --workspace OK
|
||||
cargo clippy --all-targets OK, aucun warning
|
||||
scripts/audit_rust_workspace_rules.py clean
|
||||
kb-app-demo-desktop 144 tests unitaires, tous OK
|
||||
cargo tauri dev démarrage OK
|
||||
```
|
||||
|
||||
Le bundle de logs du second essai confirme que le boxing du dispatch Metaplex supprime l’arrêt brutal observé avec `fix-001`. Les onze campagnes exposées par le desktop ont toutes un marqueur `campaign_start` suivi d’un marqueur `campaign_completed`, sans `campaign_failed` :
|
||||
|
||||
| Campagne | Exécutions/probes projetés | Statut desktop |
|
||||
|--------------------------------|---------------------------:|---------------------------------------------|
|
||||
| `create_mint_nft` | 2 | terminé |
|
||||
| `create_mint_sft` | 2 | terminé |
|
||||
| `create_mint_fungible` | 2 | terminé |
|
||||
| `create_mint_collection` | 2 | terminé |
|
||||
| `create_mint_programmable_nft` | 2 | terminé |
|
||||
| `collection_verify` | 6 | terminé |
|
||||
| `print_burn` | 4 | terminé |
|
||||
| `pnft_lifecycle` | 8 | terminé |
|
||||
| `escrow` | 7 | terminé |
|
||||
| `maintenance` | 7 | terminé, dont 4 indisponibilités qualifiées |
|
||||
| `use_probe` | 3 | terminé, dont 1 indisponibilité qualifiée |
|
||||
|
||||
Les journaux UI confirment notamment `maintenance : confirmed=3, unavailable=4, evidence=7` et `use_probe : confirmed=2, unavailable=1, evidence=3`. Les fichiers `error*.jsonl` du bundle fourni sont tous vides.
|
||||
|
||||
**Statut desktop Metaplex : validé pour les onze campagnes qualifiées.**
|
||||
|
||||
## 10. Consolidation structurelle `fix-003`
|
||||
|
||||
Le correctif suivant ne change aucune campagne réseau. Il simplifie seulement le desktop :
|
||||
|
||||
- `demo_execution_metadata_metaplex_campaign.rs` est fusionné dans `demo_execution_metadata_metaplex_token_metadata.rs` ;
|
||||
- les commandes Tauri et les contrats TS-RS restent inchangés ;
|
||||
- Metaplex suit désormais le même modèle de module unique que Token-2022 Token Metadata et Solana Program Metadata ;
|
||||
- les trois blocs de résultats deviennent un accordéon Bootstrap commun, sur le modèle de `demo_execution_spl` ;
|
||||
- la carte **Exigences** reste sous l’accordéon dans la colonne droite ;
|
||||
- le journal d’exécution reste pleine largeur sous la grille.
|
||||
|
||||
Après application, la validation requise est locale (`fmt`, `check`, Clippy, audit, 144 tests desktop) puis visuelle dans Tauri pour l’ouverture/fermeture des trois JsonViewer. Aucune nouvelle campagne Devnet n’est requise pour requalifier Metaplex.
|
||||
|
||||
## 11. Clôture de `pre.014`
|
||||
|
||||
La validation finale confirme que `fix-003` ne réintroduit aucune régression :
|
||||
|
||||
```text
|
||||
cargo fmt --all OK
|
||||
cargo check --workspace OK
|
||||
cargo clippy --all-targets OK, aucun warning
|
||||
scripts/audit_rust_workspace_rules.py clean
|
||||
kb-app-demo-desktop 144 tests unitaires, tous OK
|
||||
```
|
||||
|
||||
La vérification visuelle Tauri confirme également :
|
||||
|
||||
- les trois accordéons de résultats s’ouvrent et se ferment correctement ;
|
||||
- les JsonViewer restent lisibles dans les accordéons ;
|
||||
- la carte **Exigences** reste sous les résultats dans la colonne droite ;
|
||||
- le journal d’exécution reste en pleine largeur sous la grille ;
|
||||
- les trois domaines Metaplex Token Metadata, Token-2022 Token Metadata et Solana Program Metadata restent distincts.
|
||||
|
||||
Les campagnes réseau ayant déjà été qualifiées avant le refactor structurel de `fix-003`, aucun rerun Devnet supplémentaire n’est requis. **Statut de `0.4.8-pre.014` : terminé et validé.**
|
||||
@@ -0,0 +1,82 @@
|
||||
<!-- file: docs/validation/V0_4_8_PRE_016_FINAL_CONFORMITY_VALIDATION_REPORT.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Validation finale de conformité `0.4.8-pre.016`
|
||||
|
||||
## 1. Objet
|
||||
|
||||
Ce rapport pilote la dernière prerelease de `0.4.8`. Il ne rouvre aucune fonctionnalité ni campagne réseau déjà qualifiée ; il vérifie que le workspace, le frontend, la documentation et les livrables de release sont cohérents avant la clôture de `0.4.8`.
|
||||
|
||||
## 2. Base validée avant ouverture
|
||||
|
||||
La base `0.4.8-pre.015` est clôturée avec :
|
||||
|
||||
- `cargo fmt --all` réussi ;
|
||||
- `cargo check --workspace` réussi ;
|
||||
- `cargo clippy --all-targets` réussi sans warning ;
|
||||
- audit général, exports et workspace propres ;
|
||||
- `kb-pipeline` : 109 tests unitaires et trois tests d’API Metadata externes réussis ;
|
||||
- `kb-lib` : 706 tests unitaires et neuf tests d’intégration réussis ;
|
||||
- `kb-pipeline-demo-scenarios` : 105 tests unitaires, 1 test CLI et 1 test API externe réussis ;
|
||||
- réconciliation Metadata sans divergence fonctionnelle restante.
|
||||
|
||||
## 3. Campagne finale à exécuter
|
||||
|
||||
Depuis la racine du workspace :
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo clippy --all-targets
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
Le frontend de release ne doit pas être construit par une commande npm autonome. La validation TypeScript/Vite et l’assemblage desktop sont exécutés ensemble par Tauri :
|
||||
|
||||
```bash
|
||||
cargo tauri build -c kb-app-demo-desktop/tauri.conf.json
|
||||
```
|
||||
|
||||
Tauri déclenche lui-même le `beforeBuildCommand` configuré dans `kb-app-demo-desktop/tauri.conf.json`.
|
||||
|
||||
## 4. Critères d’acceptation
|
||||
|
||||
La campagne finale est qualifiée uniquement si :
|
||||
|
||||
- aucun échec de compilation, Clippy ou audit n’est présent ;
|
||||
- tous les tests workspace réussissent ;
|
||||
- TypeScript et Vite construisent le frontend sans erreur ;
|
||||
- le build Tauri de release termine correctement ;
|
||||
- aucune nouvelle divergence de matrice, export, registre runtime ou documentation n’est découverte ;
|
||||
- aucune campagne Devnet n’est rejouée sans régression motivant ce rerun.
|
||||
|
||||
## 5. Travaux documentaires après validation
|
||||
|
||||
Après réception des résultats verts, la clôture doit encore :
|
||||
|
||||
- mettre à jour le CHANGELOG général uniquement avec des versions fonctionnelles clôturées ;
|
||||
- passer le ROADMAP de `0.4.8` de « en clôture finale » à « version clôturée » ;
|
||||
- réconcilier une dernière fois README, USAGE, TODO et changelogs des crates concernées ;
|
||||
- préparer le prompt de la prochaine session/version ;
|
||||
- archiver le plan temporaire `0.4.8` sous `olddocs/archivekbot3/` après transfert de ses informations durables ;
|
||||
- retirer ou corriger les références actives vers le plan archivé ;
|
||||
- préparer la livraison finale de `0.4.8`.
|
||||
|
||||
## 6. Résultats observés
|
||||
|
||||
La campagne finale du 9 août 2026 est conforme :
|
||||
|
||||
- `cargo fmt --all` réussi ;
|
||||
- `cargo check --workspace` réussi ;
|
||||
- `cargo clippy --all-targets` réussi sans warning ;
|
||||
- audit général Rust, exports et règles Khadhroony propre ;
|
||||
- `cargo test --workspace` réussi sur toutes les crates et cibles d’intégration ;
|
||||
- `cargo tauri build -c kb-app-demo-desktop/tauri.conf.json` réussi ;
|
||||
- le `beforeBuildCommand` Tauri a exécuté TypeScript et Vite sans erreur ;
|
||||
- Vite a produit le frontend dans le `dist` commun du workspace ;
|
||||
- Tauri a produit les bundles `.deb`, `.rpm` et `.AppImage` en version desktop `0.4.8`.
|
||||
|
||||
## 7. Statut
|
||||
|
||||
**Campagne finale de conformité validée.**
|
||||
@@ -0,0 +1,158 @@
|
||||
<!-- file: prompts/028_v0_4_8_spl_token_metadata_and_offchain_decision.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Prompt de session — 0.4.8 Solana Program Metadata et complétude Token-2022
|
||||
|
||||
## Mission
|
||||
|
||||
Reprendre `khadhroony-bot3` après la clôture de `0.4.7`, implémenter Solana Program Metadata (`ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S`) comme surface indépendante, puis auditer et compléter le contrat `spl-token-metadata-interface` déjà implémenté par Token-2022. Metaplex Token Metadata, Token-2022 Token Metadata et Solana Program Metadata doivent rester strictement séparés.
|
||||
|
||||
La session doit commencer par une planification complète et structurée, puis conserver une planification vivante pendant tout le développement. Le plan initial doit couvrir l’ensemble de la version, mais il peut être corrigé, enrichi, réordonné ou regrouper des phases lorsque l’état réel du code le justifie.
|
||||
|
||||
Le plan actif est `docs/plans/V0_4_8_SOLANA_PROGRAM_METADATA_AND_TOKEN_2022_COMPLETENESS_PLAN.md`.
|
||||
|
||||
## Exigences de départ obligatoires
|
||||
|
||||
Avant toute proposition d’architecture, modification de code ou création de delta :
|
||||
|
||||
1. lire les documents racine actifs, notamment `README.md`, `ROADMAP.md`, `RULES.md` et les documents auxquels ils renvoient ;
|
||||
2. lire intégralement les règles et normes actives du projet :
|
||||
- `docs/rules/RULES_GENERAL.md` ;
|
||||
- `docs/rules/RULES_RUST.md` ;
|
||||
- `docs/rules/RULES_SPECIFIC_KHADHROONY.md` ;
|
||||
- `docs/rules/CRATE_DOCUMENTATION_RULES.md` ;
|
||||
- `docs/rules/VERSION_DEVELOPMENT_LIFECYCLE.md` ;
|
||||
3. relever et appliquer les conventions portant notamment sur :
|
||||
- Rust 2024 et les restrictions du workspace ;
|
||||
- visibilité, exports publics, imports et documentation Rust ;
|
||||
- interdictions `unsafe`, `unwrap`, `expect`, `panic` et règles Tauri spécifiques ;
|
||||
- entêtes `file:` et `version:` ;
|
||||
- incrémentation des versions locales des fichiers modifiés ;
|
||||
- lignes vides, fins de fichier et organisation des modules ;
|
||||
- emplacement attendu de chaque type de code, test, fixture, IDL et document ;
|
||||
- format, contenu et rôle de `README.md`, `USAGE.md`, `TODO.md`, `CHANGELOG.md`, rapports, guides, prompts et archives ;
|
||||
- documents à mettre à jour selon la nature de chaque modification ;
|
||||
- ordre antéchronologique des changelogs et limitation de chaque changelog à son propre périmètre ;
|
||||
- politique des prereleases, deltas, correctifs et archivages ;
|
||||
4. inspecter les `README.md`, `USAGE.md`, `TODO.md` et `CHANGELOG.md` des crates directement concernées avant de les modifier ;
|
||||
5. inspecter les audits, matrices, IDL, rapports de validation et archives pertinentes sans modifier les archives historiques ;
|
||||
6. vérifier l’état réel du workspace avant d’utiliser une hypothèse issue d’une ancienne session ou d’un ancien document.
|
||||
|
||||
Une règle découverte en cours de session doit être intégrée immédiatement au travail courant ; elle ne doit pas être reportée à la clôture.
|
||||
|
||||
## Première prerelease obligatoire
|
||||
|
||||
La première prerelease est consacrée au plan et au brainstorming. Avant le code, elle doit :
|
||||
|
||||
- inventorier les programmes, Program ID, interfaces, comptes, instructions, builders et sources officielles ;
|
||||
- distinguer précisément Solana Program Metadata, Metaplex Token Metadata, Token-2022 Token Metadata et le rôle contractuel de `spl-token-metadata-interface` ;
|
||||
- identifier ce qui existe déjà dans `kb-program-ids`, `kb-lib`, `kb-pipeline`, `kb-pipeline-demo-scenarios`, `kb-app-demo-desktop`, `kb-store`, les matrices et les documents ;
|
||||
- fixer séparément le périmètre decoder/materializer/executor/pipeline/desktop de `ProgM6…` et les compléments nécessaires dans les modules Token-2022 existants ;
|
||||
- décider les fixtures et scénarios réseau nécessaires ;
|
||||
- découper les prereleases et définir leurs critères d’acceptation ;
|
||||
- enregistrer le report de `kb-offchain-transport` à l’horizon `0.15+` sans commencer son implémentation ;
|
||||
- préciser les documents, changelogs, TODO, matrices et rapports à maintenir pendant chaque phase.
|
||||
|
||||
Le plan doit couvrir la version complète dès le départ, y compris les scénarios Devnet réels. Il reste toutefois souple : des étapes peuvent être ajoutées, déplacées, fusionnées ou séparées si les découvertes techniques l’exigent.
|
||||
|
||||
L’objectif opérationnel est de conserver des deltas petits et testables, dont la préparation reste généralement compatible avec une livraison toutes les 10 à 15 minutes de travail effectif. Une phase trop large doit être divisée ; des phases devenues artificiellement petites peuvent être regroupées.
|
||||
|
||||
## Scénarios Devnet réels obligatoirement prévus
|
||||
|
||||
L’erreur à ne pas reproduire depuis `0.4.7` est de ne pas avoir prévu les scénarios réels dans la planification initiale.
|
||||
|
||||
Pour chaque capacité exécutable retenue, le plan doit prévoir dès le départ, puis maintenir pendant le développement :
|
||||
|
||||
1. tests contractuels et synthétiques ;
|
||||
2. campagne réutilisable dans `kb-pipeline-demo-scenarios` lorsque cela apporte une validation automatisable ;
|
||||
3. panneau ou parcours réel dans `kb-app-demo-desktop` ;
|
||||
4. fixture Devnet reproductible et préparée par les APIs Rust du workspace lorsque cela est approprié ;
|
||||
5. simulation RPC du message exact ;
|
||||
6. soumission confirmée lorsque l’opération est sûre et réalisable ;
|
||||
7. lectures stateful, postconditions et matérialisation observables ;
|
||||
8. état de validation explicite : synthétique, simulé Devnet, soumis Devnet, non applicable, indisponible ou reporté avec justification.
|
||||
|
||||
La mise en œuvre peut évoluer pendant la version, mais les scénarios réels ne doivent jamais disparaître du plan. Une surface ne doit pas être annoncée comme validée réseau sans preuve d’exécution réelle observée.
|
||||
|
||||
## Audit et gestion des IDL
|
||||
|
||||
Avant le code :
|
||||
|
||||
1. vérifier l’IDL officielle et les sources officielles de Solana Program Metadata ;
|
||||
2. examiner l’IDL déjà présente :
|
||||
`idls/metadata.ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S.solana_program_metadata.V0_0_0.from_github_solana_program.json` ;
|
||||
3. déterminer, depuis les sources officielles, si cette IDL correspond exactement au programme `ProgM6…` et à son contrat ;
|
||||
4. ne pas assimiler deux surfaces seulement parce que leurs noms contiennent « metadata ».
|
||||
|
||||
Lorsqu’une IDL officielle pertinente est trouvée :
|
||||
|
||||
- conserver une copie brute et non réécrite sous `idls/` ;
|
||||
- appliquer la convention de nommage documentée dans `idls/001.README.md` ;
|
||||
- encoder la provenance et la version dans le nom ;
|
||||
- vérifier le Program ID déclaré, la validité JSON et l’empreinte ;
|
||||
- mettre à jour au minimum :
|
||||
- `idls/001.README.md` ;
|
||||
- `docs/IDL_AUDIT.md` ;
|
||||
- `docs/IDL_TO_KB_LIB_NOMENCLATURE.md` ;
|
||||
- `docs/MISSING_PROGRAM_IDLS.md` si le statut « manquante » change ;
|
||||
- les documents de classification ou de surface IDL concernés ;
|
||||
- mettre à jour les compteurs, inventaires, provenance, nomenclature cible et décisions architecturales associées ;
|
||||
- ne jamais inventer une version ou une provenance absente de la source.
|
||||
|
||||
Si aucune IDL officielle exploitable n’existe, documenter cette absence et utiliser les crates/interfaces officielles comme source de vérité sans fabriquer d’IDL.
|
||||
|
||||
## Frontières
|
||||
|
||||
- `spl-token-metadata-interface` ne doit pas être présenté comme un programme autonome ;
|
||||
- Solana Program Metadata est une surface indépendante de Token-2022 Token Metadata ;
|
||||
- les metadata Token-2022 restent dans les modules Token-2022 existants et ne sont pas un décodeur partiel de `ProgM6…` ;
|
||||
- le fetch off-chain n’est pas incorporé au décodeur canonique ;
|
||||
- les IDL et interfaces archivées restent des références statiques ;
|
||||
- les archives sous `olddocs/` ne doivent pas être modifiées hors opération d’archivage explicitement prévue.
|
||||
|
||||
## Décision `kb-offchain-transport`
|
||||
|
||||
`kb-offchain-transport` est hors périmètre de `0.4.8` et reportée à l’horizon `0.15+`. Aucun module HTTP(S), IPFS ou Arweave ne doit être créé pendant cette version.
|
||||
|
||||
La frontière architecturale reste documentée : timeout, taille, MIME, redirections, cache, hash, provenance, résolution DNS contrôlée, protection SSRF et interdiction des réseaux locaux seront obligatoires lorsqu’un consommateur réel justifiera cette crate. Le contenu distant ne modifiera jamais le statut canonique du replay on-chain.
|
||||
|
||||
## Discipline documentaire pendant la version
|
||||
|
||||
À chaque delta :
|
||||
|
||||
- mettre à jour uniquement les documents rendus faux ou incomplets par les modifications du delta ;
|
||||
- conserver les `README.md` et `USAGE.md` généralistes, structurés par fonction ou groupe, jamais comme journaux de prerelease ;
|
||||
- réserver les détails chronologiques aux changelogs et rapports de validation ;
|
||||
- supprimer des TODO les tâches réellement terminées ;
|
||||
- ajouter seulement des TODO explicites, encore actionnables et placés dans le bon périmètre ;
|
||||
- maintenir les changelogs du plus récent au plus ancien et limiter chaque entrée aux changements de son emplacement ;
|
||||
- réconcilier les matrices, guides, rapports, nomenclatures et documents IDL avec le code livré ;
|
||||
- archiver les documents de travail devenus historiques plutôt que de les laisser parmi les documents actifs.
|
||||
|
||||
## Dernière prerelease obligatoire
|
||||
|
||||
La dernière prerelease doit être réservée à :
|
||||
|
||||
- validations finales et conformité ;
|
||||
- documentation générale et par crate ;
|
||||
- nettoyage des TODO ;
|
||||
- mise à jour du ROADMAP et des changelogs ;
|
||||
- réconciliation des documents IDL ;
|
||||
- archivage du plan, des rapports intermédiaires et du prompt de session ;
|
||||
- préparation du prompt suivant ;
|
||||
- préparation de la release finale.
|
||||
|
||||
La dernière prerelease ne doit pas servir à découvrir tardivement des scénarios Devnet qui n’auraient jamais été planifiés.
|
||||
|
||||
## Contrôles finaux
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo clippy --all-targets
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
cargo test --workspace
|
||||
cargo tauri dev -c kb-app-demo-desktop/tauri.conf.json
|
||||
```
|
||||
|
||||
Les validations Devnet doivent être exécutées selon les parcours documentés dans `kb-app-demo-desktop`, avec conservation des signatures, résultats de simulation, confirmations, postconditions et preuves de matérialisation pertinentes.
|
||||
Reference in New Issue
Block a user