v0.3.9-pre.004

This commit is contained in:
2026-09-04 21:23:46 +02:00
parent bb7ee3cc3a
commit eb39aaa904
7 changed files with 362 additions and 27 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/000-README.md -->
<!-- version: 10 -->
<!-- version: 11 -->
# Architecture KSP
@@ -27,5 +27,6 @@ Ils ne remplacent ni `ROADMAP.md`, ni les plans de version, ni les deltas.
8. [`008-DATA_MATERIALIZATION_AND_STORE.md`](008-DATA_MATERIALIZATION_AND_STORE.md) — niveaux durables D1D4, Materialization, Store PostgreSQL de référence, provenance, idempotence, replay et notifications de données persistées ;
9. [`009-ACQUISITION_WORKERS_AND_JOBS.md`](009-ACQUISITION_WORKERS_AND_JOBS.md) — pipelines spécialisés, workers live, jobs de backfill/replay, backlog, claim/lease, reprise, concurrence et mécanisme de notification de référence ;
10. [`010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md`](010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md) — apps spécialisées, workers autonomes et need-driven, control plane, scenarios réutilisables, demos desktop et frontières IPC/orchestration.
11. [`011-RAW_TRANSACTION_ACQUISITION.md`](011-RAW_TRANSACTION_ACQUISITION.md) — taxonomie durable d'acquisition `RawTransaction`, audits datés des voies Solana/providers, convergence Store et handoff Worker/Backfill.
`004-COMPONENT_INVENTORY.md` et `005-DEPENDENCY_GRAPH.md` sont maintenus ensemble : une évolution du graphe qui change le propriétaire d'une responsabilité doit corriger l'inventaire au lieu de laisser deux descriptions contradictoires.

View File

