# Sélection et replay de l’extraction core ## Objet Ce document décrit les modes disponibles dans `demo_core_extraction`, leur effet réel sur PostgreSQL et la différence entre : - acquisition d’une transaction depuis le réseau ; - extraction du document canonique vers les tables core ; - futur décodage protocolaire ; - future matérialisation métier. Dans `0.3.4`, le mot « replay » désigne uniquement le rejeu de l’étape suivante : ```text kb_sol_raw_transactions.canonical_json -> kb_model::CanonicalTransaction -> CoreExtractionBundle -> kb_sol_core_transactions -> kb_sol_core_account_keys -> kb_sol_core_instructions -> kb_sol_core_inner_instructions -> kb_sol_core_logs -> kb_sol_core_balance_changes -> kb_sol_ops_processing_ledger ``` Aucun appel RPC n’est effectué par cette étape. Aucun décodeur Solana Core, SPL, DEX ou autre protocole n’est exécuté. Aucune ligne métier n’est matérialisée. ## Cycle complet d’une transaction Le cycle prévu est séparé en étapes rejouables : ```text réseau RPC -> acquisition HTTP ou WebSocket -> transaction canonique raw -> extraction structurelle core -> observation et classification -> décodage protocolaire -> matérialisation métier -> agrégations et stratégies ``` `0.3.4` couvre uniquement la transition `raw canonique -> core structurel`. Les lignes de `kb_sol_core_instructions` sont créées avec `processing_state = pending`. Elles constituent l’entrée des futurs décodeurs prévus à partir de `0.4.x`. ## Paramètres communs ### Transactions maximales `limit` borne le nombre de transactions sélectionnées par PostgreSQL avant démarrage de la campagne. La sélection est ordonnée par : ```text slot ASC, signature ASC ``` Cette règle s’applique aussi au mode signatures explicites. L’ordre des lignes collées dans la zone de texte n’est donc pas l’ordre d’exécution garanti. ### Concurrence `max_concurrent_extractions` borne le nombre d’extractions admises simultanément. Chaque signature reste une unité transactionnelle indépendante. Une transaction Solana produit son graphe core dans une transaction PostgreSQL atomique. ### Force replay Sans `force_replay`, une signature est ignorée lorsque le ledger contient déjà un succès ayant exactement : ```text stage = core_extraction processor_name = canonical_to_core processor_version = version courante input_key = signature input_hash = canonical_json_hash courant status = succeeded ``` Avec `force_replay`, ce skip est désactivé. Le graphe core existant de la signature est supprimé puis recréé dans la même transaction PostgreSQL. Les autres signatures ne sont pas touchées. Le force replay ne relance pas `getTransaction` et ne remplace pas le document canonique raw. Il reconstruit uniquement le core depuis le raw déjà présent. ## Mode signatures explicites ### Entrée La zone de texte accepte une signature par ligne. Avant l’appel au pipeline, l’interface : - supprime les espaces autour de chaque ligne ; - ignore les lignes vides ; - supprime les doublons en conservant la première occurrence ; - valide chaque valeur comme signature Solana. ### Sélection PostgreSQL Le mode sélectionne uniquement les signatures déjà présentes dans `kb_sol_raw_transactions`. Une signature absente de la table raw n’est pas téléchargée automatiquement. Elle n’apparaît simplement pas dans les candidats sélectionnés. Le compteur `selected` peut donc être inférieur au nombre de signatures saisies. Aucun filtre de `processing_state` n’est appliqué. Ce mode peut sélectionner une transaction raw : - `received` ; - `core_extracted` ; - `failed`. Le ledger décide ensuite entre skip, extraction ou retry. ### Usages principaux - vérifier l’idempotence sur un échantillon connu ; - forcer la reconstruction de quelques signatures ; - retenter explicitement des transactions raw en échec ; - comparer deux versions de l’extracteur ; - préparer plus tard un corpus ciblé pour un décodeur. ### Point d’attention Si le nombre de signatures existantes dépasse `limit`, seules les premières selon `slot ASC, signature ASC` sont retenues. ## Mode transactions en attente ### Filtre réel Le mode pending sélectionne : ```text kb_sol_raw_transactions.processing_state = received ``` Il ne sélectionne ni les lignes déjà `core_extracted`, ni les lignes `failed`. ### Transition après succès Après commit du graphe core : ```text received -> core_extracted ``` La même ligne ne sera donc plus sélectionnée lors d’une campagne pending suivante. ### Transition après échec Après un échec d’extraction persistable : ```text received -> failed ``` La raison est stockée dans `lifecycle_reason` et le ledger reçoit un statut `failed`. Une ligne `failed` doit être retentée par signature explicite ou par un futur mode dédié aux erreurs. Une nouvelle campagne pending ne la reprend pas. ### Usages principaux - vider progressivement la file des transactions canoniques nouvellement acquises ; - exécuter l’extraction courante après un backfill HTTP ; - traiter un lot borné sans sélectionner manuellement les signatures. ### Conséquence sur le corpus actuel Après la campagne réelle de 70 transactions : ```text selected = 70 extracted = 70 failed = 0 ``` les 70 lignes raw sont normalement passées à `core_extracted`. Une nouvelle campagne pending doit donc sélectionner zéro transaction tant qu’aucune nouvelle transaction raw `received` n’a été acquise. ## Mode plage de slots ### Filtre réel Le mode sélectionne toutes les transactions raw dont le slot se trouve dans l’intervalle inclusif : ```text min_slot <= slot <= max_slot ``` Aucun filtre de `processing_state` n’est appliqué. ### Comportement normal Les transactions déjà à jour sont sélectionnées puis comptées comme `skipped` par le ledger. Les transactions reçues, en échec, ou dont la version/hash a changé peuvent être extraites. ### Comportement avec force replay Toutes les transactions sélectionnées sont reconstruites dans la limite configurée. ### Usages principaux - rejouer un intervalle temporel connu ; - vérifier une régression sur un bloc ou une période ; - reconstruire un corpus après changement de version de l’extracteur. ## Mode programme déjà indexé dans core ### Filtre réel actuel Le mode sélectionne les signatures raw pour lesquelles il existe déjà une ligne correspondante dans : ```text kb_sol_core_instructions ``` avec un `program_id` exactement égal à la valeur demandée. La requête actuelle ne recherche pas dans : - `kb_sol_core_inner_instructions` ; - `kb_sol_core_logs` ; - `kb_sol_core_account_keys` ; - le document canonique raw. ### Conséquences Ce mode ne peut pas découvrir un programme pour la première fois. Il fonctionne uniquement après une première extraction core ayant déjà résolu au moins une instruction top-level de ce programme. Une transaction où le programme apparaît exclusivement en inner instruction n’est pas sélectionnée par ce mode dans son état actuel. ### Usages principaux - reconstruire les transactions d’un programme déjà indexé après changement de version ; - vérifier une correction de résolution des comptes ou instructions ; - préparer un sous-corpus structurel avant le futur replay d’un décodeur. ### Évolution recommandée Le futur sélecteur de corpus devrait distinguer explicitement : - programme top-level ; - programme inner ; - programme observé dans les logs ; - union de toutes les occurrences core fiables. Le mode d’extraction actuel peut rester strictement basé sur les instructions top-level, à condition que cette limite demeure visible dans l’interface. ## Interprétation des compteurs | Compteur | Signification | |-----------------------|-------------------------------------------------------------| | `selected` | Candidats retournés par PostgreSQL après filtres et limite. | | `started` | Candidats admis dans la file d’exécution bornée. | | `completed` | Candidats arrivés à un résultat terminal. | | `skipped` | Ledger déjà à jour pour la même version et le même hash. | | `extracted` | Graphe core écrit et commit réussi. | | `failed` | Extraction ou persistance terminée en erreur. | | `cancelledCandidates` | Candidats admis mais annulés avant résultat terminal. | | `notStarted` | Candidats sélectionnés mais jamais admis après arrêt. | | `cancelled` | Une demande d’arrêt coopératif a été observée. | Pour une campagne terminée normalement : ```text completed = skipped + extracted + failed selected = completed + cancelledCandidates + notStarted ``` ## Scénarios de validation recommandés ### Vérifier le skip 1. Extraire 5 à 10 signatures déjà présentes dans core depuis un outil de sélection. 2. Les coller dans le mode signatures explicites. 3. Laisser `force_replay` désactivé. Résultat attendu : ```text selected = N skipped = N extracted = 0 failed = 0 ``` ### Vérifier le force replay 1. Réutiliser exactement les mêmes signatures. 2. Activer `force_replay`. Résultat attendu : ```text selected = N skipped = 0 extracted = N failed = 0 ``` Les cardinalités globales des tables core doivent rester stables pour ces signatures. Le `attempt_count` du ledger doit augmenter. ### Vérifier un changement de version ou de hash Un changement réel de `processor_version` ou de `canonical_json_hash` doit provoquer une nouvelle extraction même sans `force_replay`. Cette vérification doit être effectuée dans une base de test dédiée. Le hash canonique d’une ligne de production ne doit pas être modifié manuellement. ### Vérifier le rollback Le rollback atomique doit être validé par un test PostgreSQL avec injection d’une erreur entre les insertions du graphe et le commit. La méthode recommandée est un test d’intégration sur `solana_test`, et non une corruption volontaire de la base principale. ## Sélecteur de corpus dans l’application La fenêtre read-only dédiée est : ```text demo_sql_replay_candidates ``` Elle est accessible depuis le menu principal et depuis l’en-tête de `demo_core_extraction`. `demo_sql_diag` reste limité à la connexion, au profil actif, aux migrations et aux cardinalités. Le sélecteur ne déclenche aucune acquisition RPC, extraction core, opération de décodage ou matérialisation. Il lit uniquement les tables PostgreSQL déjà alimentées. ## Tableau des transactions et signatures Les filtres PostgreSQL disponibles sont : - fragment de signature ; - slot minimum et maximum inclusifs ; - état raw `received`, `core_extracted`, `decoded`, `materialized` ou `failed` ; - statut ledger `not_started`, `running`, `succeeded` ou `failed` ; - program ID exact ; - portée du programme : `any`, `outer`, `inner` ou `logs` ; - entité exacte : `mint`, `owner` ou `account_key` ; - limite ; - ordre par slot croissant ou décroissant. Chaque résultat affiche : - signature et slot ; - état raw et rétention ; - présence de la transaction core et éventuel échec Solana ; - dernier statut du ledger `core_extraction` ; - version et nombre de tentatives ; - nombres d’instructions et de programmes outer/inner ; - date de dernière mise à jour raw. Le filtre de programme inspecte les instructions outer, les inner instructions et les logs auxquels un `program_id` a pu être rattaché de manière prudente. Une occurrence dans les logs n’est donc visible que lorsque le rattachement core a produit un `program_id` non nul. ## Tableau des programmes Le tableau agrège : ```text kb_sol_core_instructions.program_id kb_sol_core_inner_instructions.program_id kb_sol_core_logs.program_id ``` Pour chaque programme, il expose : - le `program_id` ; - le nombre de transactions distinctes ; - le nombre d’instructions outer ; - le nombre d’instructions inner ; - le nombre de logs reliés ; - le slot minimum et maximum. La sélection d’un programme peut alimenter le filtre programme du tableau des transactions, puis charger les signatures correspondantes. ## Tableau des entités Le tableau agrège trois familles : - `mint` depuis `kb_sol_core_balance_changes.mint` ; - `owner` depuis `kb_sol_core_balance_changes.owner` ; - `account_key` depuis `kb_sol_core_account_keys.account_key`. Chaque ligne indique le nombre de transactions distinctes, le nombre d’occurrences et la plage de slots. Une entité sélectionnée peut alimenter le filtre du tableau des transactions. Les account keys ne sont pas présentées comme wallet, pool, mint ou token account sans classification supplémentaire. ## DataTables, copie et exports La fenêtre utilise cinq tableaux DataTables avec intégration Bootstrap 5 et extension Select : 1. transactions et signatures ; 2. programmes ; 3. mints ; 4. owners ; 5. account keys. Chaque tableau affiche 10 lignes par défaut. Une checkbox par ligne et une checkbox de header permettent de sélectionner le jeu filtré. Les filtres possèdent un bouton de reset qui restaure les valeurs par défaut, efface la recherche locale, désélectionne les lignes et recharge PostgreSQL. Les signatures, program IDs, mints, owners et account keys sont tronqués visuellement sous la forme `préfixe…suffixe`. La valeur complète reste utilisée par le tri, la recherche, les tooltips, la copie et l’export. Un bouton de copie individuel évite les espaces parasites liés à une sélection manuelle. La règle d’export est la suivante : 1. lorsqu’une ou plusieurs lignes sont sélectionnées, seules ces lignes sont exportées ; 2. sans sélection, toutes les lignes correspondant au filtre local DataTables sont exportées ; 3. la limite PostgreSQL reste la borne supérieure du corpus chargé. Le frontend construit un CSV UTF-8 avec BOM, séparateur `;` et fins de ligne CRLF. Une commande Rust l’écrit dans : ```text data/exports_csv/ ``` Le backend ajoute un suffixe numérique lorsque le nom existe déjà. Ce chemin a été validé sous Tauri/Linux et ne dépend pas du téléchargement HTML du WebView. La copie multiligne des signatures est directement compatible avec la zone `Signatures explicites` de `demo_core_extraction`. ## Garanties et limites - commandes Tauri read-only ; - requêtes SQL statiques et paramétrées ; - aucune zone permettant d’exécuter du SQL libre ; - limite obligatoire comprise entre `1` et `5 000` ; - filtres exacts pour programme et entité afin d’éviter une sélection ambiguë ; - recherche locale DataTables appliquée après le chargement serveur ; - aucune découverte de transaction absente du raw ; - aucun décodage de protocole ; - aucun changement d’état raw, core ou ledger. Les futurs écrans de `0.4.x` pourront réutiliser le même principe pour les replays de décodeurs et de matérialiseurs. ## Utilisation du sélecteur SQL `demo_sql_replay_candidates` aide à constituer des corpus sans requête manuelle dans pgAdmin ou DBeaver. Il reste séparé du worker : il lit les tables raw/core/ledger, puis produit des signatures ou des filtres réutilisables. Dans l’onglet programmes, « Utiliser comme filtre transactions » prend exactement un programme coché. À défaut de sélection, une unique ligne restant après le filtre local est acceptée. L’action renseigne le program ID exact, choisit la portée `any`, ouvre l’onglet transactions et recharge la liste. Le sélecteur des programmes connus est alimenté par `kb_program_ids`, mais la saisie libre reste disponible pour les programmes observés qui ne sont pas encore enregistrés. Les exports CSV sont écrits dans `data/exports_csv/` par le backend Rust. Ils servent à l’audit et à la conservation d’un corpus ; pour rejouer immédiatement des signatures, la copie multiligne reste le chemin le plus direct. Les notions de pool et de paire ne sont pas encore disponibles à ce stade structurel. Une account key core ne suffit pas à prouver qu’un compte représente un pool ou une paire. Cette navigation sera ajoutée après matérialisation de catalogues sémantiques par les décodeurs. ## Validation réelle de l’idempotence Un échantillon de 14 signatures a produit les résultats suivants : ```text mode normal : extracted=0, skipped=14 force replay: extracted=14, skipped=0 mode normal : extracted=0, skipped=14 ``` Le nombre de transactions core est resté à 70. Le ledger contient 70 lignes `succeeded` et 84 tentatives, soit les 70 tentatives initiales plus les 14 remplacements forcés. Les requêtes d’intégrité n’ont détecté aucune anomalie. ## Politique future pour les transactions échouées Les transactions on-chain échouées restent candidates au décodage. Elles conservent des informations sur les programmes appelés, les instructions exécutées avant l’erreur, les logs, le compute consommé et la cause d’échec. Les décodeurs devront produire des observations marquées par le statut de la transaction. Les matérialiseurs ne devront pas convertir ces observations en mutations d’état réussies, trades confirmés, changements de liquidité ou candles normales.