381 lines
9.8 KiB
Markdown
381 lines
9.8 KiB
Markdown
<!-- file: docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md -->
|
||
<!-- version: 3 -->
|
||
|
||
# Data, Materialization et Store
|
||
|
||
## Objet
|
||
|
||
Ce document définit la chaîne durable KSP, la responsabilité du Store et les frontières de replay.
|
||
|
||
La nomenclature canonique est désormais :
|
||
|
||
```text
|
||
RAW -> CORE -> DECODE -> SPECIALIZED
|
||
```
|
||
|
||
Les aliases D1–D4 restent utilisés pour les niveaux persistés :
|
||
|
||
```text
|
||
D1 = RAW
|
||
D2 = CORE
|
||
D3 = DECODE / matérialisation générique décodée
|
||
D4 = SPECIALIZED
|
||
```
|
||
|
||
Cette clarification remplace l'ancienne interprétation où D1 -> D2 pouvait déjà dépendre de `ksp-program-api`. **RAW et CORE sont indépendants de tout decoder Program.**
|
||
|
||
## Principes structurants
|
||
|
||
- le Store persiste des contrats de données ; il ne possède ni transport, ni decoder, ni materializer ;
|
||
- chaque frontière durable peut être rejouée indépendamment ;
|
||
- les données de provenance/versioning permettent de savoir quel processor a produit quel output ;
|
||
- les couches dérivées ne rendent jamais obligatoire une nouvelle acquisition réseau lorsque l'input durable nécessaire existe déjà ;
|
||
- D4 privilégie les faits métier génériques lorsqu'une normalisation inter-protocoles est pertinente.
|
||
|
||
# D1 — RAW
|
||
|
||
## Mission
|
||
|
||
RAW conserve l'acquisition suffisamment fidèlement pour reconstruire CORE sans redemander la donnée au provider lorsqu'elle a déjà été capturée.
|
||
|
||
Le transport peut normaliser plusieurs providers vers un modèle KSP homogène, mais D1 doit rester lossless pour les besoins de replay couverts.
|
||
|
||
D1 ne décode aucun programme Solana/SPL/Metaplex/DEX.
|
||
|
||
## Frontière Transport -> RAW
|
||
|
||
```text
|
||
HTTP / WS / gRPC / provider
|
||
|
|
||
v
|
||
ksp-onchain-transport-lib
|
||
|
|
||
v
|
||
conversion explicite
|
||
|
|
||
v
|
||
ksp-store-api RAW DTO
|
||
|
|
||
v
|
||
ksp-store-lib
|
||
```
|
||
|
||
`ksp-onchain-transport-lib` ne dépend ni de `ksp-store-api` ni de `ksp-store-lib`.
|
||
|
||
## Provenance RAW
|
||
|
||
Selon la catégorie, D1 doit pouvoir conserver notamment :
|
||
|
||
- cluster/network ;
|
||
- slot/signature/pubkey/identité blockchain applicable ;
|
||
- payload replayable ;
|
||
- provider ;
|
||
- endpoint/source/transport ;
|
||
- rôle d'acquisition : live, backfill, import ;
|
||
- instant d'observation ;
|
||
- instant de persistence ;
|
||
- identité/hash d'idempotence ;
|
||
- cursor/page/range/checkpoint lorsque pertinent.
|
||
|
||
# D2 — CORE
|
||
|
||
## Mission
|
||
|
||
CORE est une **normalisation canonique générique de la blockchain Solana**.
|
||
|
||
Cette couche doit fonctionner même si `ksp-program-api` et `ksp-program-lib` ne sont pas encore capables de décoder le moindre programme métier.
|
||
|
||
Exemples de faits CORE candidats :
|
||
|
||
- slots ;
|
||
- blocks et block metadata ;
|
||
- signatures ;
|
||
- transactions ;
|
||
- messages legacy/versioned ;
|
||
- account keys et address lookups ;
|
||
- comptes et états bruts structurés génériquement ;
|
||
- instructions top-level brutes ;
|
||
- instructions CPI brutes ;
|
||
- logs ;
|
||
- transaction meta ;
|
||
- balances/fees/rewards lorsqu'ils appartiennent au contrat blockchain générique ;
|
||
- return data brute ;
|
||
- relations structurelles transaction/message/instruction/account.
|
||
|
||
Un fait CORE peut contenir un `program_id`, des bytes et des indexes sans savoir que l'instruction représente un `Transfer`, un `Swap` ou une mutation Metadata.
|
||
|
||
## Frontière RAW -> CORE
|
||
|
||
```text
|
||
D1 RAW
|
||
|
|
||
v
|
||
normalisation Solana générique
|
||
|
|
||
v
|
||
D2 CORE
|
||
```
|
||
|
||
Interdictions :
|
||
|
||
```text
|
||
RAW -> CORE -X-> ksp-program-api
|
||
RAW -> CORE -X-> ksp-program-lib
|
||
RAW -> CORE -X-> ksp-materializer-api
|
||
```
|
||
|
||
Les codecs/wires génériques nécessaires à la structure Solana peuvent provenir de `ksp-interface-lib` lorsqu'ils appartiennent à la façade wire officielle, sans transformer cette étape en décodage Program.
|
||
|
||
## Provenance CORE
|
||
|
||
D2 doit pouvoir relier chaque résultat à :
|
||
|
||
- son input D1 ;
|
||
- l'identité/version du normalizer CORE ;
|
||
- un hash logique d'input ;
|
||
- l'instant de processing/persistence ;
|
||
- son état de processing durable lorsque nécessaire.
|
||
|
||
# D3 — DECODE / matérialisation générique
|
||
|
||
## Mission
|
||
|
||
DECODE commence lorsque KSP interprète un `program_id`, un layout d'instruction, un compte ou un événement selon un contrat Program/protocole.
|
||
|
||
La progression logique d'un groupe est :
|
||
|
||
```text
|
||
CORE
|
||
|
|
||
v
|
||
decoder Program
|
||
|
|
||
v
|
||
decoded facts
|
||
|
|
||
v
|
||
materialisation générique / journal durable
|
||
|
|
||
v
|
||
D3 DECODE
|
||
```
|
||
|
||
D3 conserve l'équivalent conceptuel obligatoire du journal générique de matérialisation de bot3 (`k_sol_mat_outputs`), sans imposer son ancien schéma ou son nom physique.
|
||
|
||
Le journal doit pouvoir répondre au minimum :
|
||
|
||
```text
|
||
quel input CORE ?
|
||
quel program/decoder ?
|
||
quelle version ?
|
||
quel materializer ?
|
||
quelle version ?
|
||
quel output logique ?
|
||
quel type/domaine ?
|
||
quel hash ?
|
||
quel instant ?
|
||
quel état/superseded/failed/replay ?
|
||
```
|
||
|
||
Les types exacts de decoded facts et du journal sont décidés lorsque les premiers vertical slices Program existent.
|
||
|
||
## Frontière CORE -> DECODE
|
||
|
||
```text
|
||
D2 CORE
|
||
|
|
||
v
|
||
ksp-program-api implementation
|
||
|
|
||
v
|
||
decoded output
|
||
|
|
||
v
|
||
ksp-materializer-api implementation
|
||
|
|
||
v
|
||
D3 journal / decoded materialization
|
||
```
|
||
|
||
Les implémentations officielles pourront provenir de `ksp-program-lib` et `ksp-materializer-lib`; des implémentations externes compatibles restent possibles.
|
||
|
||
Program et Materializer ne dépendent pas du backend Store.
|
||
|
||
# D4 — SPECIALIZED
|
||
|
||
## Mission
|
||
|
||
SPECIALIZED expose des projections queryables utiles aux applications, analyses et futurs modèles ML.
|
||
|
||
Exemples :
|
||
|
||
- token/asset state ;
|
||
- metadata canonique d'asset ;
|
||
- pools/markets ;
|
||
- reserves/liquidity ;
|
||
- positions ;
|
||
- swaps/trades ;
|
||
- fees ;
|
||
- observations de prix ;
|
||
- OHLC/candles ;
|
||
- routes et legs ;
|
||
- faits trading-adjacent ;
|
||
- projections d'autres domaines futurs.
|
||
|
||
## Faits métier génériques
|
||
|
||
Les projections de trading ne sont pas séparées automatiquement par protocole.
|
||
|
||
Préférer lorsque possible :
|
||
|
||
```text
|
||
liquidity_pools
|
||
trades
|
||
positions
|
||
price_observations
|
||
ohlc
|
||
routes
|
||
route_legs
|
||
```
|
||
|
||
plutôt que :
|
||
|
||
```text
|
||
meteora_trades
|
||
raydium_trades
|
||
orca_trades
|
||
pump_trades
|
||
```
|
||
|
||
Les champs réellement protocol-specific peuvent être conservés dans une extension ou une projection dédiée uniquement lorsqu'un besoin de requête/invariant le justifie.
|
||
|
||
## Metadata
|
||
|
||
La direction reste :
|
||
|
||
- projection canonique commune pour metadata d'assets/tokens alimentée par Metaplex Token Metadata et Token-2022 Metadata ;
|
||
- SPM reste distinct et sera redéveloppé plus tard avec le décodage généraliste.
|
||
|
||
## OHLC
|
||
|
||
Les candles sont des projections SPECIALIZED calculées à partir des trades/price observations persistés.
|
||
|
||
Une application marché lit les OHLC matérialisés ; elle ne reparcourt pas toutes les transactions pour reconstruire les candles à chaque affichage.
|
||
|
||
# Vertical slices Program
|
||
|
||
RAW et CORE sont développés horizontalement.
|
||
|
||
À partir de DECODE, la progression est verticale par groupe :
|
||
|
||
```text
|
||
wire
|
||
-> decode
|
||
-> generic materialization / D3
|
||
-> specialized projection / D4 si utile
|
||
-> execution preparation
|
||
-> execution policy
|
||
-> execution
|
||
-> Devnet scenarios / validation
|
||
```
|
||
|
||
Un groupe doit atteindre une cohérence verticale suffisante avant que le groupe suivant devienne prioritaire.
|
||
|
||
Les composants satellites nécessaires à un protocole appartiennent à son groupe : Pump fees avec Pump, Meteora vaults avec Meteora, etc.
|
||
|
||
# `ksp-store-api`
|
||
|
||
`ksp-store-api` est backend-agnostic et porte les contrats nécessaires aux consommateurs.
|
||
|
||
La première implementation `0.3.1` est volontairement **RAW-only** : elle ne crée pas prématurément les contrats physiques D2/D3/D4.
|
||
|
||
Les surfaces CORE/DECODE/SPECIALIZED sont ajoutées quand leurs couches sont réellement ouvertes.
|
||
|
||
# `ksp-store-lib`
|
||
|
||
`ksp-store-lib` fournit PostgreSQL comme backend officiel de référence derrière `ksp-store-api`.
|
||
|
||
Il possède :
|
||
|
||
- migrations ;
|
||
- SQL ;
|
||
- transactions ;
|
||
- mapping backend ;
|
||
- pagination ;
|
||
- claim/lease lorsque nécessaire ;
|
||
- notifications backend si retenues.
|
||
|
||
Il ne possède pas :
|
||
|
||
- transport réseau ;
|
||
- decoder Program ;
|
||
- materializer ;
|
||
- orchestration de worker/job.
|
||
|
||
# `ksp-materializer-api` et `ksp-materializer-lib`
|
||
|
||
Ils sont introduits seulement lorsque le premier groupe DECODE démontre le contrat réel.
|
||
|
||
`ksp-materializer-api` porte les contrats extensibles ; `ksp-materializer-lib` contient les implementations officielles communes.
|
||
|
||
Une projection très locale/spécifique peut rester dans son groupe si la création d'une implémentation commune séparée n'apporte pas de réutilisation réelle.
|
||
|
||
# Replay
|
||
|
||
Les frontières durables restent replayables indépendamment :
|
||
|
||
```text
|
||
RAW -> CORE
|
||
CORE -> DECODE
|
||
DECODE -> SPECIALIZED
|
||
```
|
||
|
||
Un replay d'une couche dérivée ne doit pas refaire arbitrairement les couches précédentes.
|
||
|
||
# Notifications persistées
|
||
|
||
Le Store reste source de vérité du backlog.
|
||
|
||
Les notifications ne sont qu'un wake-up : elles peuvent être perdues ou dupliquées.
|
||
|
||
Ordre :
|
||
|
||
```text
|
||
persist
|
||
commit
|
||
notify
|
||
```
|
||
|
||
Le consumer reconstruit toujours son backlog depuis le Store avec les versions de processor et les marqueurs d'idempotence.
|
||
|
||
# Acquisition live et backfill
|
||
|
||
Live et backfill alimentent la même frontière RAW :
|
||
|
||
```text
|
||
live worker ----\
|
||
+--> RAW persistence
|
||
backfill job ----/
|
||
```
|
||
|
||
Ils ne dupliquent pas le contrat durable.
|
||
|
||
# Stabilité
|
||
|
||
La stabilité cible est différente selon la couche :
|
||
|
||
- RAW : fortement stable après mise en production ;
|
||
- CORE : fortement stable après validation de la normalisation Solana générique ;
|
||
- DECODE : extensible par nouveaux Program/versions ;
|
||
- SPECIALIZED : plus évolutif selon les besoins de query, trading et analytics.
|
||
|
||
# Questions laissées ouvertes
|
||
|
||
- schémas SQL exacts RAW puis CORE ;
|
||
- représentation persistable exacte d'un decoded output ;
|
||
- contrat exact du journal D3 ;
|
||
- granularité des projectors SPECIALIZED ;
|
||
- politique de supersession/versioning des outputs ;
|
||
- fenêtres OHLC initiales ;
|
||
- mécanisme de contexte pour les projections stateful.
|