17 KiB
Wire, Program API et implémentations Program
Objet
Ce document constitue la sortie principale de 0.0.3-pre.004.
Il précise :
- le rôle de
ksp-interface-lib; - la propriété des codecs wire ;
- la politique de sélection/réimplémentation des crates d'interface externes ;
- la forme générale et ouverte de
ksp-program-api; - la séparation decoder / préparation d'exécution ;
- le registry extensible ;
- l'organisation interne prévue de
ksp-program-lib; - le workflow d'une implémentation de programme développée hors de l'implémentation officielle.
Les types Rust exacts restent volontairement à définir lors de la première implémentation réelle.
ksp-interface-lib
ksp-interface-lib est la façade des contrats passifs KSP réellement partagés. Elle possède notamment les wires officiels nécessaires aux programmes Solana supportés et peut aussi posséder des faits d'acquisition provider-neutral lorsqu'ils ne sont ni des DTOs Transport ni des modèles persistants Store.
Elle possède ou réexporte de manière contrôlée les contrats nécessaires tels que :
- discriminants ;
- layouts d'instructions ;
- layouts de comptes ;
- enums/structures wire ;
- sérialisation/désérialisation wire ;
- règles et seeds de PDA lorsque ces règles appartiennent au contrat protocolaire ;
- constructeurs d'instructions lorsque ceux-ci représentent directement le contrat wire.
Les Program IDs fondamentaux restent possédés par ksp-core-lib.
ksp-interface-lib ne possède pas :
- RPC/WS/gRPC/provider DTOs ou sessions ;
- event bus, worker, job ou scheduler ;
- wallet/signature capability ;
- persistence, Store, cursor ou retention ;
- matérialisation ;
- interprétation canonique/métier d'une instruction ;
- policy/safety ;
- lifecycle d'exécution réseau.
Les événements d'acquisition admis dans Interface sont de simples faits passifs supplémentaires. Ils ne remplacent pas le DTO Transport riche et leurs conversions appartiennent à la composition qui connaît les deux contrats.
API publique de ksp-interface-lib
ksp-interface-lib reste une seule crate pour l'instant : aucune ksp-interface-api séparée n'est créée.
La façade doit néanmoins exposer une API publique stable et réutilisable par :
ksp-program-lib
external ksp-program-<name>-lib
compositions/acquisition consumers utilisant un fait passif partagé
Une implementation externe peut donc expérimenter contre les mêmes contrats wire publics avant intégration officielle dans KSP. Lorsqu'un wire externe devient officiel, son intégration dans ksp-interface-lib doit rester compatible avec l'API publique retenue, sauf évolution de contrat explicitement versionnée/documentée.
Si une future contrainte de dépendances démontre qu'un split ksp-interface-api apporte une valeur réelle, il pourra être étudié selon KSP-API-007; la symétrie avec Program ne suffit pas.
Événements passifs d'acquisition
Interface peut posséder un événement d'acquisition seulement si plusieurs sources/consumers partagent exactement le même fait et que le type reste plus petit que les DTOs producteurs. Le contrat ne doit pas accumuler des champs Option pour simuler l'union de protocoles hétérogènes.
La frontière durable est :
DTO Transport riche et lossless
|
| conversion explicite par composition/consumer
v
événement Interface passif provider-neutral
Un événement Interface ne devient ni un message de bus obligatoire ni une donnée durable. Les informations replayables appartiennent à ksp-store-api; les détails de transport, provider, commitment, session, timestamp source-specific et diagnostics qui ne font pas partie du fait commun restent Transport-owned.
Les familles actuellement démontrées sont le lifecycle de slot et le résultat d'exécution d'une transaction. Toute nouvelle famille exige un nouveau gate de convergence, consumer et bornes.
Propriété des codecs wire
Pour le code KSP officiel, les dépendances directement utilisées pour encoder/décoder les formats wire, notamment borsh, wincode ou codecs équivalents, appartiennent normalement à ksp-interface-lib.
La règle n'interdit pas qu'une autre crate KSP utilise un codec pour une raison indépendante du wire Solana, mais une telle dépendance directe doit avoir une justification distincte.
En particulier :
ksp-interface-lib
-> borsh / wincode / codecs wire nécessaires
ksp-program-lib
-X-> borsh / wincode pour redécoder directement le wire protocolaire
ksp-program-lib reçoit des contrats typés/wire exposés par la façade officielle au lieu de recréer la sérialisation protocolaire.
Politique de sélection des interfaces externes
Une crate externe d'interface Solana/Anza peut être utilisée directement lorsque :
- elle appartient à une source officielle ou suffisamment normative pour le contrat concerné ;
- sa surface est suffisamment stable et finale pour l'usage KSP ;
- son graphe de dépendances est compatible avec le stack de dépendances KSP actuel ;
- elle n'introduit pas sans nécessité une génération ancienne/incompatible d'un codec ou d'une primitive fondamentale ;
- son usage évite une réimplémentation KSP sans réduire le contrôle du contrat public.
Une crate protocolaire externe peut être rejetée même si son API fonctionne lorsqu'elle impose un stack ancien ou incompatible avec le reste du workspace.
Exemples observés pendant la planification
solana-loader-v3-interface illustre une interface officielle actuelle qui peut raisonnablement être conservée plutôt que réécrite lorsque sa version et son graphe restent compatibles avec KSP.
mpl-token-metadata illustre le cas inverse : KSP ne prévoit pas de l'intégrer comme dépendance runtime. Les contrats wire Metaplex Token Metadata nécessaires seront réimplémentés/possédés par ksp-interface-lib.
Ces exemples sont des applications de la règle, pas des exceptions codées en dur : l'état des dépendances doit être revérifié au moment de chaque implémentation.
Politique anti-doublons de générations
KSP ne cherche pas à figer arbitrairement une version précise des dépendances. Le projet cherche au contraire à rester sur des générations récentes et compatibles.
Les doublons de versions/générations de dépendances fondamentales doivent être inspectés régulièrement, notamment pour :
- codecs wire ;
- primitives Solana ;
- sérialisation ;
- bibliothèques cryptographiques fondamentales.
Un doublon réellement imposé et inévitable par une dépendance retenue peut être temporairement accepté avec justification.
Un doublon évitable provoqué par une crate protocolaire remplaçable doit être éliminé plutôt que normalisé comme dette permanente.
Outils d'audit attendus lorsque le workspace fonctionnel le permettra :
cargo tree -d
cargo tree -i borsh
cargo tree -i wincode
La liste sera complétée selon les dépendances réellement utilisées.
ksp-program-api
ksp-program-api porte des contrats publics ouverts.
Une implémentation externe doit pouvoir prendre en charge un Program ID encore inconnu de ksp-program-lib sans modifier une enum centrale KSP.
Pas d'enum fermée de programmes
Une structure de la forme suivante est explicitement évitée :
enum DecodedProgram {
Token(...),
Token2022(...),
Meteora(...),
...
}
car chaque nouveau protocole exigerait une modification de l'API centrale.
De même, un Any runtime non persistable ne peut pas constituer l'unique représentation d'un résultat décodé.
Le résultat doit rester :
- auto-identifiable ;
- extensible ;
- sérialisable/persistable lorsque la frontière Core l'exige ;
- exploitable par une implémentation externe de materializer ;
- compatible avec l'ajout de Program IDs sans modification de l'API commune.
La représentation exacte du payload ouvert sera définie avec les contrats Core/Store/Materializer réels, sans introduire prématurément une enum fermée.
Familles de decoders
Plutôt qu'un unique trait possédant toutes les surfaces possibles, ksp-program-api doit pouvoir distinguer les capacités de décodage.
Familles initiales candidates :
ProgramInstructionDecoder
ProgramAccountDecoder
ProgramEventDecoder
ProgramReturnDataDecoder
Les deux premières sont considérées comme besoins fondamentaux probables.
Les autres ne sont ajoutées que lorsqu'une première surface réelle le justifie.
Une implémentation de programme n'est pas obligée de fournir toutes les capacités.
Des descripteurs communs doivent permettre d'identifier les capacités effectivement supportées.
Politique de décodage historique
Le decoder vise toute surface techniquement décodable dont la définition est connue :
- actuelle ;
- ancienne ;
- legacy ;
- abandonnée ;
- expérimentale/test lorsque le wire est connu ;
- historique distinguable.
Le statut de lifecycle d'une opération n'empêche jamais son décodage historique.
Si une définition a réellement été écrasée sous une identité indistinguable et que l'ancienne forme ne peut plus être reconnue de manière fiable, la définition applicable la plus récente est utilisée.
Le concept deprecated n'est donc pas un filtre de decoder.
Préparation d'exécution Program
Le terme ProgramExecutor est abandonné dans le cadrage KSP parce qu'il suggère une exécution transactionnelle complète.
Le contrat prévu est nommé conceptuellement :
ProgramExecutionPreparer
Sa responsabilité :
ProgramExecutionRequest
|
v
ProgramExecutionPreparer
|
v
PreparedProgramExecution
Noms exacts des types à confirmer lors de l'implémentation.
Le preparer peut notamment :
- valider les paramètres protocolaires ;
- déterminer les comptes requis ;
- dériver les PDA nécessaires ;
- construire une ou plusieurs instructions ;
- indiquer les autorités/signers requis ;
- exposer les contraintes techniques propres à l'opération.
Il ne réalise pas :
- sélection du wallet réel ;
- accès aux secrets ;
- récupération du recent blockhash ;
- signature transactionnelle ;
- simulation RPC ;
- envoi réseau ;
- confirmation ;
- retry réseau ;
- policy/safety de produit.
Ces responsabilités appartiennent aux couches d'exécution supérieures.
PreparedProgramExecution
Le contrat préparé entre Program et Execution doit pouvoir transporter conceptuellement :
- identité du programme/opération ;
- instructions ;
- comptes/authorities/signers requis ;
- contraintes techniques ;
- informations nécessaires à une policy supérieure ;
- metadata de préparation utiles à l'exécution.
Il ne contient pas de secret wallet et ne fixe pas un endpoint réseau concret.
Il doit être consommable par ksp-execution-lib indépendamment du fait que l'implémentation Program provienne de ksp-program-lib ou d'une crate externe.
Lifecycle des opérations préparables
Le lifecycle d'une opération d'exécution doit être machine-readable.
Il faut distinguer au minimum conceptuellement :
- opération actuelle/supportée ;
- opération legacy/ancienne mais pas nécessairement déconseillée ;
- opération réellement abandonnée/déconseillée.
Les noms exacts des statuts seront définis plus tard.
Le statut « deprecated » est réservé à une surface clairement abandonnée/remplacée, pas simplement ancienne.
KSP ne rend pas obligatoire l'annotation Rust #[deprecated].
Une opération techniquement encore préparée mais marquée comme abandonnée peut :
- produire un warning explicite ;
- transmettre son statut à
ksp-execution-policy-api; - être autorisée ou rejetée par la policy supérieure selon le contexte.
Le decoder continue de la reconnaître indépendamment de ce statut.
Registry extensible
Le registry Program doit pouvoir combiner :
- implémentations officielles KSP ;
- implémentations externes ;
- implémentations expérimentales.
Les descripteurs doivent permettre d'identifier au minimum conceptuellement :
- Program ID ;
- identité/version de l'implémentation ;
- capacités de décodage ;
- opérations préparables ;
- statut/lifecycle des opérations ;
- autres capacités nécessaires au dispatch.
Le contrat exact du registry sera défini lors de l'implémentation.
Le registry ne doit pas nécessiter une modification de ksp-program-api pour enregistrer un nouveau Program ID.
Implémentations externes
Une extension externe de programme est une implémentation de ksp-program-api, pas une nouvelle API commune.
Nomenclature recommandée lorsque l'extension suit les conventions KSP :
ksp-program-<name>-lib
Exemple conceptuel :
ksp-program-fluxbeam-lib
et non :
ksp-program-fluxbeam-api
Cette crate peut dépendre de :
ksp-program-api
ksp-core-lib
ksp-interface-lib
selon les besoins.
Elle peut être développée, testée et utilisée par un runtime KSP sans être intégrée à ksp-program-lib.
Wire d'une extension encore non officielle
ksp-interface-lib est la façade wire officielle KSP, mais ksp-program-api ne doit pas empêcher une extension externe de fournir temporairement ses propres définitions wire.
Deux cas sont possibles :
interface officielle déjà dans KSP
-> extension utilise ksp-interface-lib
interface pas encore intégrée
-> extension possède sa définition wire compatible
-> extension implémente ksp-program-api
Lors de l'intégration officielle :
- les définitions wire sont revues/vérifiées ;
- les contrats wire nécessaires migrent vers
ksp-interface-lib; - decoder/preparer migrent vers
ksp-program-lib; - le contrat
ksp-program-apine change pas pour cette seule raison.
Organisation interne de ksp-program-lib
ksp-program-lib doit être organisé d'abord par domaine puis par programme/protocole, et seulement ensuite par capacité.
Direction retenue :
<domain>/
<program-or-protocol>/
dec/
...
exec_prep/
...
Exemples conceptuels :
spl/
token_2022/
dec/
exec_prep/
metadata/
token/
mpl/
dec/
exec_prep/
dex/
meteora/
dlmm/
dec/
exec_prep/
Les noms courts dec / exec_prep restent une convention candidate ; les noms précis seront décidés avec les premières vraies arborescences Rust.
Le principe durable est :
domain -> program/protocol -> capability
et non un répertoire racine géant decoder/ ou executor/ contenant tous les protocoles.
Conformité wire
Pour chaque contrat wire réimplémenté, la documentation/tests doivent permettre d'identifier la source normative utilisée.
Les validations peuvent combiner selon le cas :
- documentation/source officielle ;
- fixtures officielles ;
- transactions/comptes réels connus ;
- golden vectors ;
- comparaison avec une implémentation de référence lorsqu'elle peut être utilisée sans polluer durablement le graphe KSP.
Une crate externe provoquant volontairement une génération incompatible de dépendances fondamentales ne doit pas être ajoutée au workspace simplement pour faciliter un test si des fixtures/vecteurs indépendants permettent la même vérification.
Progression verticale par groupe
Après les couches RAW/STRUCTURAL, les Program implementations ne sont pas développées horizontalement comme une longue liste de decoders isolés. Chaque groupe prioritaire avance successivement :
wire -> decode -> materialize -> specialized -> execution preparation -> policy -> execution -> scenario
Un composant satellite nécessaire à un protocole reste dans son groupe : Meteora vaults avec Meteora, Pump fee avec Pump, etc.
Ordre prioritaire actuel : Solana Core Programs, SPL token/trading, token metadata, Anchor, Meteora, Raydium, Pump, Orca, routing, trading-adjacent puis décodage généraliste.
Questions laissées ouvertes
La première implémentation Program devra encore fixer précisément :
- formes Rust des traits decoder ;
- représentation du payload décodé ouvert/persistable ;
- forme exacte des descriptors ;
- types exacts
ProgramExecutionRequest/PreparedProgramExecution; - forme du registry et règles de conflit entre plusieurs implémentations d'un même Program ID ;
- stratégie précise de warning/log pour opérations abandonnées ;
- convention finale
dec/exec_prep.
Les releases exactes d'introduction de ksp-program-lib, ksp-execution-policy-api et ksp-execution-lib suivent désormais les vertical slices définis par le roadmap ; les responsabilités Program décrites ici restent valables.