6.4 KiB
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 :
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_jsonabsent ; - un
canonical_json_hashabsent ; - 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 :
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 :
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 :
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_indexglobal ; - son texte original ;
- un SHA-256 du texte ;
- un
program_idet uninstruction_pathuniquement lorsque la pileinvoke/success/failedpermet 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_candidatesetnot_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 :
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.