# Store transactionnel Solana canonique ## Historique `0.2.3` a créé deux tables minimales : ```text kb_sol_raw_rpc_transactions kb_sol_raw_ws_notifications ``` Ces tables restent un jalon historique validé dans `CHANGELOG.md`. La transition a été appliquée et contrôlée sur PostgreSQL réel pendant `0.3.1`. Le modèle actif est : ```text kb_sol_raw_transactions kb_sol_obs_transaction_observations ``` ## Objectif cible Le store principal conserve une transaction Solana source-indépendante par signature. Les sources HTTP, WebSocket Helius et Yellowstone gRPC ne doivent pas produire plusieurs copies complètes du même contenu. ```text source provider -> normalisation canonique -> transaction unique -> observations multiples ``` ## Table `kb_sol_raw_transactions` Rôle : stocker la représentation canonique rejouable de la transaction et de ses metadata. Colonnes principales visées : | Colonne | Rôle | |----------------------------|-----------------------------------------| | `id` | clé primaire technique | | `signature` | signature Solana unique | | `slot` | slot de la transaction | | `canonical_format_version` | version du contrat canonique | | `canonical_json` | transaction et metadata normalisées | | `canonical_json_hash` | hash déterministe du document canonique | | `retention_state` | état de rétention | | `processing_state` | état d’extraction et de traitement | | `lifecycle_reason` | raison technique optionnelle | | `created_at` | première insertion | | `updated_at` | dernière évolution autorisée | Le contenu canonique ne contient pas de nom de fournisseur, endpoint, subscription id ou format de transport. `kb_model::CanonicalTransaction` fournit la sérialisation déterministe et le hash SHA-256. `kb_store_core::RawTransactionInsert::from_canonical` construit le DTO de persistance avec la signature primaire, le slot, `canonical_format_version`, `canonical_json` et `canonical_json_hash`. Le transport RPC ne dépend donc pas du backend PostgreSQL. ## Table `kb_sol_obs_transaction_observations` Rôle : enregistrer chaque détection ou acquisition d’une signature sans recopier le payload complet. Colonnes principales visées : | Colonne | Rôle | |-----------------------|-------------------------------------------------------| | `id` | clé primaire technique | | `observation_key` | clé idempotente de l’observation | | `raw_transaction_id` | lien optionnel vers `kb_sol_raw_transactions` | | `signature` | signature détectée ou reçue | | `slot` | slot quand connu | | `provider` | fournisseur | | `endpoint_code` | endpoint de configuration | | `protocol` | protocole de transport | | `acquisition_method` | méthode ou type de stream | | `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | | `commitment` | commitment demandé ou reçu | | `capture_session_id` | session de mesure ou d’ingestion | | `filter_code` | filtre logique utilisé | | `detected_at` | premier signal reçu | | `received_at` | transaction complète reçue | | `normalized_at` | fin de normalisation | | `persisted_at` | fin de persistance | | `payload_size_bytes` | taille du message source | | `source_payload_hash` | hash optionnel du message source | | `status` | résultat technique | | `error_code` | erreur normalisée optionnelle | | `error_message` | message de diagnostic optionnel | Cette table ne possède pas de `raw_json`, de `canonical_json` ni de payload Protobuf complet. ## Notifications WebSocket `kb_sol_raw_ws_notifications` n’est plus conservée comme cible durable. Pour `logsSubscribe` : 1. recevoir la signature et le slot ; 2. mémoriser temporairement le timestamp de détection ; 3. hydrater par `getTransaction` si nécessaire ; 4. écrire la transaction canonique ; 5. écrire une observation avec `detected_at`, `received_at` et le statut d’hydratation. Les payloads complets de notifications peuvent être exportés temporairement pour un benchmark borné, mais ils ne doivent pas être dupliqués en PostgreSQL. ## Fusion par signature La signature est la clé d’unicité de la transaction canonique. - hash identique : conserver la ligne et ajouter une observation ; - metadata plus complètes : appliquer un enrichissement déterministe ; - différence incompatible : enregistrer un conflit et ne pas écraser silencieusement ; - transaction absente ou erreur provider : écrire uniquement l’observation technique. ## Cycle de vie Les états de rétention de la transaction canonique restent : ```text full compacted archived purged ``` Les états de traitement restent : ```text received core_extracted decoded materialized failed ``` Les observations sont append-only et suivent une rétention technique distincte. ## Baseline SQL active Après validation de la transition historique, le dossier `kb_store_pg/migrations/` a été consolidé : ```text 0001_canonical_transaction_store.sql 0002_core_store.sql ``` La baseline ne contient que les tables et colonnes actives. Les anciens noms RPC/WS ne sont plus recréés. Le DDL runtime conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`.