0.1.0
This commit is contained in:
148
docs/CORE_EXTRACTION_CONTRACTS.md
Normal file
148
docs/CORE_EXTRACTION_CONTRACTS.md
Normal file
@@ -0,0 +1,148 @@
|
||||
<!-- file: docs/CORE_EXTRACTION_CONTRACTS.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Contrats d’extraction core Solana
|
||||
|
||||
## État de clôture `0.3.4-pre.011`
|
||||
|
||||
Le jalon transforme une transaction canonique déjà persistée en graphe core normalisé et rejouable :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions.canonical_json
|
||||
-> kb_model::CanonicalTransaction
|
||||
-> validation version/signature/slot/hash
|
||||
-> kb_pipeline::extract_raw_transaction_to_core
|
||||
-> CoreExtractionBundle
|
||||
-> transaction PostgreSQL atomique
|
||||
```
|
||||
|
||||
Le payload fournisseur contenu à l’origine dans une réponse RPC n’est jamais relu depuis les observations. La seule source fonctionnelle de l’extracteur est le document canonique de `kb_sol_raw_transactions`.
|
||||
|
||||
## Unité atomique
|
||||
|
||||
Une signature produit un `CoreExtractionBundle` contenant :
|
||||
|
||||
- une transaction core liée par `raw_transaction_id` ;
|
||||
- les account keys statiques et chargées par ALT ;
|
||||
- les instructions top-level ;
|
||||
- les inner instructions ;
|
||||
- les logs ordonnés ;
|
||||
- les changements de balances natifs et token ;
|
||||
- l’identité du processor dans le ledger.
|
||||
|
||||
Le repository PostgreSQL supprime puis recrée le graphe de la signature dans une transaction unique. Un échec avant le commit laisse intact l’état précédemment validé. Le test PostgreSQL de clôture provoque volontairement une violation d’unicité après insertion de la transaction et d’une première account key, puis vérifie l’absence totale de graphe partiel, de changement raw et de succès ledger.
|
||||
|
||||
## Validation de l’entrée
|
||||
|
||||
L’extraction refuse explicitement :
|
||||
|
||||
- un `canonical_json` absent ;
|
||||
- un `canonical_json_hash` absent ;
|
||||
- une version différente de `CANONICAL_TRANSACTION_FORMAT_VERSION` ;
|
||||
- un JSON non désérialisable en `CanonicalTransaction` ;
|
||||
- une signature ou un slot différent de la ligne raw ;
|
||||
- un hash recalculé différent du hash stocké ;
|
||||
- un indice de programme ou de compte hors de l’espace résolu.
|
||||
|
||||
L’identité du ledger est :
|
||||
|
||||
```text
|
||||
stage = core_extraction
|
||||
processor_name = canonical_to_core
|
||||
processor_version = 1
|
||||
input_key = signature
|
||||
input_hash = canonical_json_hash
|
||||
```
|
||||
|
||||
## Account keys
|
||||
|
||||
L’espace d’indices est construit strictement dans cet ordre :
|
||||
|
||||
```text
|
||||
static account keys
|
||||
loaded writable addresses
|
||||
loaded readonly addresses
|
||||
```
|
||||
|
||||
Pour les comptes statiques, les flags `signer` et `writable` sont dérivés du header Solana. Les loaded writable sont non-signers et writable. Les loaded readonly sont non-signers et readonly. `executable` reste `NULL`, car cette information n’est pas contenue de manière fiable dans le contrat canonique actuel.
|
||||
|
||||
## Instructions
|
||||
|
||||
Les chemins sont déterministes :
|
||||
|
||||
```text
|
||||
0
|
||||
1
|
||||
2
|
||||
0/0
|
||||
0/1
|
||||
2/0
|
||||
```
|
||||
|
||||
`accounts_json` conserve, dans l’ordre original, chaque indice et la clé publique résolue. `payload_json` conserve `programIdIndex`, `dataBase64` et `stackHeight`. Un SHA-256 stable du payload JSON est persisté dans `payload_json_hash`.
|
||||
|
||||
## Logs
|
||||
|
||||
Chaque log conserve :
|
||||
|
||||
- son `log_index` global ;
|
||||
- son texte original ;
|
||||
- un SHA-256 du texte ;
|
||||
- un `program_id` et un `instruction_path` uniquement lorsque la pile `invoke/success/failed` permet un rattachement déterministe.
|
||||
|
||||
Lorsque la reconstruction est ambiguë, le lien reste `NULL` plutôt que d’inventer une relation.
|
||||
|
||||
## Balances
|
||||
|
||||
Les changements natifs utilisent les valeurs entières en lamports et stockent pre, post et delta comme chaînes décimales dans JSONB afin d’éviter toute perte de précision.
|
||||
|
||||
Les changements SPL et Token-2022 utilisent exclusivement `amount` et `decimals`. Les valeurs UI du fournisseur ne sont pas utilisées comme source de calcul. Pour un compte créé ou fermé pendant la transaction, le côté absent est représenté explicitement par un montant brut nul avec les mêmes décimales.
|
||||
|
||||
Le `balance_change_index` est déterministe : balances natives par index de compte, puis balances token triées par `(account_index, mint, program_id)`.
|
||||
|
||||
## Modes de sélection
|
||||
|
||||
La campagne peut sélectionner des transactions raw par signatures explicites, état `received`, plage de slots ou programme déjà présent dans les instructions top-level core. Les filtres, limites et scénarios de validation sont détaillés dans `docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md`.
|
||||
|
||||
Le mode par programme ne découvre pas un programme nouveau : il dépend des lignes déjà présentes dans `kb_sol_core_instructions` et ne couvre pas actuellement les occurrences exclusivement inner.
|
||||
|
||||
## Idempotence et replay
|
||||
|
||||
En mode normal, une entrée ledger `succeeded` ayant la même version et le même hash provoque un skip.
|
||||
|
||||
Un changement de version ou de hash entraîne une nouvelle extraction. Le mode `force_replay` ignore le skip et remplace atomiquement le graphe existant de la signature.
|
||||
|
||||
## Orchestration
|
||||
|
||||
`kb_pipeline::execute_core_extraction` fournit :
|
||||
|
||||
- sélection par signatures ;
|
||||
- lot raw en état `received` ;
|
||||
- plage de slots ;
|
||||
- replay par program id déjà présent dans core ;
|
||||
- limite et concurrence bornées ;
|
||||
- arrêt coopératif avant admission de nouveaux candidats ;
|
||||
- résumé `selected`, `started`, `completed`, `skipped`, `extracted`, `failed`, `cancelled_candidates` et `not_started`.
|
||||
|
||||
La logique de transformation ne dépend ni de Tauri ni d’un décodeur protocolaire.
|
||||
|
||||
## Transactions échouées
|
||||
|
||||
Une transaction Solana ayant `failed = true` reste extraite intégralement lorsque ses métadonnées sont disponibles. Les instructions, inner instructions et logs exécutés avant l’erreur constituent des observations utiles pour les futurs décodeurs.
|
||||
|
||||
Le futur pipeline doit distinguer :
|
||||
|
||||
```text
|
||||
observation décodée dans une transaction échouée
|
||||
mutation d’état confirmée dans une transaction réussie
|
||||
```
|
||||
|
||||
Une transaction échouée peut produire des observations d’intention, de cause d’échec, de compute budget ou de surface appelée. Elle ne doit pas produire automatiquement un trade, une modification de liquidité ou une candle présentés comme réussis.
|
||||
|
||||
## Validation réelle
|
||||
|
||||
Le skip et le force replay ont été validés sur 14 signatures : `14 skipped`, puis `14 extracted` avec force replay, puis `14 skipped`. Les tables core restent à 70 transactions et le ledger totalise 84 tentatives. Les requêtes `sql/validation/000_core_integrity.sql` ne retournent aucune anomalie.
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
`0.3.4` n’effectue aucun décodage Solana Core/SPL, aucun décodage DEX, aucune matérialisation métier et aucune compaction du JSON canonique.
|
||||
Reference in New Issue
Block a user