@@ -0,0 +1,269 @@
<!-- file: docs/architecture/011-RAW_TRANSACTION_ACQUISITION.md -->
<!-- version: 1 -->
# Acquisition RAW Transaction
## 1. Rôle
Ce document est l'owner durable de la taxonomie d'acquisition `RawTransaction` de KSP. Il sépare volontairement :
- les invariants durables de convergence vers Store ;
- les rôles/capabilities d'acquisition ;
- les audits datés de protocoles/providers ;
- les gaps à transmettre aux releases d'implémentation.
Il ne définit ni un endpoint secret, ni un quota provider figé, ni une configuration utilisateur. Les disponibilités et limites externes sont réauditées à la date indiquée dans chaque section d'audit.
## 2. Invariants durables
La convergence RAW reste indépendante de la source :
```text
identité canonique transaction = (network, signature)
contenu canonique = source-independent
acquisition utile = RawTransactionObservation distincte
provider/protocole/endpoint = provenance, jamais identité transactionnelle
même identité + même contenu = idempotence
même identité + contenu divergent = conflit explicite
```
Une source n'est admise pour persister un `RawTransaction` que si KSP peut reconstruire le payload canonique complet attendu par Store. Un signal incomplet reste une discovery et doit être hydraté par une autre capability.
## 3. Taxonomie de rôles
| Rôle | Responsabilité | Exemple standard actuel |
|-------------------------|-----------------------------------------------------------|------------------------------------------------------------------|
| `live_direct_full` | transaction/bloc complet reçu directement | WS blockSubscribe aujourdhui ; autres sources en pre.005 |
| `live_discovery` | référence/signature reçue puis hydration séparée | WS logsSubscribe |
| `targeted_confirmation` | signature déjà connue surveillée jusquau commitment | WS signatureSubscribe |
| `history_discovery` | énumération de références historiques | HTTP getSignaturesForAddress ; HTTP getBlocks/getBlocksWithLimit |
| `hydration` | récupération de transaction complète depuis une référence | HTTP getTransaction |
| `gap_boundary` | détermination dune fenêtre ou dune limite de rétention | getSlot/getFirstAvailableBlock/minimumLedgerSlot |
| `gap_repair` | réacquisition explicite après perte de continuité | HTTP bloc ou adresse selon le scope disponible |
Ces rôles sont orthogonaux au protocole. Une stratégie concrète peut combiner plusieurs rôles et plusieurs transports ; la configuration future ne doit pas réduire le modèle à `Http | WebSocket | Grpc`.
## 4. Audit daté A — Solana standard
Audit effectué le **4 septembre 2026** à partir de la documentation RPC Solana courante et de la surface KSP `0.3.9-pre.3`.
### 4.1 Matrice fonctionnelle
| Voie | Capability | RAW complet | Temporalité utile | Décision | État KSP |
|------------------------------------------------------|------------------------------------|----------------------------------|-----------------------------------------|------------------------|-----------------------------------------------------------------------------------|
| HTTP `getSignaturesForAddress` + `getTransaction` | discovery adresse + hydration | non / oui | historique, catch-up, repair ciblé | ADMIS | déjà utilisé par Backfill ; `getTransaction` observé conserve provider + endpoint |
| HTTP `getBlocks` / `getBlocksWithLimit` + `getBlock` | discovery slots/blocs + extraction | non / oui | catch-up global, gap repair, historique | ADMIS SOUS GAP | `getBlock` typé existe mais pas de variante observée |
| HTTP `getSlot` | borne haute selon commitment | non | pilotage catch-up / gap | AUXILIAIRE | surface typée KSP disponible |
| HTTP `getFirstAvailableBlock` | borne basse des blocs conservés | non | admission historique / diagnostic | AUXILIAIRE | surface typée KSP disponible |
| HTTP `minimumLedgerSlot` | borne basse ledger du nœud | non | diagnostic de rétention/provider | AUXILIAIRE | surface typée KSP disponible |
| WS `logsSubscribe` | discovery live par logs | non | live + signal de gap | ADMIS | hydration `getTransaction` obligatoire pour RAW complet |
| WS `signatureSubscribe` | suivi dune signature déjà connue | non | confirmation/repair ciblé | SECONDAIRE | one-shot ; ne découvre pas un flux de transactions |
| WS `blockSubscribe` | bloc live contenant transactions | oui si `transactionDetails=full` | live / catch-up très court | ADMIS SOUS CONTRAINTES | méthode Solana instable + activation validator requise |
| WS `slotSubscribe` | signal slot/parent/root | non | détection/pilotage de gap | AUXILIAIRE | stable mais ne transporte aucune transaction |
| WS `slotsUpdatesSubscribe` | lifecycle détaillé de slot | non | diagnostic de continuité | OPTIONNEL | méthode Solana instable ; pas une source RAW directe |
### 4.2 HTTP adresse : discovery + hydration
`getSignaturesForAddress` retourne des signatures confirmées associées à une adresse, ordonnées du plus récent au plus ancien, avec `before`, `until`, `limit`, `commitment` et `minContextSlot`. La discovery est donc naturellement **adressée** : elle ne constitue pas une discovery globale de toutes les transactions du réseau.
`getTransaction` retourne ensuite la transaction confirmée complète ou `null`. Pour KSP, cette combinaison est la référence actuelle du Backfill et reste admise pour :
```text
historique adressé
catch-up adressé
repair ciblé
hydration d'un signal WS qui connaît déjà la signature
```
Elle n'est pas retenue comme source live globale par polling.
### 4.3 HTTP bloc : scan global par slots
`getBlocks` et `getBlocksWithLimit` énumèrent des slots confirmés ; la documentation courante borne leurs ranges/limits à 500 000. `getBlock` peut retourner les transactions complètes du bloc avec `confirmed` ou `finalized` et les options de détail/encodage.
Cette famille est admise comme stratégie potentielle de :
```text
catch-up global par slots
gap repair global
historique lorsque le provider conserve la plage demandée
```
Elle n'est pas équivalente à `getSignaturesForAddress` : la discovery est par slot/bloc et l'hydration est un conteneur de plusieurs transactions. Le worker doit extraire chaque transaction, reconstruire son identité `(network, signature)` et produire une observation propre à chaque acquisition durable.
### 4.4 Bornes de continuité HTTP
`getSlot`, `getFirstAvailableBlock` et `minimumLedgerSlot` ne produisent aucune transaction. Ils servent à décider si un gap est encore réparable sur un endpoint et à borner un scan :
```text
getSlot -> borne haute selon commitment
getFirstAvailableBlock -> premier bloc confirmé encore disponible
minimumLedgerSlot -> plus ancien slot encore présent dans le ledger du nœud
```
Ces méthodes ne prouvent pas à elles seules qu'une transaction précise est disponible ; elles sont des primitives d'admission/diagnostic.
### 4.5 WS logsSubscribe : discovery live
`logsSubscribe` peut écouter `all`, `allWithVotes` ou les transactions mentionnant **un seul** pubkey par appel. La notification KSP contient le contexte de slot, la signature, l'erreur nullable et les logs ordonnés.
Cette notification n'est pas un `RawTransaction` complet. Elle est admise comme **live discovery** :
```text
logsSubscribe notification
-> signature + slot
-> getTransaction observed
-> normalisation RAW
-> persistence transaction + observation
```
Le filtre `mentions` limité à une adresse par subscription implique que la surveillance de nombreux programmes/comptes consomme plusieurs subscriptions, sauf utilisation de `all`/`allWithVotes` avec filtrage côté consumer.
### 4.6 WS signatureSubscribe : confirmation ciblée
`signatureSubscribe` surveille une signature déjà connue et s'arrête automatiquement après la notification terminale au commitment demandé. Il ne découvre donc aucune nouvelle transaction.
Décision : **capability secondaire** pour confirmation/repair ciblé, jamais source principale d'ingestion. Une hydration reste nécessaire pour persister le RAW complet.
### 4.7 WS blockSubscribe : direct full sous contraintes
`blockSubscribe` peut fournir les blocs `confirmed`/`finalized`, filtrés par `all` ou par `mentionsAccountOrProgram`. Avec `transactionDetails=full`, il peut transporter directement les transactions nécessaires à la normalisation RAW.
La méthode reste toutefois documentée comme **instable** par Solana et n'est disponible que si le validator active explicitement la block subscription ainsi que l'historique transactionnel requis. KSP doit donc la traiter comme capability annoncée/testée par endpoint, pas comme propriété universelle de tout endpoint WS standard.
### 4.8 slotSubscribe et slotsUpdatesSubscribe
`slotSubscribe` fournit slot/parent/root et peut aider le worker à détecter que la chaîne avance. `slotsUpdatesSubscribe` fournit un lifecycle de slot plus détaillé, mais reste officiellement instable.
Aucune de ces voies ne transporte une transaction. Elles restent auxiliaires pour continuité/diagnostic et ne justifient jamais une observation Store `RawTransaction` seules.
### 4.9 Reconnect, ordering et gaps
Le protocole standard ne transforme pas une reconnexion en replay des notifications perdues. KSP Transport sait resouscrire les subscriptions logiques après reconnexion et expose un compteur de gaps de continuité, mais cela ne prouve pas quels slots/transactions ont été manqués.
Invariants de composition retenus :
```text
reconnect WS != replay
duplicate notification = acceptable et dédupliquée par identité/contenu/observation
gap détecté = déclenche une stratégie de repair distincte
repair = HTTP slot/block ou HTTP adresse selon le scope connu
```
## 5. Inventaire KSP existant
### 5.1 Transport
| Surface KSP | Disponible | Filtres / entrée | Provenance / remarque |
|------------------------------------------------------------------|---------------|-----------------------------------------|--------------------------------------------------------------|
| `get_signatures_for_address` | oui | adresse + before/until/limit/commitment | aucune observation durable nécessaire pour la discovery |
| `get_transaction_observed` | oui | signature + config | provider + endpoint gagnant sûrs |
| `get_blocks` / `get_blocks_with_limit` | oui | range/limit + commitment | discovery de slots uniquement |
| `get_block` | oui | slot + config | GAP : pas de provider/endpoint gagnant observé |
| `get_slot` / `get_first_available_block` / `minimum_ledger_slot` | oui | bornes de continuité | auxiliaires, sans payload RAW |
| `logs_subscribe` | oui | all / allWithVotes / mentions(1 pubkey) | session snapshot fournit endpoint/provider/cluster/protocole |
| `signature_subscribe` | oui | signature connue + commitment | one-shot, terminal côté serveur |
| `block_subscribe` | oui, instable | all / mentionsAccountOrProgram + config | session snapshot fournit provenance de session |
| `slot_subscribe` / `slots_updates_subscribe` | oui | aucun filtre | signaux de continuité uniquement |
Le runtime WS possède déjà des queues bornées. Une overflow d'une subscription lente est terminale pour cette subscription plutôt que de créer un backlog non borné. Le worker devra traiter cette terminaison comme une cause potentielle de gap et réparer avant de déclarer la continuité retrouvée.
### 5.2 Store
Store possède déjà les invariants nécessaires à la convergence multi-source :
```text
RawTransactionReference = network + signature
RawTransaction = reference + slot + block_time + payload canonique
RawTransactionObservation
RawAcquisitionProvenance
RawTransactionWrite::persist_raw_transaction_acquisition
RawTransactionObservationWrite::record_raw_transaction_observation
```
`RawAcquisitionProvenance` sait déjà conserver de manière sûre : provider, protocole, méthode, origin, endpoint logique, commitment, filter id, capture session id, timestamps et hash/taille du payload source. Aucun DTO Transport ne doit être persistant directement.
### 5.3 Normalisation RAW actuelle
Le premier canonicalizer RAW v1 est actuellement implémenté dans `ksp-job-backfill-lib::conversion` autour de `getTransaction` observé. Cette implémentation a démontré le format, le hash canonique, la provenance et la persistence du vertical slice Backfill, mais son ownership Job n'est pas réutilisable comme dépendance du futur Worker.
Décision de cette tranche : **ne pas copier** cette logique dans `0.3.10`. Le handoff final devra choisir une ownership commune ou une extraction compatible avec les frontières de dépendances.
### 5.4 Config et réseaux
| Profil | Cluster Transport | Surfaces standard | État |
|-------------------------|-------------------|---------------------------|------------------------------------|
| `devnet_public` | devnet | HTTP + WS standard | déjà présent |
| `mainnet_public` | mainnet-beta | HTTP + WS standard | déjà présent |
| `mainnet_backfill_pool` | mainnet-beta | HTTP pool + WS standard | déjà présent ; rôles backfill HTTP |
| `publicnode_mainnet` | mainnet-beta | HTTP + WS standard + gRPC | gRPC traité en pre.005 |
| `publicnode_testnet` | testnet | HTTP + WS standard + gRPC | gRPC traité en pre.005 |
Les URLs et secrets restent exclusivement Config-owned. `0.3.9` n'ajoute aucun endpoint ni secret.
#### Canonicalisation Mainnet
L'audit interne montre une distinction existante :
```text
profile/composite id : mainnet
network / cluster : mainnet-beta
```
`std.store.json` et `std.transport.json` utilisent déjà `mainnet-beta` comme identité réseau/cluster. Quelques exemples/tests de Backfill utilisent encore `RawNetworkId("mainnet")` comme valeur synthétique. Pour l'ingestion durable, la direction retenue est de conserver **`mainnet-beta` comme identité réseau canonique** et de réserver `mainnet` aux identifiants de profil/UI lorsque nécessaire. Aucune migration n'est effectuée en `pre.004`.
### 5.5 Interface passive
`ksp-interface-lib` possède `TransactionExecutionEvent` et `SlotLifecycleEvent`, utiles lorsque plusieurs producers/consumers partagent exactement ces faits passifs. Ils ne remplacent ni les DTOs Transport riches ni `RawTransaction`/`RawTransactionObservation` et ne doivent pas être utilisés comme conteneurs d'acquisition génériques.
## 6. Gaps préparatoires identifiés en pre.004
| ID | Owner futur | Gap / décision à matérialiser | Release cible | Motif |
|-------|-------------------------|---------------------------------------------------------------------------------------------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------|
| TR-A | Transport | ajouter une voie observée pour `getBlock` si le scan bloc est admis en V1 | 0.3.10 | nécessaire pour provider/endpoint exacts dans `RawTransactionObservation` avec pool multi-endpoint |
| TR-B | Transport/ingest | définir lextraction déterministe transaction-par-transaction depuis `SolanaConfirmedBlock` | 0.3.10 | le DTO bloc existe, la conversion RAW transactionnelle commune nexiste pas encore |
| RAW-A | Architecture/code owner | sortir la normalisation RAW v1 de lenfermement Backfill sans duplication | 0.3.10 | la logique canonique actuelle vit dans `ksp-job-backfill-lib::conversion` |
| REC-A | Worker ingest | associer reconnect WS à une stratégie explicite de gap repair | 0.3.10 | resubscribe != replay ; `continuity_gap_count` nidentifie pas les transactions manquées |
| CFG-A | Config/composition | exprimer des rôles/capabilities dacquisition, pas un simple enum HTTP/WS/gRPC | 0.3.10 | les profils existent déjà mais le rôle ingest multi-source nest pas matérialisé |
| NET-A | Naming | retenir `mainnet-beta` comme identité réseau durable/configurée et `mainnet` seulement comme profile/UI alias | 0.3.10 / 0.3.12 | Store + Transport Config utilisent déjà `mainnet-beta`; quelques exemples/tests Backfill utilisent encore `mainnet` |
Ces gaps sont des entrées de handoff. `pre.004` ne les implémente pas.
## 7. Sources externes de l'audit daté
Consultées le 4 septembre 2026 :
| Source Solana | URL |
|-------------------------|-------------------------------------------------------------|
| getSignaturesForAddress | https://solana.com/docs/rpc/http/getsignaturesforaddress |
| getTransaction | https://solana.com/docs/rpc/http/gettransaction |
| getBlocks | https://solana.com/docs/rpc/http/getblocks |
| getBlocksWithLimit | https://solana.com/docs/rpc/http/getblockswithlimit |
| getBlock | https://solana.com/docs/rpc/http/getblock |
| getSlot | https://solana.com/docs/rpc/http/getslot |
| getFirstAvailableBlock | https://solana.com/docs/rpc/http/getfirstavailableblock |
| minimumLedgerSlot | https://solana.com/docs/rpc/http/minimumledgerslot |
| logsSubscribe | https://solana.com/docs/rpc/websocket/logssubscribe |
| signatureSubscribe | https://solana.com/docs/rpc/websocket/signaturesubscribe |
| blockSubscribe | https://solana.com/docs/rpc/websocket/blocksubscribe |
| slotSubscribe | https://solana.com/docs/rpc/websocket/slotsubscribe |
| slotsUpdatesSubscribe | https://solana.com/docs/rpc/websocket/slotsupdatessubscribe |
Les quotas, tiers provider et capacités Helius/Yellowstone ne sont volontairement pas documentés ici ; ils appartiennent à l'audit `pre.005`.
## 8. État après pre.004
Décisions fermées pour le standard Solana :
```text
HTTP address discovery + hydration = admis historique/catch-up/repair
HTTP block scan = admis sous gap de provenance observée
WS logsSubscribe = admis comme live discovery + hydration
WS signatureSubscribe = secondaire, signature déjà connue
WS blockSubscribe = admis sous capability explicite et instabilité
slot/ledger methods = auxiliaires de continuité
reconnect WS = jamais considéré comme replay
mainnet-beta = identité réseau canonique à préserver
```
Restent ouverts pour `pre.005`/`pre.006` : Helius, Yellowstone, autres providers, replay provider-specific, quotas/tier, stratégie multi-source V1 exacte et décision finale d'ownership du canonicalizer RAW.