v0.5.3-pre.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 69
|
# version: 72
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -19,7 +19,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.5.3-pre.2"
|
version = "0.5.3-pre.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||||
|
|||||||
@@ -34,14 +34,34 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"target_filters": [
|
"target_filters": [
|
||||||
|
{
|
||||||
|
"target": "h2",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "hyper",
|
"target": "hyper",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "hyper_util",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "reqwest",
|
"target": "reqwest",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls_platform_verifier",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "sqlx",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "tao",
|
"target": "tao",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
|
|||||||
@@ -1168,14 +1168,34 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"target_filters": [
|
"target_filters": [
|
||||||
|
{
|
||||||
|
"target": "h2",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "hyper",
|
"target": "hyper",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "hyper_util",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "reqwest",
|
"target": "reqwest",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls_platform_verifier",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "sqlx",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "tao",
|
"target": "tao",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
@@ -2352,14 +2372,34 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"target_filters": [
|
"target_filters": [
|
||||||
|
{
|
||||||
|
"target": "h2",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "hyper",
|
"target": "hyper",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "hyper_util",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "reqwest",
|
"target": "reqwest",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls_platform_verifier",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "sqlx",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "tao",
|
"target": "tao",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
@@ -3536,14 +3576,34 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"target_filters": [
|
"target_filters": [
|
||||||
|
{
|
||||||
|
"target": "h2",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "hyper",
|
"target": "hyper",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "hyper_util",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "reqwest",
|
"target": "reqwest",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rustls_platform_verifier",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "sqlx",
|
||||||
|
"level": "warn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"target": "tao",
|
"target": "tao",
|
||||||
"level": "warn"
|
"level": "warn"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/STORAGE_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/STORAGE_ARCHITECTURE.md -->
|
||||||
<!-- version: 7 -->
|
<!-- version: 8 -->
|
||||||
|
|
||||||
# Architecture du stockage
|
# Architecture du stockage
|
||||||
|
|
||||||
@@ -9,42 +9,45 @@
|
|||||||
|
|
||||||
- la façade publique backend-agnostique `Store` et ses options d'ouverture ;
|
- la façade publique backend-agnostique `Store` et ses options d'ouverture ;
|
||||||
- les contrats store-neutral ;
|
- les contrats store-neutral ;
|
||||||
- les DTO et entités persistées ;
|
- les DTO et entités persistés ;
|
||||||
- la pagination, le replay et les rapports de santé ;
|
- la pagination, le replay et les rapports de santé ;
|
||||||
- les traits de repositories ;
|
- les traits de repositories ;
|
||||||
- les résumés runtime/init sanitisés ;
|
- les résumés runtime/init sanitisés ;
|
||||||
- l’implémentation PostgreSQL privée ;
|
- l'implémentation PostgreSQL privée ;
|
||||||
- les migrations et requêtes appartenant au backend actif.
|
- les ressources de schéma, maintenance et requêtes appartenant au backend actif.
|
||||||
|
|
||||||
Depuis `0.5.3-pre.002`, les consommateurs ne construisent plus un backend concret. `ks-store` possède la connexion et le dispatch interne ; `PostgresStore`, `PgPool` et SQLx ne traversent plus sa frontière publique.
|
Depuis `0.5.3-pre.002`, les consommateurs ne construisent plus un backend concret. `ks-store` possède la connexion et le dispatch interne ; `PostgresStore`, `PgPool` et SQLx ne traversent plus sa frontière publique.
|
||||||
|
|
||||||
|
Depuis `0.5.3-pre.003`, PostgreSQL construit directement le nouveau baseline `k_sol_*` à partir de ressources SQL atomiques ; les migrations historiques `0001` à `0004` ne constituent plus la chaîne active.
|
||||||
|
|
||||||
## 2. Frontières
|
## 2. Frontières
|
||||||
|
|
||||||
### 2.1 Contrats store-neutral
|
### 2.1 Contrats store-neutral
|
||||||
|
|
||||||
Les contrats ne dépendent pas des détails SQL lorsqu’une abstraction stable est suffisante. Ils décrivent les entrées, sorties, identifiants, pages, diagnostics et erreurs nécessaires aux consommateurs. `StoreOpenOptions` transporte un code backend et un objet d'options opaque ; seul `ks-store` interprète le moteur sélectionné.
|
Les contrats ne dépendent pas des détails SQL lorsqu'une abstraction stable est suffisante. Ils décrivent les entrées, sorties, identifiants, pages, diagnostics et erreurs nécessaires aux consommateurs. `StoreOpenOptions` transporte un code backend et un objet d'options opaque ; seul `ks-store` interprète le moteur sélectionné.
|
||||||
|
|
||||||
Le résumé runtime expose des modèles logiques et des compteurs d'objets par catégorie sans publier les noms physiques. PostgreSQL peut ainsi exposer des compteurs `table`/`index`, tandis qu'un futur backend peut utiliser d'autres catégories sans modifier le desktop.
|
Le résumé runtime expose des modèles logiques et des compteurs d'objets par catégorie sans publier les noms physiques. PostgreSQL expose actuellement des compteurs `table` et `index`, tandis qu'un futur backend peut utiliser d'autres catégories sans modifier le desktop.
|
||||||
|
|
||||||
### 2.2 Adaptateur PostgreSQL
|
### 2.2 Adaptateur PostgreSQL
|
||||||
|
|
||||||
Le module PostgreSQL possède :
|
Le module PostgreSQL possède :
|
||||||
|
|
||||||
- la validation de ses options ;
|
- la validation de ses options ;
|
||||||
- la connexion et l’initialisation ;
|
- la connexion et l'initialisation ;
|
||||||
- l’application idempotente des migrations ;
|
- l'orchestrateur transactionnel global du baseline ;
|
||||||
|
- les ressources SQL `include_str!` ;
|
||||||
- les requêtes typées ;
|
- les requêtes typées ;
|
||||||
- les diagnostics physiques ;
|
- les diagnostics physiques ;
|
||||||
- les opérations de replay et de sélection de candidats ;
|
- les opérations de replay et de sélection de candidats ;
|
||||||
- le target de tracing canonique `ks-store`, enrichi des champs `backend="postgres"`, `domain="ks-store.pg"` et `action` pour les opérations PostgreSQL.
|
- le target de tracing canonique `ks-store`, enrichi des champs `backend="postgres"`, `domain="ks-store.pg"` et `action`.
|
||||||
|
|
||||||
Les détails physiques restent privés. Les erreurs et résumés traversant la façade ne doivent pas divulguer de DSN, password ou option sensible.
|
Les détails physiques restent privés. Les erreurs et résumés traversant la façade ne doivent pas divulguer de DSN, password ou option sensible.
|
||||||
|
|
||||||
### 2.3 Modèles partagés
|
### 2.3 Modèles partagés
|
||||||
|
|
||||||
Les modèles métier communs restent dans `ks-lib` lorsqu’ils dépassent la seule persistance. `ks-store` ne doit pas créer une seconde définition concurrente d’un contrat partagé.
|
Les modèles métier communs restent dans `ks-lib` lorsqu'ils dépassent la seule persistance. `ks-store` ne doit pas créer une seconde définition concurrente d'un contrat partagé.
|
||||||
|
|
||||||
## 3. Catégories de données
|
## 3. Niveaux de persistance
|
||||||
|
|
||||||
Le stockage distingue quatre niveaux durables :
|
Le stockage distingue quatre niveaux durables :
|
||||||
|
|
||||||
@@ -53,11 +56,101 @@ Le stockage distingue quatre niveaux durables :
|
|||||||
- **N3** — decode/materialization générique et journaux versionnés ;
|
- **N3** — decode/materialization générique et journaux versionnés ;
|
||||||
- **N4** — projections spécialisées/queryables additives.
|
- **N4** — projections spécialisées/queryables additives.
|
||||||
|
|
||||||
N1/N2/N3 constituent la fondation fortement gelée après `0.5.3`, sous réserve d'une urgence ou d'une omission structurelle majeure. N4 reste plus évolutif. Les replays N1 -> N2, N2 -> N3 et N3 -> N4 doivent pouvoir être exécutés indépendamment et de manière idempotente.
|
N1/N2/N3 constituent la fondation candidate au gel de `0.5.3`. Le gel formel n'est déclaré qu'après snapshot et validations de réconciliation ; après clôture, leur évolution normale doit être fortement contrainte. N4 reste volontairement plus évolutif.
|
||||||
|
|
||||||
Les noms de tables, contrats de replay et APIs publiques sont documentés dans `ks-store/USAGE.md` à partir des exports et migrations actuels.
|
Les replays N1 -> N2, N2 -> N3 et N3 -> N4 doivent pouvoir être exécutés indépendamment et de manière idempotente.
|
||||||
|
|
||||||
## 4. Propriétés attendues
|
## 4. Baseline N1-N3 de `pre.003`
|
||||||
|
|
||||||
|
Le baseline PostgreSQL comporte **16 tables**.
|
||||||
|
|
||||||
|
### 4.1 N1
|
||||||
|
|
||||||
|
- `k_sol_raw_transactions` ;
|
||||||
|
- `k_sol_obs_transaction_observations` ;
|
||||||
|
- `k_sol_obs_account_observations`.
|
||||||
|
|
||||||
|
`k_sol_raw_transactions` conserve le document canonique source-independent et `block_time` lorsqu'il est fourni par Solana. Les observations d'acquisition restent séparées de ce document canonique.
|
||||||
|
|
||||||
|
`k_sol_obs_account_observations` réserve un input générique pour les acquisitions d'état de compte : identité du compte, slot/context, bytes exacts, hash et provenance d'acquisition. Cette structure n'est liée à aucun décodeur ou IDL.
|
||||||
|
|
||||||
|
### 4.2 N2 Core
|
||||||
|
|
||||||
|
- `k_sol_core_transactions` ;
|
||||||
|
- `k_sol_core_account_keys` ;
|
||||||
|
- `k_sol_core_instructions` ;
|
||||||
|
- `k_sol_core_inner_instructions` ;
|
||||||
|
- `k_sol_core_logs` ;
|
||||||
|
- `k_sol_core_balance_changes` ;
|
||||||
|
- `k_sol_core_return_data` ;
|
||||||
|
- `k_sol_core_account_states`.
|
||||||
|
|
||||||
|
`k_sol_core_transactions` porte l'ancre transactionnelle et `block_time`.
|
||||||
|
|
||||||
|
Les instructions top-level et CPI restent physiquement séparées. Les deux portent `stack_height` lorsque disponible ; les CPI conservent leur parent immédiat reconstructible. Elles doivent converger vers des capacités lifecycle/replay équivalentes sans imposer un stockage physique unique.
|
||||||
|
|
||||||
|
`k_sol_core_return_data` évite de perdre en N2 la `returnData` déjà présente dans la transaction canonique N1.
|
||||||
|
|
||||||
|
`k_sol_core_account_states` fournit le pendant Core des observations de comptes et conserve les bytes exacts ainsi que leur provenance N1.
|
||||||
|
|
||||||
|
### 4.3 N3 et transverse
|
||||||
|
|
||||||
|
- `k_sol_ops_processing_ledger` ;
|
||||||
|
- `k_sol_decode_events` ;
|
||||||
|
- `k_sol_decode_coverage_declarations` ;
|
||||||
|
- `k_sol_decode_coverage_observations` ;
|
||||||
|
- `k_sol_mat_outputs`.
|
||||||
|
|
||||||
|
`k_sol_mat_outputs` est le journal générique durable des sorties de matérialisation. Les projections métier N4 ne le remplacent pas.
|
||||||
|
|
||||||
|
`k_sol_decode_events` réserve une provenance de schéma nullable (`kind/id/version/hash`) pour les mécanismes de décodage pouvant dépendre d'un schéma externe. Les décodeurs compilés actuels la laissent vide.
|
||||||
|
|
||||||
|
## 5. Readiness des futurs décodeurs
|
||||||
|
|
||||||
|
Le canari future-decoder ne doit pas introduire Anchor dans le schéma. Il vérifie que les faits Solana génériques suffisants sont conservés :
|
||||||
|
|
||||||
|
- bytes d'instruction et comptes ordonnés ;
|
||||||
|
- propriétés signer/writable résolubles ;
|
||||||
|
- instructions top-level et CPI ;
|
||||||
|
- `stack_height` et parent CPI ;
|
||||||
|
- logs ordonnés ;
|
||||||
|
- `returnData` ;
|
||||||
|
- observations/états bruts de comptes ;
|
||||||
|
- temporalité et provenance ;
|
||||||
|
- identité/version/hash d'un schéma externe lorsqu'un décodeur en utilise un.
|
||||||
|
|
||||||
|
La conséquence de `pre.003` est l'ajout de trois contrats génériques au-dessus des 13 tables historiques : observation de compte, état Core de compte et return data Core. Le baseline candidat au gel passe donc à 16 tables.
|
||||||
|
|
||||||
|
Les métriques transactionnelles comme fee, rewards, compute units et cost units restent disponibles dans N1. Elles ne sont pas requises pour ce canari et ne justifient pas une extension de l'ancre Core en `pre.003`; une future projection additive pourra les rendre queryables si nécessaire.
|
||||||
|
|
||||||
|
## 6. Ressources PostgreSQL
|
||||||
|
|
||||||
|
Les ressources actives sont classées sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ks-store/migrations/postgres/
|
||||||
|
schema/
|
||||||
|
tables/
|
||||||
|
constraints/
|
||||||
|
indexes/
|
||||||
|
maintenance/
|
||||||
|
truncate/
|
||||||
|
drop/
|
||||||
|
```
|
||||||
|
|
||||||
|
Principes :
|
||||||
|
|
||||||
|
- une instruction SQL par fichier ;
|
||||||
|
- un `include_str!` privé par ressource ;
|
||||||
|
- aucun DDL dupliqué dans une chaîne Rust ;
|
||||||
|
- tables avant contraintes, contraintes avant index ;
|
||||||
|
- initialisation complète dans une transaction PostgreSQL unique avec advisory lock ;
|
||||||
|
- `DROP`/`TRUNCATE` séparés de l'auto-init ;
|
||||||
|
- refus explicite d'un schéma contenant encore des objets `kb_sol_*`.
|
||||||
|
|
||||||
|
Le baseline `pre.003` contient **236 ressources SQL atomiques** et **75 index attendus**. Ces nombres décrivent l'implémentation PostgreSQL actuelle, pas une API publique gelée.
|
||||||
|
|
||||||
|
## 7. Propriétés attendues
|
||||||
|
|
||||||
- initialisation idempotente ;
|
- initialisation idempotente ;
|
||||||
- pagination bornée ;
|
- pagination bornée ;
|
||||||
@@ -65,12 +158,12 @@ Les noms de tables, contrats de replay et APIs publiques sont documentés dans `
|
|||||||
- absence de double effet lors des replays ;
|
- absence de double effet lors des replays ;
|
||||||
- validation stricte des entrées ;
|
- validation stricte des entrées ;
|
||||||
- erreurs explicites ;
|
- erreurs explicites ;
|
||||||
- séparation entre données brutes, résultats de décodage et matérialisations.
|
- séparation entre acquisition/raw, Core, decode/materialization et projections N4 ;
|
||||||
|
- absence de dépendance de N1/N2 à une technologie de décodeur ;
|
||||||
|
- conservation des faits nécessaires à un redécodage historique.
|
||||||
|
|
||||||
La série `0.5.3` normalisera cette fondation avant l’arrivée des protocoles trading. Elle doit notamment distinguer `slot`, `block_time` et les timestamps d’acquisition/persistance, normaliser la structure interne de `ks-store`, vérifier les champs et index manquants et préparer les futures matérialisations trading, routing et multi-pools sans perdre les contrats de provenance et d’idempotence.
|
Les index non uniques restent des optimisations physiques évolutives. Les noms/colonnes/types/nullabilités/PK/FK/uniques/checks/sémantiques du baseline N1-N3 seront figés séparément lors de la tranche de gel.
|
||||||
|
|
||||||
La même migration remplace le préfixe historique des tables Solana `kb_sol_*` par `k_sol_*`. La base peut encore être reconstruite ou migrée proprement avant `0.6.x`, il n’est donc pas nécessaire de conserver indéfiniment l’ancien préfixe. Le préfixe `kb_*` est réservé aux éventuelles tables dont la responsabilité appartient réellement au domaine applicatif Bot, pas aux faits Solana simplement consommés par le bot.
|
## 8. Données de test
|
||||||
|
|
||||||
## 5. Données de test
|
Les fixtures privées, bases locales et preuves temporaires ne font pas partie des livraisons. Les matrices contractuelles partagées restent sous `test-fixtures/contract-matrices/` lorsqu'elles sont nécessaires aux tests.
|
||||||
|
|
||||||
Les fixtures privées, bases locales et preuves temporaires ne font pas partie des livraisons. Les matrices contractuelles partagées restent sous `test-fixtures/contract-matrices/` lorsqu’elles sont nécessaires aux tests.
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md -->
|
<!-- file: docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Politique de namespace Khadhroony Solana
|
# Politique de namespace Khadhroony Solana
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Cette décision est adoptée pendant la clôture de `0.5.0` et devient la cible normative des migrations de fondation `0.5.1` à `0.5.3`.
|
Cette décision est adoptée pendant la clôture de `0.5.0` et devient la cible normative des migrations de fondation `0.5.1` à `0.5.3`.
|
||||||
|
|
||||||
À partir de `0.5.1-pre.002`, les dix crates Solana généralistes sont physiquement nommées `ks-*` et leurs identifiants Rust `ks_*`. `0.5.1-pre.003` migre également les identités techniques `kb-lib.*` vers `ks-lib-*`. `0.5.1-pre.004` migre les variables possédées par Khadhroony Solana vers `KS_*` et réserve explicitement `KB_*` aux composants réellement possédés par Khadhroony Bot. Le namespace SQL `kb_sol_*` reste temporairement présent jusqu'à `0.5.3`.
|
À partir de `0.5.1-pre.002`, les dix crates Solana généralistes sont physiquement nommées `ks-*` et leurs identifiants Rust `ks_*`. `0.5.1-pre.003` migre également les identités techniques `kb-lib.*` vers `ks-lib-*`. `0.5.1-pre.004` migre les variables possédées par Khadhroony Solana vers `KS_*` et réserve explicitement `KB_*` aux composants réellement possédés par Khadhroony Bot. Le namespace SQL historique `kb_sol_*` est remplacé par `k_sol_*` dans le baseline PostgreSQL de `0.5.3-pre.003`.
|
||||||
|
|
||||||
Le renommage des bibliothèques internes ne renomme pas le workspace, le dépôt ni le répertoire racine : ils restent `khadhroony-bot3` pendant toute la série pré-`1.0`. Un éventuel renommage du workspace racine est explicitement hors périmètre de `0.5.x` et ne doit intervenir qu'en `1.0` ou ultérieurement sur décision dédiée.
|
Le renommage des bibliothèques internes ne renomme pas le workspace, le dépôt ni le répertoire racine : ils restent `khadhroony-bot3` pendant toute la série pré-`1.0`. Un éventuel renommage du workspace racine est explicitement hors périmètre de `0.5.x` et ne doit intervenir qu'en `1.0` ou ultérieurement sur décision dédiée.
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ Les segments métier internes (`solana`, `spl`, protocoles, surfaces et opérati
|
|||||||
|
|
||||||
La normalisation SQL appartient à `0.5.3` avec `ks-store`.
|
La normalisation SQL appartient à `0.5.3` avec `ks-store`.
|
||||||
|
|
||||||
Les tables génériques représentant des faits Solana doivent migrer du préfixe actuel `kb_sol_*` vers :
|
Depuis `0.5.3-pre.003`, le baseline PostgreSQL actif des faits Solana utilise :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
k_sol_*
|
k_sol_*
|
||||||
@@ -124,7 +124,7 @@ kb_*
|
|||||||
|
|
||||||
Une table ne reçoit pas `kb_*` simplement parce qu'elle est consommée par le bot. Elle doit contenir des données dont la responsabilité appartient réellement au domaine applicatif Bot et non à la blockchain ou aux bibliothèques Solana généralistes.
|
Une table ne reçoit pas `kb_*` simplement parce qu'elle est consommée par le bot. Elle doit contenir des données dont la responsabilité appartient réellement au domaine applicatif Bot et non à la blockchain ou aux bibliothèques Solana généralistes.
|
||||||
|
|
||||||
La migration `kb_sol_*` → `k_sol_*` doit être traitée avec les contrats temporels, de provenance, d'idempotence, d'index et de replay de `0.5.3`, et non comme un remplacement textuel isolé.
|
La reconstruction `kb_sol_*` → `k_sol_*` de `0.5.3-pre.003` est traitée avec les contrats temporels, de provenance, d'idempotence, d'index et de replay ; l'ancien namespace reste uniquement un marqueur détecté/refusé et une référence historique.
|
||||||
|
|
||||||
## 8. Ordre des migrations
|
## 8. Ordre des migrations
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/guides/POSTGRES_STORAGE.md -->
|
<!-- file: docs/guides/POSTGRES_STORAGE.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Guide PostgreSQL et contrats de stockage
|
# Guide PostgreSQL et contrats de stockage
|
||||||
|
|
||||||
@@ -35,47 +35,114 @@ Le descripteur public éventuellement affiché provient du résumé runtime et e
|
|||||||
|
|
||||||
## Domaines
|
## Domaines
|
||||||
|
|
||||||
- raw : acquisitions et observations ;
|
- N1 raw/acquisition : transactions canoniques, observations de transactions et observations génériques de comptes ;
|
||||||
- Core : transactions, instructions, comptes et contexte normalisés ;
|
- N2 Core : transactions, clés, top-level, CPI, logs, variations de balances, return data et états de comptes ;
|
||||||
- decode : ledger, observations décodées et matérialisations ;
|
- N3/transverse : ledger, observations décodées, couverture et `k_sol_mat_outputs` ;
|
||||||
- replay : candidats et résumés bornés.
|
- replay/diagnostic : sélections et résumés bornés au-dessus de ces contrats.
|
||||||
|
|
||||||
## Migrations
|
## Baseline `0.5.3-pre.003`
|
||||||
|
|
||||||
`0.5.3-pre.002` conserve temporairement le schéma PostgreSQL historique `kb_sol_*` et son mécanisme d'initialisation afin de réaliser d'abord la rupture d'API backend. `pre.003` reconstruit le baseline sous `k_sol_*`, range les ressources sous `ks-store/migrations/postgres/` et impose une instruction SQL par fichier exécutée par les fonctions privées du backend.
|
`pre.003` reconstruit directement le schéma sous `k_sol_*`. Les bases Devnet/Mainnet/test historiques sont considérées reconstructibles ; aucune migration in-place depuis `kb_sol_*` n'est fournie.
|
||||||
|
|
||||||
Les bases Devnet/Mainnet/test `0.5.2` sont reconstructibles : aucune compatibilité in-place n'est requise pour ce changement de baseline.
|
Le comportement attendu à l'ouverture est :
|
||||||
|
|
||||||
|
1. base vide + auto-init : création du baseline complet ;
|
||||||
|
2. base `k_sol_*` déjà conforme : vérification/idempotence ;
|
||||||
|
3. base contenant encore des objets `kb_sol_*` : refus non destructif et demande de reconstruction explicite ;
|
||||||
|
4. `DROP`/`TRUNCATE` : opérations de maintenance distinctes, jamais déclenchées implicitement par `Store::open()`.
|
||||||
|
|
||||||
|
Le baseline candidat au gel comprend 16 tables N1-N3.
|
||||||
|
|
||||||
|
## Ressources SQL
|
||||||
|
|
||||||
|
Les ressources actives sont sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ks-store/migrations/postgres/
|
||||||
|
schema/
|
||||||
|
tables/
|
||||||
|
constraints/
|
||||||
|
indexes/
|
||||||
|
maintenance/
|
||||||
|
truncate/
|
||||||
|
drop/
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque fichier contient une instruction SQL. Le code PostgreSQL privé utilise `include_str!` et l'orchestrateur global exécute le schéma dans une transaction unique avec advisory lock.
|
||||||
|
|
||||||
|
Il n'existe plus trois initialiseurs raw/Core/decode indépendants : **l'initialisation du schéma est globale**.
|
||||||
|
|
||||||
|
Le corpus `pre.003` contient actuellement :
|
||||||
|
|
||||||
|
- 16 créations de tables ;
|
||||||
|
- 129 ressources de contraintes ;
|
||||||
|
- 59 créations d'index explicites ;
|
||||||
|
- 16 ressources `TRUNCATE` ;
|
||||||
|
- 16 ressources `DROP` ;
|
||||||
|
- soit **236 ressources SQL**.
|
||||||
|
|
||||||
|
PostgreSQL attend **75 index** au total dans son diagnostic : 59 index explicites plus les 16 index de clé primaire créés par PostgreSQL.
|
||||||
|
|
||||||
|
Les index non uniques restent des optimisations physiques et ne font pas partie du gel logique N1-N3.
|
||||||
|
|
||||||
|
## Statut du schéma
|
||||||
|
|
||||||
|
La façade n'utilise plus `_sqlx_migrations` comme preuve du baseline actuel. Le statut est dérivé du contrat réellement observé :
|
||||||
|
|
||||||
|
- aucune ressource connue : non initialisé ;
|
||||||
|
- toutes les ressources attendues : contrat courant ;
|
||||||
|
- présence partielle : drift/partial.
|
||||||
|
|
||||||
|
Le résumé d'initialisation expose des compteurs de modèles, tables et index sans publier leurs noms physiques.
|
||||||
|
|
||||||
## Compatibilité des identités de processeurs
|
## Compatibilité des identités de processeurs
|
||||||
|
|
||||||
Depuis `0.5.1-pre.003`, les identités techniques produites par `ks-lib` utilisent `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*`. Une base alimentée avant cette migration peut encore contenir des valeurs `kb-lib.*` dans les contrats de provenance/replay.
|
Depuis `0.5.1-pre.003`, les identités techniques produites par `ks-lib` utilisent `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*`.
|
||||||
|
|
||||||
Aucune migration SQL automatique de ces valeurs n'est ajoutée en `0.5.1` : avant `0.6.x`, une base conservée doit soit être reconstruite proprement, soit faire l'objet d'une migration de données explicitement contrôlée avant reprise du replay. Il ne faut pas exploiter durablement un même corpus avec les anciennes et nouvelles identités mélangées. Les noms de tables `kb_sol_*` restent inchangés jusqu'au chantier `0.5.3`.
|
Comme les bases `0.5.2` doivent être reconstruites pour le nouveau baseline `0.5.3`, les anciennes identités `kb-lib.*` ne sont pas migrées automatiquement dans ce chantier.
|
||||||
|
|
||||||
|
## Core readiness
|
||||||
|
|
||||||
|
`pre.003` ajoute les éléments génériques nécessaires au futur redécodage :
|
||||||
|
|
||||||
|
- `block_time` sur raw/Core transaction ;
|
||||||
|
- `stack_height` sur top-level/CPI ;
|
||||||
|
- parent CPI immédiat reconstructible ;
|
||||||
|
- `k_sol_core_return_data` ;
|
||||||
|
- `k_sol_obs_account_observations` ;
|
||||||
|
- `k_sol_core_account_states` ;
|
||||||
|
- provenance de schéma optionnelle sur les observations décodées.
|
||||||
|
|
||||||
|
Ces contrats sont indépendants d'Anchor/IDL. Aucun parser Anchor ni table `anchor_*` n'appartient à `0.5.3`.
|
||||||
|
|
||||||
## Repositories
|
## Repositories
|
||||||
|
|
||||||
Les traits publics et la façade `Store` décrivent des opérations fonctionnelles indépendantes du moteur. PostgreSQL implémente ces contrats à l’intérieur de `ks-store`; aucune crate consommatrice ne reçoit `PgPool`, un type SQLx ou une requête concrète. Les opérations de lecture utilisent des filtres et paginations bornés.
|
Les traits publics et la façade `Store` décrivent des opérations fonctionnelles indépendantes du moteur. PostgreSQL implémente ces contrats à l'intérieur de `ks-store`; aucune crate consommatrice ne reçoit `PgPool`, un type SQLx ou une requête concrète.
|
||||||
|
|
||||||
|
Le schéma des observations/états de comptes est introduit avant gel en `pre.003`; son ingestion/replay fonctionnel est raccordé dans la tranche repositories/replay suivante.
|
||||||
|
|
||||||
## Diagnostics
|
## Diagnostics
|
||||||
|
|
||||||
La surface publique expose :
|
La surface publique expose :
|
||||||
|
|
||||||
- health snapshot ;
|
- health snapshot ;
|
||||||
- migration snapshot ;
|
- schema/migration snapshot générique ;
|
||||||
- backend descriptor masqué ;
|
- backend descriptor masqué ;
|
||||||
- résumé de configuration sanitisé ;
|
- résumé de configuration sanitisé ;
|
||||||
- résumé d'initialisation/vérification par modèle logique ;
|
- résumé d'initialisation/vérification par modèle logique ;
|
||||||
- diagnostics de ressources raw/Core/decode/materialization sans nom physique d'objet.
|
- diagnostics de ressources sans nom physique d'objet.
|
||||||
|
|
||||||
Les noms de tables, index, contraintes et le SQL restent des détails backend-internes. Les traces PostgreSQL utilisent l’unique `target: crate::TRACING_TARGET` (`ks-store`) avec les champs structurés `backend="postgres"`, `domain="ks-store.pg"` et un `action` précis (`connection`, `migration`, `query`, `health`, `replay`, etc.).
|
Les noms de tables, index, contraintes et le SQL restent des détails backend-internes. Les traces PostgreSQL utilisent l'unique `target: crate::TRACING_TARGET` (`ks-store`) avec les champs structurés `backend="postgres"`, `domain="ks-store.pg"` et un `action` précis.
|
||||||
|
|
||||||
## Invariants
|
## Invariants
|
||||||
|
|
||||||
- aucune donnée canonique ne doit être dupliquée sans justification ;
|
- aucune donnée canonique ne doit être dupliquée sans justification ;
|
||||||
- les écritures rejouables doivent être idempotentes ;
|
- les écritures rejouables doivent être idempotentes ;
|
||||||
- la progression de campagne doit rester cohérente avec les lignes effectivement traitées ;
|
- la progression de campagne doit rester cohérente avec les lignes effectivement traitées ;
|
||||||
- les requêtes dynamiques n’acceptent que des identifiants validés ;
|
- les requêtes dynamiques n'acceptent que des identifiants validés ;
|
||||||
- les détails PostgreSQL utiles au diagnostic restent backend-internes ; les erreurs traversant la façade publique ne doivent jamais divulguer DSN, password ou options sensibles.
|
- les détails PostgreSQL utiles au diagnostic restent backend-internes ;
|
||||||
|
- les erreurs traversant la façade publique ne doivent jamais divulguer DSN, password ou options sensibles ;
|
||||||
|
- un nouveau décodeur doit pouvoir exploiter N1/N2/N3 sans remodelage normal de ces niveaux.
|
||||||
|
|
||||||
## Références
|
## Références
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/V0_5_3_KS_STORE_NORMALIZATION_PLAN.md -->
|
<!-- file: docs/plans/V0_5_3_KS_STORE_NORMALIZATION_PLAN.md -->
|
||||||
<!-- version: 7 -->
|
<!-- version: 8 -->
|
||||||
|
|
||||||
# Plan `0.5.3` — audit et normalisation de `ks-store`
|
# Plan `0.5.3` — audit et normalisation de `ks-store`
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ Les fonctions privées PostgreSQL correspondantes sont séparées et utilisent `
|
|||||||
| `k_sol_decode_events` | `DecodeObservationInsert` | `DecodePipelineStore` | `decode_pipeline_queries` | `ks-pipeline::decode_replay` |
|
| `k_sol_decode_events` | `DecodeObservationInsert` | `DecodePipelineStore` | `decode_pipeline_queries` | `ks-pipeline::decode_replay` |
|
||||||
| `k_sol_decode_coverage_declarations` | `DecodeCoverageDeclarationInsert` | `DecodePipelineStore` | `decode_pipeline_queries` | `ks-pipeline::decode_replay` |
|
| `k_sol_decode_coverage_declarations` | `DecodeCoverageDeclarationInsert` | `DecodePipelineStore` | `decode_pipeline_queries` | `ks-pipeline::decode_replay` |
|
||||||
| `k_sol_decode_coverage_observations` | `DecodeCoverageObservationInsert`, `DecodeCoverageSummaryRow` | `DecodePipelineStore` | `decode_pipeline_queries` | pipeline + desktop diagnostics |
|
| `k_sol_decode_coverage_observations` | `DecodeCoverageObservationInsert`, `DecodeCoverageSummaryRow` | `DecodePipelineStore` | `decode_pipeline_queries` | pipeline + desktop diagnostics |
|
||||||
| `k_sol_mat_outputs` | `MaterializedOutputInsert`, `MaterializedEventQueryRow` | `DecodePipelineStore` | `decode_pipeline_queries` | pipeline, scénarios Devnet, desktop |
|
| `k_sol_mat_outputs` | `MaterializedOutputInsert`, `MaterializedOutputQueryRow` | `DecodePipelineStore` | `decode_pipeline_queries` | pipeline, scénarios Devnet, desktop |
|
||||||
|
|
||||||
### 5.1 Contrats publics hérités sans implémentation
|
### 5.1 Contrats publics hérités sans implémentation
|
||||||
|
|
||||||
@@ -1079,7 +1079,7 @@ La même règle s'applique à tout autre protocole : une table nommée d'après
|
|||||||
- réouverture idempotente d'une base déjà initialisée ;
|
- réouverture idempotente d'une base déjà initialisée ;
|
||||||
- refus propre d'une base contenant encore des objets `kb_sol_*` ;
|
- refus propre d'une base contenant encore des objets `kb_sol_*` ;
|
||||||
- absence finale de tables, contraintes, index et séquences `kb_sol_*` après reconstruction ;
|
- absence finale de tables, contraintes, index et séquences `kb_sol_*` après reconstruction ;
|
||||||
- présence des **13 tables de baseline**, plus des éventuels contrats N1/N2/N3 génériques explicitement retenus après le canari future-decoder ;
|
- présence des **16 tables du baseline candidat au gel** retenu en `pre.003` ;
|
||||||
- snapshot exact des colonnes/types/nullabilités/PK/FK/uniques/checks frozen ;
|
- snapshot exact des colonnes/types/nullabilités/PK/FK/uniques/checks frozen ;
|
||||||
- présence et contrat séparé des tables top-level/CPI ;
|
- présence et contrat séparé des tables top-level/CPI ;
|
||||||
- test de lecture logique combinée top-level+CPI sans perte, doublon ni ordre instable ;
|
- test de lecture logique combinée top-level+CPI sans perte, doublon ni ordre instable ;
|
||||||
@@ -1154,7 +1154,7 @@ Avec `KS_SECRET_POSTGRES_TEST_URL` local :
|
|||||||
|
|
||||||
## 26. Snapshot de contrat frozen
|
## 26. Snapshot de contrat frozen
|
||||||
|
|
||||||
Le snapshot `0.5.3` couvre les **13 tables existantes de baseline, plus toute structure N1/N2/N3 générique dont le canari future-decoder démontre la nécessité avant gel**, ainsi que les tables transverses de processing/decode qui assurent leur idempotence et leur journalisation. Les futures projections N4 disposent de contrats/version propres, mais ne sont pas ajoutées à ce snapshot frozen fondamental.
|
Le snapshot `0.5.3` couvre les **16 tables du baseline candidat au gel retenu en `pre.003`**, y compris les trois contrats génériques ajoutés par le canari future-decoder (`account observation`, `account state`, `transaction return data`), ainsi que les tables transverses de processing/decode qui assurent leur idempotence et leur journalisation. Les futures projections N4 disposent de contrats/version propres, mais ne sont pas ajoutées à ce snapshot frozen fondamental.
|
||||||
|
|
||||||
Une fixture machine-readable sera créée avant clôture, par exemple :
|
Une fixture machine-readable sera créée avant clôture, par exemple :
|
||||||
|
|
||||||
@@ -1320,7 +1320,7 @@ cargo tauri dev -c kb-app-demo-desktop/tauri.conf.json
|
|||||||
La version ne peut être clôturée que si :
|
La version ne peut être clôturée que si :
|
||||||
|
|
||||||
- il n'existe plus d'objet actif `kb_sol_*` ;
|
- il n'existe plus d'objet actif `kb_sol_*` ;
|
||||||
- les 13 tables de baseline et toute table N1/N2/N3 générique ajoutée après le canari future-decoder correspondent exactement au snapshot frozen documenté ;
|
- les 16 tables du baseline candidat retenu en `pre.003` correspondent exactement au snapshot frozen documenté ;
|
||||||
- `block_time` est conservé lorsqu'il existe et reste `NULL` sinon ;
|
- `block_time` est conservé lorsqu'il existe et reste `NULL` sinon ;
|
||||||
- les top-level et CPI restent physiquement séparées mais peuvent être sélectionnées séparément ou comme flux logique commun pour le decode ;
|
- les top-level et CPI restent physiquement séparées mais peuvent être sélectionnées séparément ou comme flux logique commun pour le decode ;
|
||||||
- replay et invalidation descendante sont cohérents et transactionnels, avec contrats N1 -> N2, N2 -> N3 et frontière N3 -> N4 documentée/testable ;
|
- replay et invalidation descendante sont cohérents et transactionnels, avec contrats N1 -> N2, N2 -> N3 et frontière N3 -> N4 documentée/testable ;
|
||||||
@@ -1453,3 +1453,159 @@ Le nettoyage fin de l’instrumentation des requêtes (durées, nombres de ligne
|
|||||||
- n’implémente pas Anchor.
|
- n’implémente pas Anchor.
|
||||||
|
|
||||||
Ces travaux restent affectés aux tranches `pre.003+` prévues par le présent plan.
|
Ces travaux restent affectés aux tranches `pre.003+` prévues par le présent plan.
|
||||||
|
|
||||||
|
## 33. État réalisé de `0.5.3-pre.003`
|
||||||
|
|
||||||
|
`pre.003` réalise la reconstruction physique PostgreSQL et ferme les principaux manques révélés par le canari future-decoder. Le résultat est un **baseline candidat au gel**, pas encore le snapshot frozen final de `pre.006`.
|
||||||
|
|
||||||
|
### 33.1 Baseline candidat : 16 tables
|
||||||
|
|
||||||
|
Les 13 tables historiques sont conservées conceptuellement sous le namespace `k_sol_*`, sans fusion des instructions top-level/CPI. Trois contrats Solana génériques sont ajoutés avant gel :
|
||||||
|
|
||||||
|
1. `k_sol_obs_account_observations` — observation N1 d'un état de compte avec bytes exacts, hash et provenance d'acquisition ;
|
||||||
|
2. `k_sol_core_account_states` — état N2 canonique dérivé d'une observation de compte ;
|
||||||
|
3. `k_sol_core_return_data` — conservation N2 de la `returnData` transactionnelle déjà présente dans le document canonique N1.
|
||||||
|
|
||||||
|
Le baseline candidat comporte donc :
|
||||||
|
|
||||||
|
```text
|
||||||
|
N1
|
||||||
|
k_sol_raw_transactions
|
||||||
|
k_sol_obs_transaction_observations
|
||||||
|
k_sol_obs_account_observations
|
||||||
|
|
||||||
|
N2
|
||||||
|
k_sol_core_transactions
|
||||||
|
k_sol_core_account_keys
|
||||||
|
k_sol_core_instructions
|
||||||
|
k_sol_core_inner_instructions
|
||||||
|
k_sol_core_logs
|
||||||
|
k_sol_core_balance_changes
|
||||||
|
k_sol_core_return_data
|
||||||
|
k_sol_core_account_states
|
||||||
|
|
||||||
|
N3 / transverse
|
||||||
|
k_sol_ops_processing_ledger
|
||||||
|
k_sol_decode_events
|
||||||
|
k_sol_decode_coverage_declarations
|
||||||
|
k_sol_decode_coverage_observations
|
||||||
|
k_sol_mat_outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune structure n'est nommée d'après Anchor, une IDL, un DEX ou un protocole.
|
||||||
|
|
||||||
|
### 33.2 Temporalité et Core instructionnel
|
||||||
|
|
||||||
|
`block_time` devient une colonne nullable de premier rang sur `k_sol_raw_transactions` et `k_sol_core_transactions`. La Core extraction vérifie la cohérence entre le document canonique N1 et l'ancre Core lorsqu'ils sont reliés.
|
||||||
|
|
||||||
|
Les instructions top-level et CPI restent physiquement séparées. Les deux contrats portent désormais `stack_height` lorsque Solana la fournit. La Core extraction reconstruit les chemins CPI imbriqués à partir de la pile ; `parent_instruction_path` pointe alors vers le parent immédiat. Lorsque `stackHeight` est absent, la CPI reste rattachée de manière déterministe à la top-level racine sans inventer un niveau de pile.
|
||||||
|
|
||||||
|
Les CPI reçoivent dans le schéma les mêmes champs de lifecycle/processor que les top-level. La sélection autonome et le replay opérationnel CPI restent à finaliser dans `pre.004`.
|
||||||
|
|
||||||
|
### 33.3 Replay Core v3
|
||||||
|
|
||||||
|
`MdCoreInstructionReplayInput` passe à la version 3 et peut transporter :
|
||||||
|
|
||||||
|
- `block_time` ;
|
||||||
|
- `stack_height` ;
|
||||||
|
- `parent_instruction_path` ;
|
||||||
|
- le contexte ordonné des instructions top-level ;
|
||||||
|
- la `returnData` transactionnelle.
|
||||||
|
|
||||||
|
Le champ de contexte nouveau est nommé `top_level_instructions_json`. Les usages historiques du terme `outer` dans des décodeurs existants ne sont pas renommés mécaniquement lorsqu'ils décrivent encore une sémantique propre à ces contrats.
|
||||||
|
|
||||||
|
Le constructeur historique reste disponible pour les tests et sources synthétiques ; les inputs provenant du Core persistant sont enrichis par le store.
|
||||||
|
|
||||||
|
### 33.4 Provenance de schéma N3
|
||||||
|
|
||||||
|
`k_sol_decode_events` réserve des champs nullable de provenance de schéma :
|
||||||
|
|
||||||
|
```text
|
||||||
|
schema_kind
|
||||||
|
schema_id
|
||||||
|
schema_version
|
||||||
|
schema_hash
|
||||||
|
```
|
||||||
|
|
||||||
|
Ils restent vides pour les décodeurs compilés actuels. Un futur mécanisme basé sur un schéma externe/IDL pourra les renseigner sans introduire de colonne Anchor ou modifier N1/N2.
|
||||||
|
|
||||||
|
### 33.5 `k_sol_mat_outputs`
|
||||||
|
|
||||||
|
Le renommage conceptuel est réalisé jusque dans l'API :
|
||||||
|
|
||||||
|
```text
|
||||||
|
MaterializedOutputFilter
|
||||||
|
MaterializedOutputQueryRow
|
||||||
|
DecodePipelineStore::list_materialized_outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
`k_sol_mat_outputs` reste le journal N3 générique versionné et la source de replay vers les futures projections N4.
|
||||||
|
|
||||||
|
### 33.6 Ressources PostgreSQL atomiques
|
||||||
|
|
||||||
|
La chaîne active historique `0001` à `0004` est retirée. Les scripts de maintenance monolithiques sont également retirés.
|
||||||
|
|
||||||
|
La nouvelle arborescence contient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
migrations/postgres/
|
||||||
|
schema/tables/ 16
|
||||||
|
schema/constraints/ 129
|
||||||
|
schema/indexes/ 59
|
||||||
|
maintenance/truncate/16
|
||||||
|
maintenance/drop/ 16
|
||||||
|
```
|
||||||
|
|
||||||
|
Soit **236 ressources SQL**. Chaque ressource contient une seule instruction top-level et est embarquée une seule fois par `include_str!`.
|
||||||
|
|
||||||
|
L'orchestrateur PostgreSQL global :
|
||||||
|
|
||||||
|
- valide les ressources ;
|
||||||
|
- refuse les objets historiques `kb_sol_*` ;
|
||||||
|
- ouvre une transaction ;
|
||||||
|
- prend un advisory lock ;
|
||||||
|
- applique tables, contraintes et index dans l'ordre ;
|
||||||
|
- commit uniquement lorsque le baseline complet est cohérent.
|
||||||
|
|
||||||
|
Les initialiseurs partiels raw/Core/decode sont supprimés, y compris des tests. Une initialisation ne peut donc plus valider accidentellement un sous-schéma comme si le baseline complet était prêt.
|
||||||
|
|
||||||
|
### 33.7 Diagnostics du schéma
|
||||||
|
|
||||||
|
Le diagnostic ne dépend plus de `_sqlx_migrations` pour déterminer si le baseline courant est disponible. Il compare les ressources connues réellement présentes.
|
||||||
|
|
||||||
|
Le résumé backend-agnostique expose :
|
||||||
|
|
||||||
|
- modèles logiques ;
|
||||||
|
- compte de tables ;
|
||||||
|
- compte d'index ;
|
||||||
|
- état ready/partial/not-initialized ;
|
||||||
|
|
||||||
|
sans publier les noms physiques.
|
||||||
|
|
||||||
|
Le backend PostgreSQL attend actuellement **75 index** : 59 index explicites et les 16 index de clés primaires produits par PostgreSQL.
|
||||||
|
|
||||||
|
### 33.8 Conclusion du canari future-decoder
|
||||||
|
|
||||||
|
Le canari a effectivement révélé deux pertes/absences structurelles de la baseline `0.5.2` :
|
||||||
|
|
||||||
|
- `returnData` était conservée en N1 mais perdue en N2 ;
|
||||||
|
- aucun chemin persistant générique d'observation/état de compte n'était réservé.
|
||||||
|
|
||||||
|
Ces deux lacunes sont corrigées avant gel.
|
||||||
|
|
||||||
|
Les autres metadata transactionnelles auditées (`fee`, rewards, compute units, cost units) restent conservées dans le document canonique N1. Elles ne sont pas nécessaires au décodage futur et ne justifient pas de gonfler l'ancre N2 avant gel ; une projection additive reste possible si un besoin queryable stable apparaît.
|
||||||
|
|
||||||
|
### 33.9 Travaux volontairement laissés à `pre.004+`
|
||||||
|
|
||||||
|
`pre.003` ne clôt pas encore :
|
||||||
|
|
||||||
|
- la lecture logique commune top-level+CPI et le replay CPI autonome ;
|
||||||
|
- l'ingestion/replay opérationnel du nouveau flux account observation -> account state ;
|
||||||
|
- l'invalidation descendante N1 -> N2 -> N3 -> N4 ;
|
||||||
|
- la pagination cursorisée ;
|
||||||
|
- la validation des index par plans réels sur dataset représentatif ;
|
||||||
|
- le snapshot machine-readable frozen ;
|
||||||
|
- la refonte fonctionnelle desktop finale ;
|
||||||
|
- les projections N4 metadata/trading.
|
||||||
|
|
||||||
|
Ces points restent dans les tranches suivantes et peuvent décaler le premier candidat de clôture au-delà de `pre.007`.
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
||||||
<!-- version: 64 -->
|
<!-- version: 65 -->
|
||||||
|
|
||||||
# CHANGELOG — kb-app-demo-desktop
|
# CHANGELOG — kb-app-demo-desktop
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
- adapte les vues de replay/journaux aux contrats N3 `MaterializedOutput*` et au journal `k_sol_mat_outputs` sans réintroduire de dépendance PostgreSQL dans les DTO desktop ;
|
||||||
|
- neutralise les derniers libellés visibles de sources physiques dans la fenêtre de candidats replay : l'UI décrit désormais les faits Core/logiques plutôt que les noms de tables PostgreSQL ;
|
||||||
|
- conserve la refonte fonctionnelle complète des compteurs/modèles du nouveau baseline pour la tranche consommateurs prévue par le plan.
|
||||||
|
|
||||||
## `0.5.3-pre.002-delta-fix-004`
|
## `0.5.3-pre.002-delta-fix-004`
|
||||||
|
|
||||||
- aligne les versions Tauri/Vite de l'application sur `0.5.3` dans `package.json` et `tauri.conf.json` ;
|
- aligne les versions Tauri/Vite de l'application sur `0.5.3` dans `package.json` et `tauri.conf.json` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-app-demo-desktop/frontend/demo_store_replay_candidates.html -->
|
<!-- file: kb-app-demo-desktop/frontend/demo_store_replay_candidates.html -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 16 -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2 class="h5 card-title mb-1">Mints</h2>
|
<h2 class="h5 card-title mb-1">Mints</h2>
|
||||||
<div id="mintResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
<div id="mintResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
||||||
<div class="small text-body-secondary">Source : <code>kb_sol_core_balance_changes.mint</code>.</div>
|
<div class="small text-body-secondary">Source : faits Core de variation de balance (mint).</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="btn-group flex-wrap" role="group" aria-label="Actions mints">
|
<div class="btn-group flex-wrap" role="group" aria-label="Actions mints">
|
||||||
@@ -339,7 +339,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2 class="h5 card-title mb-1">Owners</h2>
|
<h2 class="h5 card-title mb-1">Owners</h2>
|
||||||
<div id="ownerResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
<div id="ownerResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
||||||
<div class="small text-body-secondary">Source : <code>kb_sol_core_balance_changes.owner</code>.</div>
|
<div class="small text-body-secondary">Source : faits Core de variation de balance (owner).</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="btn-group flex-wrap" role="group" aria-label="Actions owners">
|
<div class="btn-group flex-wrap" role="group" aria-label="Actions owners">
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2 class="h5 card-title mb-1">Comptes</h2>
|
<h2 class="h5 card-title mb-1">Comptes</h2>
|
||||||
<div id="accountResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
<div id="accountResultSummary" class="small text-body-secondary">Aucune requête exécutée.</div>
|
||||||
<div class="small text-body-secondary">Source : <code>kb_sol_core_account_keys.account_key</code>.</div>
|
<div class="small text-body-secondary">Source : clés de comptes Core résolues.</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="btn-group flex-wrap" role="group" aria-label="Actions comptes">
|
<div class="btn-group flex-wrap" role="group" aria-label="Actions comptes">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
|
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
|
||||||
// version: 44
|
// version: 45
|
||||||
|
|
||||||
//! Tauri commands and UI payloads for contextual instruction decode replay.
|
//! Tauri commands and UI payloads for contextual instruction decode replay.
|
||||||
|
|
||||||
@@ -605,7 +605,7 @@ pub(crate) async fn demo_decode_replay_annotations(
|
|||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
request: crate::DemoTransactionAnnotationRequest,
|
request: crate::DemoTransactionAnnotationRequest,
|
||||||
) -> std::result::Result<std::vec::Vec<crate::DemoTransactionAnnotationRow>, std::string::String> {
|
) -> std::result::Result<std::vec::Vec<crate::DemoTransactionAnnotationRow>, std::string::String> {
|
||||||
let filter_result = ks_store::MaterializedEventFilter::new(
|
let filter_result = ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::Some("materializer.transaction.annotations".to_string()),
|
std::option::Option::Some("materializer.transaction.annotations".to_string()),
|
||||||
std::option::Option::Some("transaction_annotation".to_string()),
|
std::option::Option::Some("transaction_annotation".to_string()),
|
||||||
request.signature_contains,
|
request.signature_contains,
|
||||||
@@ -622,7 +622,7 @@ pub(crate) async fn demo_decode_replay_annotations(
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows_result =
|
let rows_result =
|
||||||
ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await;
|
ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await;
|
||||||
let rows = match rows_result {
|
let rows = match rows_result {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
@@ -882,7 +882,7 @@ fn coverage_payload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn annotation_payload(
|
fn annotation_payload(
|
||||||
row: ks_store::MaterializedEventQueryRow,
|
row: ks_store::MaterializedOutputQueryRow,
|
||||||
) -> std::result::Result<crate::DemoTransactionAnnotationRow, std::string::String> {
|
) -> std::result::Result<crate::DemoTransactionAnnotationRow, std::string::String> {
|
||||||
if row.processor_name != "materializer.transaction.annotations"
|
if row.processor_name != "materializer.transaction.annotations"
|
||||||
|| row.materialized_family != "transaction_annotation"
|
|| row.materialized_family != "transaction_annotation"
|
||||||
@@ -1274,7 +1274,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn committed_annotation_row_is_mapped_to_ui_safe_contract() {
|
fn committed_annotation_row_is_mapped_to_ui_safe_contract() {
|
||||||
let row = ks_store::MaterializedEventQueryRow {
|
let row = ks_store::MaterializedOutputQueryRow {
|
||||||
processor_name: "materializer.transaction.annotations".to_string(),
|
processor_name: "materializer.transaction.annotations".to_string(),
|
||||||
processor_version: "0.4.3".to_string(),
|
processor_version: "0.4.3".to_string(),
|
||||||
input_key: "input".to_string(),
|
input_key: "input".to_string(),
|
||||||
@@ -1310,7 +1310,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn malformed_annotation_payload_fails_closed() {
|
fn malformed_annotation_payload_fails_closed() {
|
||||||
let row = ks_store::MaterializedEventQueryRow {
|
let row = ks_store::MaterializedOutputQueryRow {
|
||||||
processor_name: "materializer.transaction.annotations".to_string(),
|
processor_name: "materializer.transaction.annotations".to_string(),
|
||||||
processor_version: "0.4.3".to_string(),
|
processor_version: "0.4.3".to_string(),
|
||||||
input_key: "input".to_string(),
|
input_key: "input".to_string(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
|
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
|
||||||
// version: 18
|
// version: 19
|
||||||
|
|
||||||
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
|
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ pub(crate) async fn demo_spl_ata_journal(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::Some("materializer.token.accounts".to_string()),
|
std::option::Option::Some("materializer.token.accounts".to_string()),
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
request.signature_contains.clone(),
|
request.signature_contains.clone(),
|
||||||
@@ -325,7 +325,7 @@ pub(crate) async fn demo_spl_ata_journal(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await
|
||||||
{
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
@@ -560,7 +560,7 @@ fn journal_matches(payload: &serde_json::Value, request: &crate::DemoSplAtaJourn
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn journal_row(row: ks_store::MaterializedEventQueryRow) -> crate::DemoSplAtaJournalRow {
|
fn journal_row(row: ks_store::MaterializedOutputQueryRow) -> crate::DemoSplAtaJournalRow {
|
||||||
let text = |key: &str| {
|
let text = |key: &str| {
|
||||||
return row
|
return row
|
||||||
.payload_json
|
.payload_json
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/demo_spl_token.rs
|
// file: kb-app-demo-desktop/src/demo_spl_token.rs
|
||||||
// version: 16
|
// version: 17
|
||||||
|
|
||||||
//! Thin Tauri adapter for classic SPL Token execution and materialized journals.
|
//! Thin Tauri adapter for classic SPL Token execution and materialized journals.
|
||||||
|
|
||||||
@@ -263,11 +263,11 @@ pub(crate) async fn demo_spl_token_journal(
|
|||||||
let needs_payload_filter =
|
let needs_payload_filter =
|
||||||
validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some();
|
validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some();
|
||||||
let query_limit = if needs_payload_filter {
|
let query_limit = if needs_payload_filter {
|
||||||
ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS
|
ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS
|
||||||
} else {
|
} else {
|
||||||
validated.limit
|
validated.limit
|
||||||
};
|
};
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
validated.family.clone(),
|
validated.family.clone(),
|
||||||
validated.signature_contains.clone(),
|
validated.signature_contains.clone(),
|
||||||
@@ -276,7 +276,7 @@ pub(crate) async fn demo_spl_token_journal(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(&store, &filter).await
|
||||||
{
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||||
@@ -412,10 +412,10 @@ fn replay_diagnostics(summary: &ks_pipeline::DecodeReplaySummary) -> serde_json:
|
|||||||
fn validate_journal_request(
|
fn validate_journal_request(
|
||||||
request: crate::DemoSplTokenJournalRequest,
|
request: crate::DemoSplTokenJournalRequest,
|
||||||
) -> std::result::Result<crate::DemoSplTokenJournalRequest, std::string::String> {
|
) -> std::result::Result<crate::DemoSplTokenJournalRequest, std::string::String> {
|
||||||
if request.limit == 0 || request.limit > ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS {
|
if request.limit == 0 || request.limit > ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS {
|
||||||
return std::result::Result::Err(format!(
|
return std::result::Result::Err(format!(
|
||||||
"journal limit must be between 1 and {}",
|
"journal limit must be between 1 and {}",
|
||||||
ks_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS
|
ks_store::MAX_MATERIALIZED_OUTPUT_QUERY_ROWS
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if request.profile_name.trim().is_empty() {
|
if request.profile_name.trim().is_empty() {
|
||||||
@@ -496,7 +496,7 @@ fn payload_matches(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn journal_row(row: ks_store::MaterializedEventQueryRow) -> DemoSplTokenJournalRow {
|
fn journal_row(row: ks_store::MaterializedOutputQueryRow) -> DemoSplTokenJournalRow {
|
||||||
let operation = row
|
let operation = row
|
||||||
.payload_json
|
.payload_json
|
||||||
.get("operation")
|
.get("operation")
|
||||||
@@ -602,7 +602,7 @@ mod tests {
|
|||||||
payload.get("amountRaw").and_then(serde_json::Value::as_str),
|
payload.get("amountRaw").and_then(serde_json::Value::as_str),
|
||||||
std::option::Option::Some("18446744073709551615")
|
std::option::Option::Some("18446744073709551615")
|
||||||
);
|
);
|
||||||
let row = super::journal_row(ks_store::MaterializedEventQueryRow {
|
let row = super::journal_row(ks_store::MaterializedOutputQueryRow {
|
||||||
processor_name: "materializer.token.accounts".to_string(),
|
processor_name: "materializer.token.accounts".to_string(),
|
||||||
processor_version: "0.4.4".to_string(),
|
processor_version: "0.4.4".to_string(),
|
||||||
input_key: "input".to_string(),
|
input_key: "input".to_string(),
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<!-- file: ks-lib/CHANGELOG.md -->
|
<!-- file: ks-lib/CHANGELOG.md -->
|
||||||
<!-- version: 53 -->
|
<!-- version: 54 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-lib
|
# CHANGELOG — ks-lib
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
- fait évoluer `MdCoreInstructionReplayInput` vers le contrat version 3 afin de transporter le contexte Core nécessaire aux futurs décodeurs : `block_time`, `stack_height`, parent CPI et `returnData` transactionnelle ;
|
||||||
|
- renomme le contexte de transaction `outer_instructions_json` en `top_level_instructions_json` dans ce nouveau contrat store/replay, sans supprimer les usages historiques du terme `outer` lorsqu'ils décrivent encore explicitement une sémantique de décodeur existante ;
|
||||||
|
- conserve le constructeur historique de replay pour les tests/sources synthétiques et ajoute l'enrichissement `with_core_context` utilisé par `ks-store`.
|
||||||
|
|
||||||
## `0.5.1-pre.008`
|
## `0.5.1-pre.008`
|
||||||
|
|
||||||
- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ;
|
- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-lib/src/decoder/api/contracts.rs
|
// file: ks-lib/src/decoder/api/contracts.rs
|
||||||
// version: 9
|
// version: 10
|
||||||
|
|
||||||
//! Backend-neutral contextual instruction decoder contracts.
|
//! Backend-neutral contextual instruction decoder contracts.
|
||||||
|
|
||||||
@@ -511,7 +511,7 @@ mod tests {
|
|||||||
fn contextual_input_hash_changes_when_outer_context_changes() {
|
fn contextual_input_hash_changes_when_outer_context_changes() {
|
||||||
let first = replay_input();
|
let first = replay_input();
|
||||||
let mut second = replay_input();
|
let mut second = replay_input();
|
||||||
second.outer_instructions_json = serde_json::json!([{
|
second.top_level_instructions_json = serde_json::json!([{
|
||||||
"instructionIndex": 0,
|
"instructionIndex": 0,
|
||||||
"instructionPath": "0",
|
"instructionPath": "0",
|
||||||
"programId": "program",
|
"programId": "program",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-lib/src/decoder/solana/core/compute_budget.rs
|
// file: ks-lib/src/decoder/solana/core/compute_budget.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
//! Maximal current and historical Compute Budget instruction decoding.
|
//! Maximal current and historical Compute Budget instruction decoding.
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ fn compute_budget_outer_rows(
|
|||||||
input: &crate::MdCoreInstructionReplayInput,
|
input: &crate::MdCoreInstructionReplayInput,
|
||||||
) -> std::vec::Vec<ComputeBudgetProfileRow> {
|
) -> std::vec::Vec<ComputeBudgetProfileRow> {
|
||||||
let mut rows = std::vec::Vec::new();
|
let mut rows = std::vec::Vec::new();
|
||||||
let outer_values = match input.outer_instructions_json.as_array() {
|
let outer_values = match input.top_level_instructions_json.as_array() {
|
||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
std::option::Option::None => return rows,
|
std::option::Option::None => return rows,
|
||||||
};
|
};
|
||||||
@@ -705,7 +705,7 @@ mod tests {
|
|||||||
fn replay_input_with_outer(
|
fn replay_input_with_outer(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
instruction_path: &str,
|
instruction_path: &str,
|
||||||
outer_instructions_json: serde_json::Value,
|
top_level_instructions_json: serde_json::Value,
|
||||||
) -> crate::MdCoreInstructionReplayInput {
|
) -> crate::MdCoreInstructionReplayInput {
|
||||||
let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
|
let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
|
||||||
let result = crate::MdCoreInstructionReplayInput::new(
|
let result = crate::MdCoreInstructionReplayInput::new(
|
||||||
@@ -720,7 +720,7 @@ mod tests {
|
|||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
std::option::Option::Some(serde_json::json!({"dataBase64": encoded})),
|
std::option::Option::Some(serde_json::json!({"dataBase64": encoded})),
|
||||||
std::option::Option::Some("payload-hash".to_string()),
|
std::option::Option::Some("payload-hash".to_string()),
|
||||||
outer_instructions_json,
|
top_level_instructions_json,
|
||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
serde_json::json!([]),
|
serde_json::json!([]),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-lib/src/decoder/solana/core/payload.rs
|
// file: ks-lib/src/decoder/solana/core/payload.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
//! Bounded native instruction payload helpers.
|
//! Bounded native instruction payload helpers.
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ fn resolve_explicit_outer_instruction(
|
|||||||
input: &crate::MdCoreInstructionReplayInput,
|
input: &crate::MdCoreInstructionReplayInput,
|
||||||
instruction_index: usize,
|
instruction_index: usize,
|
||||||
) -> ks_core::Result<crate::DcSolanaCoreResolvedInstructionPayload> {
|
) -> ks_core::Result<crate::DcSolanaCoreResolvedInstructionPayload> {
|
||||||
let entries = match input.outer_instructions_json.as_array() {
|
let entries = match input.top_level_instructions_json.as_array() {
|
||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
std::option::Option::None => {
|
std::option::Option::None => {
|
||||||
return std::result::Result::Err(ks_core::Error::invalid_state(
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-lib/src/decoder/solana/core/precompiles.rs
|
// file: ks-lib/src/decoder/solana/core/precompiles.rs
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
//! Bounded structural decoding for Solana signature verification precompiles.
|
//! Bounded structural decoding for Solana signature verification precompiles.
|
||||||
|
|
||||||
@@ -1599,11 +1599,11 @@ mod tests {
|
|||||||
for (program_id, target, external) in cases {
|
for (program_id, target, external) in cases {
|
||||||
let mut missing =
|
let mut missing =
|
||||||
replay_input(program_id, target.clone(), vec![(0, external.clone())], false);
|
replay_input(program_id, target.clone(), vec![(0, external.clone())], false);
|
||||||
missing.outer_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
|
missing.top_level_instructions_json[0]["payloadJson"] = serde_json::Value::Null;
|
||||||
let missing_result = crate::decoder_solana_core_precompiles_decode(&missing);
|
let missing_result = crate::decoder_solana_core_precompiles_decode(&missing);
|
||||||
assert_eq!(missing_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
|
assert_eq!(missing_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
|
||||||
let mut invalid = replay_input(program_id, target, vec![(0, external)], false);
|
let mut invalid = replay_input(program_id, target, vec![(0, external)], false);
|
||||||
invalid.outer_instructions_json[0]["payloadJson"] =
|
invalid.top_level_instructions_json[0]["payloadJson"] =
|
||||||
serde_json::json!({"dataBase64": "%%%"});
|
serde_json::json!({"dataBase64": "%%%"});
|
||||||
let invalid_result = crate::decoder_solana_core_precompiles_decode(&invalid);
|
let invalid_result = crate::decoder_solana_core_precompiles_decode(&invalid);
|
||||||
assert_eq!(invalid_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
|
assert_eq!(invalid_result.status, crate::DcApiDecoderOutcomeStatus::Failed);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-lib/src/decoder/solana/core/slashing.rs
|
// file: ks-lib/src/decoder/solana/core/slashing.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Exact structural decoding for the enshrined stateless Slashing Program.
|
//! Exact structural decoding for the enshrined stateless Slashing Program.
|
||||||
|
|
||||||
@@ -413,7 +413,7 @@ fn preceding_ed25519_instruction(input: &crate::MdCoreInstructionReplayInput) ->
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let previous = input.outer_instructions_json.as_array().and_then(|instructions| {
|
let previous = input.top_level_instructions_json.as_array().and_then(|instructions| {
|
||||||
return instructions.iter().find(|instruction| {
|
return instructions.iter().find(|instruction| {
|
||||||
return instruction
|
return instruction
|
||||||
.get("instructionIndex")
|
.get("instructionIndex")
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// file: ks-lib/src/model/replay.rs
|
// file: ks-lib/src/model/replay.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Source-neutral instruction replay input shared by decoders and stores.
|
//! Source-neutral instruction replay input shared by decoders and stores.
|
||||||
|
|
||||||
/// Version of the normalized core replay contract.
|
/// Version of the normalized core replay contract.
|
||||||
pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 2;
|
pub const MD_CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 3;
|
||||||
|
|
||||||
/// Decoder replay input containing one instruction plus extracted transaction context.
|
/// Decoder replay input containing one instruction plus extracted transaction context.
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||||
@@ -17,8 +17,14 @@ pub struct MdCoreInstructionReplayInput {
|
|||||||
pub signature: std::string::String,
|
pub signature: std::string::String,
|
||||||
/// Transaction slot in Solana unsigned representation.
|
/// Transaction slot in Solana unsigned representation.
|
||||||
pub slot: u64,
|
pub slot: u64,
|
||||||
|
/// Optional block timestamp as Unix seconds supplied by Solana.
|
||||||
|
pub block_time: std::option::Option<i64>,
|
||||||
/// Stable instruction path, for example `0` or `2/1`.
|
/// Stable instruction path, for example `0` or `2/1`.
|
||||||
pub instruction_path: std::string::String,
|
pub instruction_path: std::string::String,
|
||||||
|
/// Optional immediate or best-known parent instruction path for CPI inputs.
|
||||||
|
pub parent_instruction_path: std::option::Option<std::string::String>,
|
||||||
|
/// Optional runtime invocation stack height.
|
||||||
|
pub stack_height: std::option::Option<u32>,
|
||||||
/// Program id as non-empty base58 text.
|
/// Program id as non-empty base58 text.
|
||||||
pub program_id: std::string::String,
|
pub program_id: std::string::String,
|
||||||
/// Optional surface hint supplied by an operator or upstream classifier.
|
/// Optional surface hint supplied by an operator or upstream classifier.
|
||||||
@@ -35,8 +41,10 @@ pub struct MdCoreInstructionReplayInput {
|
|||||||
pub instruction_payload_json: std::option::Option<serde_json::Value>,
|
pub instruction_payload_json: std::option::Option<serde_json::Value>,
|
||||||
/// Optional deterministic hash of the retained instruction payload.
|
/// Optional deterministic hash of the retained instruction payload.
|
||||||
pub instruction_payload_hash: std::option::Option<std::string::String>,
|
pub instruction_payload_hash: std::option::Option<std::string::String>,
|
||||||
/// Ordered outer instructions for the transaction.
|
/// Optional transaction return-data fact retained by Core.
|
||||||
pub outer_instructions_json: serde_json::Value,
|
pub return_data: std::option::Option<crate::MdCanonicalReturnData>,
|
||||||
|
/// Ordered top-level instructions for the transaction.
|
||||||
|
pub top_level_instructions_json: serde_json::Value,
|
||||||
/// Inner instruction tree or subtree relevant to the target instruction.
|
/// Inner instruction tree or subtree relevant to the target instruction.
|
||||||
pub inner_instructions_json: serde_json::Value,
|
pub inner_instructions_json: serde_json::Value,
|
||||||
/// Ordered logs relevant to the transaction or target instruction.
|
/// Ordered logs relevant to the transaction or target instruction.
|
||||||
@@ -45,7 +53,7 @@ pub struct MdCoreInstructionReplayInput {
|
|||||||
pub balance_changes_json: serde_json::Value,
|
pub balance_changes_json: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MdCoreInstructionReplayInput {
|
impl crate::MdCoreInstructionReplayInput {
|
||||||
/// Builds a decoder replay input after validating its stable identity and array context.
|
/// Builds a decoder replay input after validating its stable identity and array context.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@@ -60,14 +68,14 @@ impl MdCoreInstructionReplayInput {
|
|||||||
instruction_accounts_json: serde_json::Value,
|
instruction_accounts_json: serde_json::Value,
|
||||||
instruction_payload_json: std::option::Option<serde_json::Value>,
|
instruction_payload_json: std::option::Option<serde_json::Value>,
|
||||||
instruction_payload_hash: std::option::Option<std::string::String>,
|
instruction_payload_hash: std::option::Option<std::string::String>,
|
||||||
outer_instructions_json: serde_json::Value,
|
top_level_instructions_json: serde_json::Value,
|
||||||
inner_instructions_json: serde_json::Value,
|
inner_instructions_json: serde_json::Value,
|
||||||
logs_json: serde_json::Value,
|
logs_json: serde_json::Value,
|
||||||
balance_changes_json: serde_json::Value,
|
balance_changes_json: serde_json::Value,
|
||||||
) -> ks_core::Result<Self> {
|
) -> ks_core::Result<Self> {
|
||||||
if !outer_instructions_json.is_array() {
|
if !top_level_instructions_json.is_array() {
|
||||||
return std::result::Result::Err(ks_core::Error::invalid_state(
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
"core replay input outer instructions must be a JSON array",
|
"core replay input top-level instructions must be a JSON array",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let value = Self {
|
let value = Self {
|
||||||
@@ -75,7 +83,10 @@ impl MdCoreInstructionReplayInput {
|
|||||||
replay_input_key: replay_input_key.into(),
|
replay_input_key: replay_input_key.into(),
|
||||||
signature: signature.into(),
|
signature: signature.into(),
|
||||||
slot,
|
slot,
|
||||||
|
block_time: std::option::Option::None,
|
||||||
instruction_path: instruction_path.into(),
|
instruction_path: instruction_path.into(),
|
||||||
|
parent_instruction_path: std::option::Option::None,
|
||||||
|
stack_height: std::option::Option::None,
|
||||||
program_id: program_id.into(),
|
program_id: program_id.into(),
|
||||||
surface_code_hint: std::option::Option::None,
|
surface_code_hint: std::option::Option::None,
|
||||||
transaction_failed,
|
transaction_failed,
|
||||||
@@ -84,7 +95,8 @@ impl MdCoreInstructionReplayInput {
|
|||||||
instruction_accounts_json,
|
instruction_accounts_json,
|
||||||
instruction_payload_json,
|
instruction_payload_json,
|
||||||
instruction_payload_hash,
|
instruction_payload_hash,
|
||||||
outer_instructions_json,
|
return_data: std::option::Option::None,
|
||||||
|
top_level_instructions_json,
|
||||||
inner_instructions_json,
|
inner_instructions_json,
|
||||||
logs_json,
|
logs_json,
|
||||||
balance_changes_json,
|
balance_changes_json,
|
||||||
@@ -124,6 +136,47 @@ impl MdCoreInstructionReplayInput {
|
|||||||
return std::result::Result::Ok(());
|
return std::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds transaction time, CPI ancestry and return-data context retained by Core.
|
||||||
|
pub fn with_core_context(
|
||||||
|
mut self,
|
||||||
|
block_time: std::option::Option<i64>,
|
||||||
|
parent_instruction_path: std::option::Option<std::string::String>,
|
||||||
|
stack_height: std::option::Option<u32>,
|
||||||
|
return_data: std::option::Option<crate::MdCanonicalReturnData>,
|
||||||
|
) -> ks_core::Result<Self> {
|
||||||
|
if parent_instruction_path
|
||||||
|
.as_deref()
|
||||||
|
.is_some_and(|value| return value.trim().is_empty())
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"core replay parent instruction path must not be empty when present",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if stack_height.is_some_and(|value| return value == 0) {
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"core replay stack height must be greater than zero when present",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if parent_instruction_path.is_some() && stack_height.is_some_and(|value| return value <= 1)
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"core replay CPI stack height must be greater than one when present",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let std::option::Option::Some(value) = &return_data {
|
||||||
|
if value.program_id.trim().is_empty() {
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"core replay return-data program id must not be empty",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.block_time = block_time;
|
||||||
|
self.parent_instruction_path = parent_instruction_path;
|
||||||
|
self.stack_height = stack_height;
|
||||||
|
self.return_data = return_data;
|
||||||
|
return std::result::Result::Ok(self);
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds an optional stable surface hint after validation.
|
/// Adds an optional stable surface hint after validation.
|
||||||
pub fn with_surface_code_hint(
|
pub fn with_surface_code_hint(
|
||||||
mut self,
|
mut self,
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
<!-- file: ks-logging/CHANGELOG.md -->
|
<!-- file: ks-logging/CHANGELOG.md -->
|
||||||
<!-- version: 20 -->
|
<!-- version: 22 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-logging
|
# CHANGELOG — ks-logging
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
### Fix `pre.003-delta-fix-005`
|
||||||
|
|
||||||
|
- ajoute `h2` et `hyper_util` à `WARN` dans les profils standards ; `hyper` reste explicitement filtré à `WARN` ;
|
||||||
|
- étend le canari de configuration aux six targets tierces bruyantes : `h2`, `hyper`, `hyper_util`, `rustls`, `rustls_platform_verifier` et `sqlx` ;
|
||||||
|
- documente que le statement logging automatique SQLx est désormais désactivé dans `ks-store`, les traces fonctionnelles de requêtes devant être produites par `ks-store` lui-même.
|
||||||
|
|
||||||
|
|
||||||
|
- ajoute `rustls`, `rustls_platform_verifier` et `sqlx` à `WARN` dans les `target_filters` des profils standards `local_devnet`, `mainnet_research` et `mainnet` ;
|
||||||
|
- aligne l'exemple de configuration et ajoute un test vérifiant cette politique pour chaque profil ;
|
||||||
|
- documente que les logs détaillés des dépendances doivent être réactivés par une route explicite, sans changer leurs targets propriétaires.
|
||||||
|
|
||||||
## `0.5.1-pre.008`
|
## `0.5.1-pre.008`
|
||||||
|
|
||||||
- Fix: remplace un ancien fragment de credential Helius utilisé comme canari négatif par le nom synthétique `KS_SECRET_HELIUS_API_KEY`; aucune donnée réelle ou partielle de credential ne doit être conservée dans les tests.
|
- Fix: remplace un ancien fragment de credential Helius utilisé comme canari négatif par le nom synthétique `KS_SECRET_HELIUS_API_KEY`; aucune donnée réelle ou partielle de credential ne doit être conservée dans les tests.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-logging/USAGE.md -->
|
<!-- file: ks-logging/USAGE.md -->
|
||||||
<!-- version: 7 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Utilisation de ks-logging
|
# Utilisation de ks-logging
|
||||||
|
|
||||||
@@ -58,3 +58,12 @@ match ks_logging::init_logging(&config) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
L'initialisation reste globale au processus. Les binaires doivent donc résoudre leur configuration avant le premier appel à `init_logging`.
|
L'initialisation reste globale au processus. Les binaires doivent donc résoudre leur configuration avant le premier appel à `init_logging`.
|
||||||
|
|
||||||
|
## Filtres des dépendances tierces
|
||||||
|
|
||||||
|
Les routes wildcard `debug` restent destinées aux composants Khadhroony, mais elles ne doivent pas transformer la console et `debug.log` en trace brute des bibliothèques réseau ou SQL.
|
||||||
|
|
||||||
|
Les profils standards imposent `WARN` aux targets `h2`, `hyper`, `hyper_util`, `rustls`, `rustls_platform_verifier` et `sqlx`. Les targets tiers restent ceux de leurs crates propriétaires : un événement HTTP/2 reste `h2::*`, un événement Hyper reste `hyper*`, un événement SQLx reste `sqlx::query` et un événement TLS reste `rustls::*`.
|
||||||
|
|
||||||
|
`ks-store` désactive en complément le statement logging automatique de SQLx. Les requêtes fonctionnellement importantes doivent être instrumentées par `ks-store` avec son target canonique et des champs structurés, sans dépendre du format de logging d'une bibliothèque tierce. Les warnings/errors des dépendances restent observables dans les profils standards.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-logging/src/document.rs
|
// file: ks-logging/src/document.rs
|
||||||
// version: 6
|
// version: 8
|
||||||
|
|
||||||
//! Independent logging configuration document loading and validation.
|
//! Independent logging configuration document loading and validation.
|
||||||
|
|
||||||
@@ -521,6 +521,32 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_logging_config_suppresses_verbose_dependency_targets() {
|
||||||
|
let mut value = parse_default_value();
|
||||||
|
value["logs_directory"] = serde_json::Value::String("logs".to_string());
|
||||||
|
let raw_json = value_to_json(&value);
|
||||||
|
let document_result = super::parse_logging_json(&raw_json);
|
||||||
|
let document = match document_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => panic!("default logging config must parse: {error}"),
|
||||||
|
};
|
||||||
|
for profile in &document.profiles {
|
||||||
|
for dependency_target in
|
||||||
|
["h2", "hyper", "hyper_util", "rustls", "rustls_platform_verifier", "sqlx"]
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
profile.config.target_filters.iter().any(|filter| {
|
||||||
|
return filter.target == dependency_target && filter.level == "warn";
|
||||||
|
}),
|
||||||
|
"logging profile {} must suppress verbose {} events",
|
||||||
|
profile.name,
|
||||||
|
dependency_target
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() {
|
fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() {
|
||||||
assert!(!DEFAULT_LOGGING_CONFIG.contains("KS_SECRET_HELIUS_API_KEY"));
|
assert!(!DEFAULT_LOGGING_CONFIG.contains("KS_SECRET_HELIUS_API_KEY"));
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
<!-- file: ks-pipeline-demo-scenarios/CHANGELOG.md -->
|
<!-- file: ks-pipeline-demo-scenarios/CHANGELOG.md -->
|
||||||
<!-- version: 82 -->
|
<!-- version: 83 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-pipeline-demo-scenarios
|
# CHANGELOG — ks-pipeline-demo-scenarios
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
- adapte les scénarios réutilisables au renommage N3 `MaterializedOutput*` / `list_materialized_outputs` sans modifier les campagnes Devnet ni leurs critères de succès ;
|
||||||
|
- conserve la façade `Store` backend-agnostique introduite en `pre.002` pendant la reconstruction physique PostgreSQL de `pre.003`.
|
||||||
|
|
||||||
## `0.5.3-pre.002`
|
## `0.5.3-pre.002`
|
||||||
|
|
||||||
- remplace les constructions `PostgresStore`/`PostgresStoreOptions` des scénarios par `ks_store::Store` et `StoreOpenOptions` ;
|
- remplace les constructions `PostgresStore`/`PostgresStoreOptions` des scénarios par `ks_store::Store` et `StoreOpenOptions` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
|
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
|
||||||
// version: 26
|
// version: 27
|
||||||
|
|
||||||
//! Real Devnet simulation and submission for current Metaplex Token Metadata operations.
|
//! Real Devnet simulation and submission for current Metaplex Token Metadata operations.
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ pub struct DevnetMetaplexTokenMetadataExecutionSummary {
|
|||||||
/// Second replay proving idempotence for the exact same input.
|
/// Second replay proving idempotence for the exact same input.
|
||||||
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
||||||
/// Exact materialized rows produced for the submitted Metaplex transaction.
|
/// Exact materialized rows produced for the submitted Metaplex transaction.
|
||||||
pub materializations: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
pub materializations: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
/// Aggregated canonical post-execution diagnostic.
|
/// Aggregated canonical post-execution diagnostic.
|
||||||
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
||||||
}
|
}
|
||||||
@@ -758,7 +758,7 @@ where
|
|||||||
.push(replay_summary_diagnostic("first Metaplex replay", &first_replay));
|
.push(replay_summary_diagnostic("first Metaplex replay", &first_replay));
|
||||||
}
|
}
|
||||||
summary.decode_replay = std::option::Option::Some(first_replay);
|
summary.decode_replay = std::option::Option::Some(first_replay);
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -767,7 +767,8 @@ where
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
|
||||||
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
diagnostic
|
diagnostic
|
||||||
@@ -1119,8 +1120,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn metaplex_instruction_materializations(
|
fn metaplex_instruction_materializations(
|
||||||
rows: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
rows: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
) -> std::vec::Vec<ks_store::MaterializedEventQueryRow> {
|
) -> std::vec::Vec<ks_store::MaterializedOutputQueryRow> {
|
||||||
let materializer_name = ks_lib::MtApiEventMaterializer::identity(
|
let materializer_name = ks_lib::MtApiEventMaterializer::identity(
|
||||||
&ks_lib::MtMetadataMetaplexTokenMetadataMaterializer,
|
&ks_lib::MtMetadataMetaplexTokenMetadataMaterializer,
|
||||||
)
|
)
|
||||||
@@ -1613,8 +1614,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn materialization_evidence_is_owned_by_the_metaplex_materializer() {
|
fn materialization_evidence_is_owned_by_the_metaplex_materializer() {
|
||||||
fn row(processor_name: &str) -> ks_store::MaterializedEventQueryRow {
|
fn row(processor_name: &str) -> ks_store::MaterializedOutputQueryRow {
|
||||||
return ks_store::MaterializedEventQueryRow {
|
return ks_store::MaterializedOutputQueryRow {
|
||||||
processor_name: processor_name.to_string(),
|
processor_name: processor_name.to_string(),
|
||||||
processor_version: "0.4.8-pre.13".to_string(),
|
processor_version: "0.4.8-pre.13".to_string(),
|
||||||
input_key: "input".to_string(),
|
input_key: "input".to_string(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
|
||||||
// version: 19
|
// version: 20
|
||||||
|
|
||||||
//! Devnet ATA execution with stateful and canonical post-validation.
|
//! Devnet ATA execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ pub struct DevnetSplAssociatedTokenAccountExecutionSummary {
|
|||||||
/// Second replay proving idempotence for the same decoder version and input.
|
/// Second replay proving idempotence for the same decoder version and input.
|
||||||
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
||||||
/// Exact materialized rows produced for the submitted Token transaction.
|
/// Exact materialized rows produced for the submitted Token transaction.
|
||||||
pub materializations: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
pub materializations: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
/// Aggregated post-execution validation diagnostic.
|
/// Aggregated post-execution validation diagnostic.
|
||||||
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
||||||
}
|
}
|
||||||
@@ -424,7 +424,7 @@ where
|
|||||||
};
|
};
|
||||||
diagnostic.decode_replayed = decode_completed(&first_replay);
|
diagnostic.decode_replayed = decode_completed(&first_replay);
|
||||||
summary.decode_replay = std::option::Option::Some(first_replay);
|
summary.decode_replay = std::option::Option::Some(first_replay);
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -433,7 +433,8 @@ where
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
|
||||||
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
diagnostic.diagnostics.push(format!(
|
diagnostic.diagnostics.push(format!(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
|
||||||
// version: 15
|
// version: 16
|
||||||
|
|
||||||
//! Devnet SPL Memo v4 execution with canonical post-validation.
|
//! Devnet SPL Memo v4 execution with canonical post-validation.
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ pub struct DevnetMemoExecutionSummary {
|
|||||||
/// Second replay proving that the same decoder version and input are idempotent.
|
/// Second replay proving that the same decoder version and input are idempotent.
|
||||||
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
||||||
/// Exact persisted transaction annotation rows for the submitted signature.
|
/// Exact persisted transaction annotation rows for the submitted signature.
|
||||||
pub annotations: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
pub annotations: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
/// Aggregated post-execution validation diagnostic.
|
/// Aggregated post-execution validation diagnostic.
|
||||||
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
||||||
}
|
}
|
||||||
@@ -490,7 +490,7 @@ where
|
|||||||
};
|
};
|
||||||
diagnostic.decode_replayed = decode_completed(&first_replay);
|
diagnostic.decode_replayed = decode_completed(&first_replay);
|
||||||
summary.decode_replay = std::option::Option::Some(first_replay);
|
summary.decode_replay = std::option::Option::Some(first_replay);
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::Some("materializer.transaction.annotations".to_string()),
|
std::option::Option::Some("materializer.transaction.annotations".to_string()),
|
||||||
std::option::Option::Some("transaction_annotation".to_string()),
|
std::option::Option::Some("transaction_annotation".to_string()),
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -500,7 +500,7 @@ where
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
summary.annotations =
|
summary.annotations =
|
||||||
match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
diagnostic.diagnostics.push(format!("Memo annotation query failed: {error}"));
|
diagnostic.diagnostics.push(format!("Memo annotation query failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs
|
||||||
// version: 19
|
// version: 20
|
||||||
|
|
||||||
//! Devnet classic SPL Token execution with stateful and canonical post-validation.
|
//! Devnet classic SPL Token execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ pub struct DevnetSplTokenExecutionSummary {
|
|||||||
/// Second replay proving idempotence for the same decoder version and input.
|
/// Second replay proving idempotence for the same decoder version and input.
|
||||||
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
||||||
/// Exact materialized rows produced for the submitted Token transaction.
|
/// Exact materialized rows produced for the submitted Token transaction.
|
||||||
pub materializations: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
pub materializations: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
/// Aggregated post-execution validation diagnostic.
|
/// Aggregated post-execution validation diagnostic.
|
||||||
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
||||||
}
|
}
|
||||||
@@ -390,7 +390,7 @@ where
|
|||||||
};
|
};
|
||||||
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
|
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
|
||||||
summary.decode_replay = std::option::Option::Some(first_replay);
|
summary.decode_replay = std::option::Option::Some(first_replay);
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -399,7 +399,8 @@ where
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
|
||||||
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
diagnostic
|
diagnostic
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
|
||||||
// version: 18
|
// version: 19
|
||||||
|
|
||||||
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
|
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
|
||||||
|
|
||||||
@@ -662,7 +662,7 @@ where
|
|||||||
"lifecycle predecessor decode and materialization did not complete",
|
"lifecycle predecessor decode and materialization did not complete",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -671,7 +671,8 @@ where
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
|
||||||
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
|
||||||
// version: 12
|
// version: 13
|
||||||
|
|
||||||
//! Devnet Token-2022 execution with stateful and canonical post-validation.
|
//! Devnet Token-2022 execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ pub struct DevnetSplToken2022ExecutionSummary {
|
|||||||
/// Second replay proving idempotence for the same decoder version and input.
|
/// Second replay proving idempotence for the same decoder version and input.
|
||||||
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
pub idempotence_replay: std::option::Option<ks_pipeline::DecodeReplaySummary>,
|
||||||
/// Exact materialized rows produced for the submitted Token transaction.
|
/// Exact materialized rows produced for the submitted Token transaction.
|
||||||
pub materializations: std::vec::Vec<ks_store::MaterializedEventQueryRow>,
|
pub materializations: std::vec::Vec<ks_store::MaterializedOutputQueryRow>,
|
||||||
/// Aggregated post-execution validation diagnostic.
|
/// Aggregated post-execution validation diagnostic.
|
||||||
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
pub post_execution: std::option::Option<ks_lib::ExApiPostExecutionDiagnostic>,
|
||||||
}
|
}
|
||||||
@@ -390,7 +390,7 @@ where
|
|||||||
};
|
};
|
||||||
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
|
diagnostic.decode_replayed = crate::decode_completed(&first_replay);
|
||||||
summary.decode_replay = std::option::Option::Some(first_replay);
|
summary.decode_replay = std::option::Option::Some(first_replay);
|
||||||
let filter = match ks_store::MaterializedEventFilter::new(
|
let filter = match ks_store::MaterializedOutputFilter::new(
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
std::option::Option::Some(signature.0.clone()),
|
std::option::Option::Some(signature.0.clone()),
|
||||||
@@ -399,7 +399,8 @@ where
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let rows = match ks_store::DecodePipelineStore::list_materialized_events(store, &filter).await {
|
let rows = match ks_store::DecodePipelineStore::list_materialized_outputs(store, &filter).await
|
||||||
|
{
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
diagnostic
|
diagnostic
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
<!-- file: ks-pipeline/CHANGELOG.md -->
|
<!-- file: ks-pipeline/CHANGELOG.md -->
|
||||||
<!-- version: 35 -->
|
<!-- version: 37 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-pipeline
|
# CHANGELOG — ks-pipeline
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
- corrige le premier delta `pre.003` en utilisant le nom canonique exporté `MdCanonicalInnerInstructionGroup` pour la reconstruction des chemins CPI.
|
||||||
|
- aligne Core extraction sur le nouveau baseline `ks-store` : conservation de `block_time`, `stack_height`, `returnData` et persistance du contexte N2 nécessaire au replay ;
|
||||||
|
- reconstruit les chemins CPI imbriqués à partir de `stackHeight` lorsque l'information est disponible et conserve un fallback déterministe vers la top-level lorsque Solana ne fournit pas cette hauteur ;
|
||||||
|
- enrichit les inputs N2 -> N3 avec le contexte Core v3 sans modifier le dispatch fonctionnel des décodeurs actuels ;
|
||||||
|
- adapte les lectures de matérialisation au contrat `MaterializedOutput*` renommé avec `k_sol_mat_outputs`.
|
||||||
|
|
||||||
## `0.5.1-pre.006`
|
## `0.5.1-pre.006`
|
||||||
|
|
||||||
- déplace ses fixtures de configuration vers le contrat runtime résolu sous `test-fixtures/config/` ;
|
- déplace ses fixtures de configuration vers le contrat runtime résolu sous `test-fixtures/config/` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline/src/backfill.rs
|
// file: ks-pipeline/src/backfill.rs
|
||||||
// version: 11
|
// version: 12
|
||||||
|
|
||||||
//! Reusable HTTP transaction backfill orchestration.
|
//! Reusable HTTP transaction backfill orchestration.
|
||||||
|
|
||||||
@@ -1409,7 +1409,7 @@ where
|
|||||||
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
|
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
|
||||||
let observation = match payload_result {
|
let observation = match payload_result {
|
||||||
std::result::Result::Ok(value) => {
|
std::result::Result::Ok(value) => {
|
||||||
value.with_status(ks_store::TransactionObservationStatus::Persisted)
|
value.with_status(ks_store::AcquisitionObservationStatus::Persisted)
|
||||||
},
|
},
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
@@ -1456,7 +1456,7 @@ where
|
|||||||
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
|
observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash);
|
||||||
let observation = match payload_result {
|
let observation = match payload_result {
|
||||||
std::result::Result::Ok(value) => {
|
std::result::Result::Ok(value) => {
|
||||||
value.with_status(ks_store::TransactionObservationStatus::Missing)
|
value.with_status(ks_store::AcquisitionObservationStatus::Missing)
|
||||||
},
|
},
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
@@ -1538,7 +1538,7 @@ fn base_observation(
|
|||||||
client.provider().to_string(),
|
client.provider().to_string(),
|
||||||
"solana_http_json_rpc".to_string(),
|
"solana_http_json_rpc".to_string(),
|
||||||
"getTransaction".to_string(),
|
"getTransaction".to_string(),
|
||||||
ks_store::TransactionObservationOrigin::Backfill,
|
ks_store::AcquisitionObservationOrigin::Backfill,
|
||||||
received_at,
|
received_at,
|
||||||
);
|
);
|
||||||
let observation = match observation_result {
|
let observation = match observation_result {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline/src/core_extraction.rs
|
// file: ks-pipeline/src/core_extraction.rs
|
||||||
// version: 11
|
// version: 13
|
||||||
|
|
||||||
//! Canonical Solana transaction to normalized core extraction pipeline.
|
//! Canonical Solana transaction to normalized core extraction pipeline.
|
||||||
|
|
||||||
@@ -568,6 +568,11 @@ pub fn extract_raw_transaction_to_core(
|
|||||||
"canonical transaction slot does not match raw row slot",
|
"canonical transaction slot does not match raw row slot",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if transaction.block_time != row.block_time {
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"canonical transaction block time does not match raw row block time",
|
||||||
|
));
|
||||||
|
}
|
||||||
let computed_hash_result = transaction.canonical_json_hash();
|
let computed_hash_result = transaction.canonical_json_hash();
|
||||||
let computed_hash = match computed_hash_result {
|
let computed_hash = match computed_hash_result {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -594,6 +599,7 @@ pub fn extract_raw_transaction_to_core(
|
|||||||
let core_transaction_result = ks_store::CoreTransactionInsert::new(
|
let core_transaction_result = ks_store::CoreTransactionInsert::new(
|
||||||
transaction.primary_signature.clone(),
|
transaction.primary_signature.clone(),
|
||||||
transaction.slot,
|
transaction.slot,
|
||||||
|
transaction.block_time,
|
||||||
false,
|
false,
|
||||||
std::option::Option::None,
|
std::option::Option::None,
|
||||||
);
|
);
|
||||||
@@ -620,6 +626,7 @@ pub fn extract_raw_transaction_to_core(
|
|||||||
inner_instructions,
|
inner_instructions,
|
||||||
logs: std::vec::Vec::new(),
|
logs: std::vec::Vec::new(),
|
||||||
balance_changes: std::vec::Vec::new(),
|
balance_changes: std::vec::Vec::new(),
|
||||||
|
return_data: std::option::Option::None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,6 +640,7 @@ fn build_bundle_with_metadata(
|
|||||||
let core_transaction_result = ks_store::CoreTransactionInsert::new(
|
let core_transaction_result = ks_store::CoreTransactionInsert::new(
|
||||||
transaction.primary_signature.clone(),
|
transaction.primary_signature.clone(),
|
||||||
transaction.slot,
|
transaction.slot,
|
||||||
|
transaction.block_time,
|
||||||
failed,
|
failed,
|
||||||
metadata.error.clone(),
|
metadata.error.clone(),
|
||||||
);
|
);
|
||||||
@@ -660,6 +668,11 @@ fn build_bundle_with_metadata(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
|
let return_data_result = crate::core_extraction::extract_return_data(transaction, metadata);
|
||||||
|
let return_data = match return_data_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
return std::result::Result::Ok(ks_store::CoreExtractionBundle {
|
return std::result::Result::Ok(ks_store::CoreExtractionBundle {
|
||||||
raw_transaction_id: row.id,
|
raw_transaction_id: row.id,
|
||||||
ledger_identity,
|
ledger_identity,
|
||||||
@@ -669,9 +682,32 @@ fn build_bundle_with_metadata(
|
|||||||
inner_instructions,
|
inner_instructions,
|
||||||
logs,
|
logs,
|
||||||
balance_changes,
|
balance_changes,
|
||||||
|
return_data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_return_data(
|
||||||
|
transaction: &ks_lib::MdCanonicalTransaction,
|
||||||
|
metadata: &ks_lib::MdCanonicalTransactionMetadata,
|
||||||
|
) -> ks_core::Result<std::option::Option<ks_store::CoreReturnDataInsert>> {
|
||||||
|
let value = match &metadata.return_data {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
|
||||||
|
};
|
||||||
|
let insert_result = ks_store::CoreReturnDataInsert::new(
|
||||||
|
transaction.primary_signature.clone(),
|
||||||
|
transaction.slot,
|
||||||
|
value.program_id.clone(),
|
||||||
|
value.data_base64.clone(),
|
||||||
|
);
|
||||||
|
return match insert_result {
|
||||||
|
std::result::Result::Ok(insert) => {
|
||||||
|
std::result::Result::Ok(std::option::Option::Some(insert))
|
||||||
|
},
|
||||||
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn ledger_identity_from_raw(
|
fn ledger_identity_from_raw(
|
||||||
row: &ks_store::RawTransactionRow,
|
row: &ks_store::RawTransactionRow,
|
||||||
) -> ks_core::Result<ks_store::ProcessingLedgerIdentity> {
|
) -> ks_core::Result<ks_store::ProcessingLedgerIdentity> {
|
||||||
@@ -819,6 +855,7 @@ fn extract_instructions(
|
|||||||
transaction.slot,
|
transaction.slot,
|
||||||
path,
|
path,
|
||||||
program_id,
|
program_id,
|
||||||
|
instruction.stack_height,
|
||||||
accounts_json,
|
accounts_json,
|
||||||
payload_json,
|
payload_json,
|
||||||
);
|
);
|
||||||
@@ -836,9 +873,12 @@ fn extract_instructions(
|
|||||||
let mut inner_instructions = std::vec::Vec::new();
|
let mut inner_instructions = std::vec::Vec::new();
|
||||||
if let std::option::Option::Some(metadata) = &transaction.metadata {
|
if let std::option::Option::Some(metadata) = &transaction.metadata {
|
||||||
for group in &metadata.inner_instructions {
|
for group in &metadata.inner_instructions {
|
||||||
let parent_path = group.parent_instruction_index.to_string();
|
let paths_result = crate::core_extraction::resolve_inner_instruction_paths(group);
|
||||||
for (inner_index, instruction) in group.instructions.iter().enumerate() {
|
let paths = match paths_result {
|
||||||
let path = format!("{parent_path}/{inner_index}");
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
for (instruction, path) in group.instructions.iter().zip(paths.iter()) {
|
||||||
let resolved_result =
|
let resolved_result =
|
||||||
crate::core_extraction::resolve_instruction(&keys, instruction);
|
crate::core_extraction::resolve_instruction(&keys, instruction);
|
||||||
let (program_id, accounts_json, payload_json, payload_hash) = match resolved_result
|
let (program_id, accounts_json, payload_json, payload_hash) = match resolved_result
|
||||||
@@ -849,9 +889,10 @@ fn extract_instructions(
|
|||||||
let insert_result = ks_store::CoreInnerInstructionInsert::new(
|
let insert_result = ks_store::CoreInnerInstructionInsert::new(
|
||||||
transaction.primary_signature.clone(),
|
transaction.primary_signature.clone(),
|
||||||
transaction.slot,
|
transaction.slot,
|
||||||
parent_path.clone(),
|
path.0.clone(),
|
||||||
path,
|
path.1.clone(),
|
||||||
program_id,
|
program_id,
|
||||||
|
instruction.stack_height,
|
||||||
accounts_json,
|
accounts_json,
|
||||||
payload_json,
|
payload_json,
|
||||||
);
|
);
|
||||||
@@ -871,6 +912,57 @@ fn extract_instructions(
|
|||||||
return std::result::Result::Ok((instructions, inner_instructions));
|
return std::result::Result::Ok((instructions, inner_instructions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_inner_instruction_paths(
|
||||||
|
group: &ks_lib::MdCanonicalInnerInstructionGroup,
|
||||||
|
) -> ks_core::Result<std::vec::Vec<(std::string::String, std::string::String)>> {
|
||||||
|
let root_path = group.parent_instruction_index.to_string();
|
||||||
|
let mut stack = std::vec::Vec::<(u32, std::string::String)>::new();
|
||||||
|
let mut child_counts = std::collections::BTreeMap::<std::string::String, u32>::new();
|
||||||
|
let mut output = std::vec::Vec::with_capacity(group.instructions.len());
|
||||||
|
for instruction in &group.instructions {
|
||||||
|
let parent_path = match instruction.stack_height {
|
||||||
|
std::option::Option::Some(height) if height <= 1 => {
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"inner instruction stack height must be greater than one",
|
||||||
|
));
|
||||||
|
},
|
||||||
|
std::option::Option::Some(2) => {
|
||||||
|
stack.clear();
|
||||||
|
root_path.clone()
|
||||||
|
},
|
||||||
|
std::option::Option::Some(height) => {
|
||||||
|
while stack.last().is_some_and(|entry| return entry.0 >= height) {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
match stack.last() {
|
||||||
|
std::option::Option::Some((parent_height, path))
|
||||||
|
if parent_height.saturating_add(1) == height =>
|
||||||
|
{
|
||||||
|
path.clone()
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return std::result::Result::Err(ks_core::Error::invalid_state(
|
||||||
|
"inner instruction stack height is inconsistent with invocation order",
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std::option::Option::None => {
|
||||||
|
stack.clear();
|
||||||
|
root_path.clone()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let child_index = child_counts.entry(parent_path.clone()).or_insert(0);
|
||||||
|
let instruction_path = format!("{parent_path}/{}", *child_index);
|
||||||
|
*child_index = child_index.saturating_add(1);
|
||||||
|
if let std::option::Option::Some(height) = instruction.stack_height {
|
||||||
|
stack.push((height, instruction_path.clone()));
|
||||||
|
}
|
||||||
|
output.push((parent_path, instruction_path));
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(output);
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_instruction(
|
fn resolve_instruction(
|
||||||
keys: &[std::string::String],
|
keys: &[std::string::String],
|
||||||
instruction: &ks_lib::MdCanonicalCompiledInstruction,
|
instruction: &ks_lib::MdCanonicalCompiledInstruction,
|
||||||
@@ -918,7 +1010,11 @@ fn extract_logs(
|
|||||||
metadata: &ks_lib::MdCanonicalTransactionMetadata,
|
metadata: &ks_lib::MdCanonicalTransactionMetadata,
|
||||||
) -> ks_core::Result<std::vec::Vec<ks_store::CoreLogInsert>> {
|
) -> ks_core::Result<std::vec::Vec<ks_store::CoreLogInsert>> {
|
||||||
let mut output = std::vec::Vec::with_capacity(metadata.log_messages.len());
|
let mut output = std::vec::Vec::with_capacity(metadata.log_messages.len());
|
||||||
let links = crate::core_extraction::resolve_log_links(transaction, metadata);
|
let links_result = crate::core_extraction::resolve_log_links(transaction, metadata);
|
||||||
|
let links = match links_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
for (index, text) in metadata.log_messages.iter().enumerate() {
|
for (index, text) in metadata.log_messages.iter().enumerate() {
|
||||||
let log_index_result = u32::try_from(index);
|
let log_index_result = u32::try_from(index);
|
||||||
let log_index = match log_index_result {
|
let log_index = match log_index_result {
|
||||||
@@ -959,10 +1055,12 @@ fn extract_logs(
|
|||||||
fn resolve_log_links(
|
fn resolve_log_links(
|
||||||
transaction: &ks_lib::MdCanonicalTransaction,
|
transaction: &ks_lib::MdCanonicalTransaction,
|
||||||
metadata: &ks_lib::MdCanonicalTransactionMetadata,
|
metadata: &ks_lib::MdCanonicalTransactionMetadata,
|
||||||
) -> std::vec::Vec<(
|
) -> ks_core::Result<
|
||||||
std::option::Option<std::string::String>,
|
std::vec::Vec<(
|
||||||
std::option::Option<std::string::String>,
|
std::option::Option<std::string::String>,
|
||||||
)> {
|
std::option::Option<std::string::String>,
|
||||||
|
)>,
|
||||||
|
> {
|
||||||
let keys = crate::core_extraction::resolved_account_keys(transaction);
|
let keys = crate::core_extraction::resolved_account_keys(transaction);
|
||||||
let mut top_level = std::vec::Vec::<(std::string::String, std::string::String)>::new();
|
let mut top_level = std::vec::Vec::<(std::string::String, std::string::String)>::new();
|
||||||
for (index, instruction) in transaction.message.instructions.iter().enumerate() {
|
for (index, instruction) in transaction.message.instructions.iter().enumerate() {
|
||||||
@@ -972,18 +1070,23 @@ fn resolve_log_links(
|
|||||||
top_level.push((program_id.clone(), index.to_string()));
|
top_level.push((program_id.clone(), index.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut inner_by_parent = std::collections::BTreeMap::<
|
let mut inner_by_root = std::collections::BTreeMap::<
|
||||||
std::string::String,
|
std::string::String,
|
||||||
std::vec::Vec<(std::string::String, std::string::String)>,
|
std::vec::Vec<(std::string::String, std::string::String, std::option::Option<u32>)>,
|
||||||
>::new();
|
>::new();
|
||||||
for group in &metadata.inner_instructions {
|
for group in &metadata.inner_instructions {
|
||||||
let parent = group.parent_instruction_index.to_string();
|
let root = group.parent_instruction_index.to_string();
|
||||||
let entries = inner_by_parent.entry(parent.clone()).or_default();
|
let paths_result = crate::core_extraction::resolve_inner_instruction_paths(group);
|
||||||
for (index, instruction) in group.instructions.iter().enumerate() {
|
let paths = match paths_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
let entries = inner_by_root.entry(root).or_default();
|
||||||
|
for (instruction, path) in group.instructions.iter().zip(paths.iter()) {
|
||||||
if let std::option::Option::Some(program_id) =
|
if let std::option::Option::Some(program_id) =
|
||||||
keys.get(usize::from(instruction.program_id_index))
|
keys.get(usize::from(instruction.program_id_index))
|
||||||
{
|
{
|
||||||
entries.push((program_id.clone(), format!("{parent}/{index}")));
|
entries.push((program_id.clone(), path.1.clone(), instruction.stack_height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1011,18 +1114,20 @@ fn resolve_log_links(
|
|||||||
}
|
}
|
||||||
resolved
|
resolved
|
||||||
} else {
|
} else {
|
||||||
let parent_path = stack.first().and_then(|entry| return entry.1.clone());
|
let root_path = stack.first().and_then(|entry| return entry.1.clone());
|
||||||
match parent_path {
|
match root_path {
|
||||||
std::option::Option::Some(parent) => {
|
std::option::Option::Some(root) => {
|
||||||
let cursor = inner_cursors.entry(parent.clone()).or_insert(0);
|
let cursor = inner_cursors.entry(root.clone()).or_insert(0);
|
||||||
let mut resolved = std::option::Option::None;
|
let mut resolved = std::option::Option::None;
|
||||||
if let std::option::Option::Some(entries) =
|
if let std::option::Option::Some(entries) =
|
||||||
inner_by_parent.get(parent.as_str())
|
inner_by_root.get(root.as_str())
|
||||||
{
|
{
|
||||||
while *cursor < entries.len() {
|
while *cursor < entries.len() {
|
||||||
let candidate = &entries[*cursor];
|
let candidate = &entries[*cursor];
|
||||||
*cursor += 1;
|
*cursor += 1;
|
||||||
if candidate.0 == program_id {
|
let depth_matches =
|
||||||
|
candidate.2.is_none_or(|height| return height == depth);
|
||||||
|
if candidate.0 == program_id && depth_matches {
|
||||||
resolved = std::option::Option::Some(candidate.1.clone());
|
resolved = std::option::Option::Some(candidate.1.clone());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1067,7 +1172,7 @@ fn resolve_log_links(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return std::result::Result::Ok(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -1570,6 +1675,7 @@ mod tests {
|
|||||||
id: 1,
|
id: 1,
|
||||||
signature: transaction.primary_signature.clone(),
|
signature: transaction.primary_signature.clone(),
|
||||||
slot: 42,
|
slot: 42,
|
||||||
|
block_time: transaction.block_time,
|
||||||
canonical_json: std::option::Option::Some(json),
|
canonical_json: std::option::Option::Some(json),
|
||||||
canonical_json_hash: std::option::Option::Some(hash),
|
canonical_json_hash: std::option::Option::Some(hash),
|
||||||
canonical_format_version: 1,
|
canonical_format_version: 1,
|
||||||
@@ -1598,6 +1704,105 @@ mod tests {
|
|||||||
assert_eq!(bundle.logs[1].instruction_path.as_deref(), std::option::Option::Some("0/0"));
|
assert_eq!(bundle.logs[1].instruction_path.as_deref(), std::option::Option::Some("0/0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_cpi_paths_preserve_immediate_parent_from_stack_height() {
|
||||||
|
let mut transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
let metadata = match transaction.metadata.as_mut() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => panic!("sample metadata is required"),
|
||||||
|
};
|
||||||
|
let template = metadata.inner_instructions[0].instructions[0].clone();
|
||||||
|
let mut direct = template.clone();
|
||||||
|
direct.stack_height = std::option::Option::Some(2);
|
||||||
|
let mut nested = template.clone();
|
||||||
|
nested.stack_height = std::option::Option::Some(3);
|
||||||
|
let mut sibling = template;
|
||||||
|
sibling.stack_height = std::option::Option::Some(2);
|
||||||
|
metadata.inner_instructions[0].instructions = std::vec![direct, nested, sibling];
|
||||||
|
let extraction_result = crate::core_extraction::extract_instructions(&transaction);
|
||||||
|
let (_top_level, inner) = match extraction_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => panic!("nested CPI extraction failed: {error}"),
|
||||||
|
};
|
||||||
|
assert_eq!(inner[0].parent_instruction_path, "0");
|
||||||
|
assert_eq!(inner[0].instruction_path, "0/0");
|
||||||
|
assert_eq!(inner[1].parent_instruction_path, "0/0");
|
||||||
|
assert_eq!(inner[1].instruction_path, "0/0/0");
|
||||||
|
assert_eq!(inner[2].parent_instruction_path, "0");
|
||||||
|
assert_eq!(inner[2].instruction_path, "0/1");
|
||||||
|
assert_eq!(inner[1].stack_height, std::option::Option::Some(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inconsistent_cpi_stack_height_is_rejected() {
|
||||||
|
let mut transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
let metadata = match transaction.metadata.as_mut() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => panic!("sample metadata is required"),
|
||||||
|
};
|
||||||
|
let template = metadata.inner_instructions[0].instructions[0].clone();
|
||||||
|
let mut direct = template.clone();
|
||||||
|
direct.stack_height = std::option::Option::Some(2);
|
||||||
|
let mut skipped_parent = template;
|
||||||
|
skipped_parent.stack_height = std::option::Option::Some(4);
|
||||||
|
metadata.inner_instructions[0].instructions = std::vec![direct, skipped_parent];
|
||||||
|
let extraction_result = crate::core_extraction::extract_instructions(&transaction);
|
||||||
|
assert!(extraction_result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn absent_cpi_stack_height_falls_back_to_top_level_parent() {
|
||||||
|
let mut transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
let metadata = match transaction.metadata.as_mut() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => panic!("sample metadata is required"),
|
||||||
|
};
|
||||||
|
metadata.inner_instructions[0].instructions[0].stack_height = std::option::Option::None;
|
||||||
|
let extraction_result = crate::core_extraction::extract_instructions(&transaction);
|
||||||
|
let (_top_level, inner) = match extraction_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => panic!("CPI extraction failed: {error}"),
|
||||||
|
};
|
||||||
|
assert_eq!(inner[0].parent_instruction_path, "0");
|
||||||
|
assert_eq!(inner[0].instruction_path, "0/0");
|
||||||
|
assert_eq!(inner[0].stack_height, std::option::Option::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extraction_preserves_block_time_and_return_data() {
|
||||||
|
let mut transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
let metadata = match transaction.metadata.as_mut() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => panic!("sample metadata is required"),
|
||||||
|
};
|
||||||
|
metadata.return_data = std::option::Option::Some(ks_lib::MdCanonicalReturnData {
|
||||||
|
program_id: "11111111111111111111111111111111".to_string(),
|
||||||
|
data_base64: "AQID".to_string(),
|
||||||
|
});
|
||||||
|
let row = crate::core_extraction::tests::raw_row(&transaction);
|
||||||
|
let extraction_result = crate::extract_raw_transaction_to_core(&row);
|
||||||
|
let bundle = match extraction_result {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => panic!("Core extraction failed: {error}"),
|
||||||
|
};
|
||||||
|
assert_eq!(bundle.transaction.block_time, transaction.block_time);
|
||||||
|
let return_data = match bundle.return_data {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => panic!("return data must be preserved"),
|
||||||
|
};
|
||||||
|
assert_eq!(return_data.program_id, "11111111111111111111111111111111");
|
||||||
|
assert_eq!(return_data.data_base64, "AQID");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extraction_rejects_block_time_mismatch() {
|
||||||
|
let transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
let mut row = crate::core_extraction::tests::raw_row(&transaction);
|
||||||
|
row.block_time = std::option::Option::Some(1_700_000_001);
|
||||||
|
let result = crate::extract_raw_transaction_to_core(&row);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extraction_rejects_hash_mismatch() {
|
fn extraction_rejects_hash_mismatch() {
|
||||||
let transaction = crate::core_extraction::tests::sample_transaction();
|
let transaction = crate::core_extraction::tests::sample_transaction();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline/src/decode_replay.rs
|
// file: ks-pipeline/src/decode_replay.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Common contextual instruction decode and optional materialization pipeline.
|
//! Common contextual instruction decode and optional materialization pipeline.
|
||||||
|
|
||||||
@@ -1320,6 +1320,7 @@ fn observation_inserts(
|
|||||||
proof_kind: proof_kind_code(observation.proof.kind).to_string(),
|
proof_kind: proof_kind_code(observation.proof.kind).to_string(),
|
||||||
proof_json,
|
proof_json,
|
||||||
payload_json: observation.payload_json.clone(),
|
payload_json: observation.payload_json.clone(),
|
||||||
|
schema_provenance: std::option::Option::None,
|
||||||
transaction_failed: observation.transaction_failed,
|
transaction_failed: observation.transaction_failed,
|
||||||
transaction_error: observation.transaction_error.clone(),
|
transaction_error: observation.transaction_error.clone(),
|
||||||
observation_committed: observation.observation_committed,
|
observation_committed: observation.observation_committed,
|
||||||
@@ -1821,10 +1822,10 @@ mod tests {
|
|||||||
clippy::implicit_return,
|
clippy::implicit_return,
|
||||||
reason = "async_trait expansion triggers implicit_return on generated async trait methods."
|
reason = "async_trait expansion triggers implicit_return on generated async trait methods."
|
||||||
)]
|
)]
|
||||||
async fn list_materialized_events(
|
async fn list_materialized_outputs(
|
||||||
&self,
|
&self,
|
||||||
_filter: &ks_store::MaterializedEventFilter,
|
_filter: &ks_store::MaterializedOutputFilter,
|
||||||
) -> ks_core::Result<std::vec::Vec<ks_store::MaterializedEventQueryRow>> {
|
) -> ks_core::Result<std::vec::Vec<ks_store::MaterializedOutputQueryRow>> {
|
||||||
return std::result::Result::Ok(std::vec::Vec::new());
|
return std::result::Result::Ok(std::vec::Vec::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline/src/plan.rs
|
// file: ks-pipeline/src/plan.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Pipeline planning primitives.
|
//! Pipeline planning primitives.
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ pub enum PipelineStage {
|
|||||||
Decode,
|
Decode,
|
||||||
/// Materialize business events.
|
/// Materialize business events.
|
||||||
Materialize,
|
Materialize,
|
||||||
/// Aggregate materialized events.
|
/// Aggregate materialized outputs.
|
||||||
Aggregate,
|
Aggregate,
|
||||||
/// Validate outputs.
|
/// Validate outputs.
|
||||||
Validate,
|
Validate,
|
||||||
|
|||||||
@@ -1,8 +1,53 @@
|
|||||||
<!-- file: ks-store/CHANGELOG.md -->
|
<!-- file: ks-store/CHANGELOG.md -->
|
||||||
<!-- version: 23 -->
|
<!-- version: 29 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-store
|
# CHANGELOG — ks-store
|
||||||
|
|
||||||
|
## `0.5.3-pre.003`
|
||||||
|
|
||||||
|
### Fix `pre.003-delta-fix-005`
|
||||||
|
|
||||||
|
- remplace le passage des statements SQLx en `TRACE` par `ConnectOptions::disable_statement_logging`, afin que `ks-store` ne dépende pas directement de la crate `log` uniquement pour `LevelFilter` ;
|
||||||
|
- conserve l'ouverture PostgreSQL via `PgConnectOptions` + `connect_with` et la journalisation fonctionnelle propre à `ks-store` sous `target: crate::TRACING_TARGET` ;
|
||||||
|
- adapte le canari de connexion pour vérifier explicitement la désactivation du statement logging SQLx.
|
||||||
|
|
||||||
|
|
||||||
|
### Fix `pre.003-delta-fix-004`
|
||||||
|
|
||||||
|
- configure `PgConnectOptions` pour émettre les statements SQLx ordinaires au niveau `TRACE` avant l'ouverture du pool via `connect_with`, tout en conservant le target tiers `sqlx::query` ;
|
||||||
|
- préserve les événements fonctionnels PostgreSQL `ks-store` sous `target: crate::TRACING_TARGET` avec `backend`, `domain` et `action` ;
|
||||||
|
- ajoute un canari positif vérifiant le chemin `PgConnectOptions` + `TRACE` + `connect_with`.
|
||||||
|
|
||||||
|
### Fix `pre.003-delta-fix-003`
|
||||||
|
|
||||||
|
- corrige la branche `incomplete_signatures` de la sélection N2 -> N3 : le `SELECT` contextualisé projette désormais `stack_height` comme l'exige `CoreInstructionRow` ;
|
||||||
|
- extrait cette requête rare dans `INCOMPLETE_DECODE_INPUTS_SQL` et ajoute un test contractuel vérifiant toutes les colonnes attendues par le mapper Core, afin d'éviter une nouvelle dérive requête/DTO.
|
||||||
|
|
||||||
|
|
||||||
|
### Baseline N1-N3 PostgreSQL
|
||||||
|
|
||||||
|
- corrige le premier delta `pre.003` : fermeture de l'`impl PostgresStore`, appels des helpers privés de tests via `super::` et retrait des validateurs de noms de tables du runtime/re-export crate-root ; ces validateurs restent des canaris privés `#[cfg(test)]`.
|
||||||
|
- reconstruit directement le schéma actif sous `k_sol_*` avec un baseline candidat au gel de **16 tables** : les 13 tables historiques conservées plus `k_sol_obs_account_observations`, `k_sol_core_account_states` et `k_sol_core_return_data` révélées par le canari future-decoder ;
|
||||||
|
- ajoute `block_time` aux ancres raw/Core transaction, `stack_height` aux contrats top-level/CPI et un lifecycle de processing symétrique pour les CPI ; la reconstruction de Core préserve le parent CPI immédiat lorsque la pile est disponible ;
|
||||||
|
- renomme le journal N3 en `k_sol_mat_outputs` et aligne l'API publique sur `MaterializedOutputFilter`, `MaterializedOutputQueryRow` et `list_materialized_outputs` ;
|
||||||
|
- réserve sur `k_sol_decode_events` une provenance nullable de schéma externe (`kind/id/version/hash`) afin qu'un futur décodeur IDL puisse tracer son schéma sans remodeler N1/N2/N3.
|
||||||
|
|
||||||
|
### Ressources et initialisation
|
||||||
|
|
||||||
|
- corrige `pre.003-delta-fix-002` l'ordre transactionnel des contraintes : les clés primaires référencées sont désormais appliquées avant les clés étrangères ; ajoute un canari couvrant les neuf dépendances FK du baseline afin d'empêcher un nouvel ordre invalide et le rollback complet observé sur base vide ;
|
||||||
|
- remplace les migrations historiques `0001` à `0004` et les deux scripts de maintenance monolithiques par **236 ressources PostgreSQL atomiques** sous `migrations/postgres/` : tables, contraintes, index, `TRUNCATE` et `DROP`, une instruction SQL par fichier embarquée par `include_str!` ;
|
||||||
|
- centralise l'initialisation dans un seul orchestrateur PostgreSQL transactionnel avec advisory lock ; les initialiseurs raw/Core/decode partiels sont supprimés, y compris dans les tests ;
|
||||||
|
- refuse explicitement la présence d'objets historiques `kb_sol_*` au lieu de mélanger les deux namespaces ;
|
||||||
|
- remplace la lecture `_sqlx_migrations` comme preuve du schéma courant par la vérification du contrat réellement observé ;
|
||||||
|
- étend le résumé backend-agnostique aux catégories physiques `table` et `index`, avec **75 index attendus** sans exposer leurs noms.
|
||||||
|
|
||||||
|
### Readiness et contrats
|
||||||
|
|
||||||
|
- conserve explicitement la `returnData` en N2 et enrichit le replay Core v3 avec `block_time`, `stack_height`, parent CPI, contexte top-level et return data ;
|
||||||
|
- introduit les DTO N1/N2 génériques d'observation/état de compte nécessaires au futur décodage de layouts inconnus, sans ajouter de structure Anchor/IDL spécifique ; leur ingestion/replay fonctionnel est raccordé dans la tranche repositories/replay ;
|
||||||
|
- ajoute un canari workspace imposant `crate::Type` pour les types publics de `ks-store` utilisés dans leur crate propriétaire ;
|
||||||
|
- ne crée aucune table DEX/trading ni projection metadata N4 et ne déclare pas encore le gel formel : le snapshot final reste prévu après les validations techniques.
|
||||||
|
|
||||||
## `0.5.3-pre.002-delta-fix-003`
|
## `0.5.3-pre.002-delta-fix-003`
|
||||||
|
|
||||||
- corrige deux appels de helpers strictement privés PostgreSQL qui avaient été qualifiés à tort via `crate::`; ils restent locaux à leur module et ne sont pas réexportés au crate-root ;
|
- corrige deux appels de helpers strictement privés PostgreSQL qui avaient été qualifiés à tort via `crate::`; ils restent locaux à leur module et ne sont pas réexportés au crate-root ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: ks-store/Cargo.toml
|
# file: ks-store/Cargo.toml
|
||||||
# version: 3
|
# version: 5
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "ks-store"
|
name = "ks-store"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-store/README.md -->
|
<!-- file: ks-store/README.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# ks-store
|
# ks-store
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
La crate expose :
|
La crate expose :
|
||||||
|
|
||||||
- `Store` et `StoreOpenOptions` comme façade d'ouverture et handle persistant backend-agnostique ;
|
- `Store` et `StoreOpenOptions` comme façade d'ouverture et handle persistant backend-agnostique ;
|
||||||
- les DTO et entités raw, observations, Core, décodage, matérialisation et ledger ;
|
- les DTO et entités N1 raw/acquisition, N2 Core, N3 decode/materialization et ledger ;
|
||||||
- les traits async `StoreHealthStore`, `RawTransactionStore`, `CoreTransactionStore`, `CoreExtractionStore` et `DecodePipelineStore` ;
|
- les traits async `StoreHealthStore`, `RawTransactionStore`, `CoreTransactionStore`, `CoreExtractionStore` et `DecodePipelineStore` ;
|
||||||
- les filtres et résultats de replay sans préfixe backend ;
|
- les filtres et résultats de replay sans préfixe backend ;
|
||||||
- des diagnostics et résumés runtime/init sanitisés ne révélant ni secret ni nom physique d'objet ;
|
- des diagnostics et résumés runtime/init sanitisés ne révélant ni secret ni nom physique d'objet ;
|
||||||
@@ -26,11 +26,35 @@ La crate expose :
|
|||||||
|
|
||||||
`StoreOpenOptions` ne dérive pas `Debug` et ne rend pas les options backend accessibles après construction. Les résumés publics exposent uniquement des informations explicitement sanitisées : code backend, présence d'une connexion, état d'initialisation, health, modèle logique et compteurs.
|
`StoreOpenOptions` ne dérive pas `Debug` et ne rend pas les options backend accessibles après construction. Les résumés publics exposent uniquement des informations explicitement sanitisées : code backend, présence d'une connexion, état d'initialisation, health, modèle logique et compteurs.
|
||||||
|
|
||||||
Le backend PostgreSQL masque le descripteur de connexion et utilise l’unique target de crate `ks-store`, complété par `backend="postgres"`, `domain="ks-store.pg"` et un champ `action` structuré. Aucun DSN, password ou valeur bindée n'est destiné aux diagnostics publics.
|
Le backend PostgreSQL masque le descripteur de connexion et utilise l'unique target de crate `ks-store`, complété par `backend="postgres"`, `domain="ks-store.pg"` et un champ `action` structuré. Aucun DSN, password ou valeur bindée n'est destiné aux diagnostics publics.
|
||||||
|
|
||||||
## Schéma pendant `0.5.3`
|
## Baseline PostgreSQL `0.5.3-pre.003`
|
||||||
|
|
||||||
`0.5.3-pre.002` ne modifie pas encore les tables héritées `kb_sol_*`. Leur reconstruction sous `k_sol_*`, les ressources SQL atomiques et le gel N1/N2/N3 commencent en `pre.003` conformément au plan actif.
|
`pre.003` reconstruit directement le schéma actif sous le namespace `k_sol_*`. Les quatre migrations historiques `0001` à `0004` et les deux scripts de maintenance monolithiques ne font plus partie de la chaîne active.
|
||||||
|
|
||||||
|
Le baseline candidat au gel comprend **16 tables N1-N3** :
|
||||||
|
|
||||||
|
- N1 : transactions raw, observations d'acquisition de transactions et observations génériques de comptes ;
|
||||||
|
- N2 : transaction Core, clés de comptes, instructions top-level, CPI, logs, variations de balances, return data et états canoniques de comptes ;
|
||||||
|
- N3/transverse : processing ledger, événements décodés, déclarations/observations de couverture et `k_sol_mat_outputs`.
|
||||||
|
|
||||||
|
Les ressources PostgreSQL sont rangées sous `migrations/postgres/` et suivent la règle **une instruction SQL par fichier**. L'orchestrateur privé applique tables, contraintes et index dans une transaction unique et refuse explicitement un schéma historique contenant encore des objets `kb_sol_*`.
|
||||||
|
|
||||||
|
Le baseline comporte actuellement **236 ressources SQL atomiques** et **75 index attendus**, comptés sans exposer leurs noms dans l'API publique.
|
||||||
|
|
||||||
|
## Readiness des futurs décodeurs
|
||||||
|
|
||||||
|
Le canari future-decoder a conduit à trois corrections génériques avant gel :
|
||||||
|
|
||||||
|
- `block_time` devient une colonne de premier rang sur raw/Core transaction ;
|
||||||
|
- `returnData` est conservée explicitement dans le Core via `k_sol_core_return_data` ;
|
||||||
|
- un chemin générique d'observation/état de compte est réservé via `k_sol_obs_account_observations` et `k_sol_core_account_states`.
|
||||||
|
|
||||||
|
Les instructions top-level et CPI restent physiquement séparées. Les deux contrats portent `stack_height` lorsque disponible ; les CPI conservent un `parent_instruction_path` vers le parent immédiat reconstructible.
|
||||||
|
|
||||||
|
N3 réserve en outre une provenance de schéma nullable sur les observations décodées afin qu'un futur décodeur basé sur un schéma externe/IDL puisse tracer ce schéma sans remodeler le Core.
|
||||||
|
|
||||||
|
Aucune structure `anchor_*`, DEX ou projection metadata N4 n'est créée en `pre.003`.
|
||||||
|
|
||||||
## Responsabilités
|
## Responsabilités
|
||||||
|
|
||||||
@@ -43,7 +67,7 @@ Le backend PostgreSQL masque le descripteur de connexion et utilise l’unique t
|
|||||||
|
|
||||||
## Hors périmètre
|
## Hors périmètre
|
||||||
|
|
||||||
La crate ne décode pas les instructions, n'acquiert pas les transactions et ne décide pas quelle matérialisation ou projection N4 exécuter. Elle persiste les faits produits par `ks-lib` et orchestrés par `ks-pipeline`.
|
La crate ne décode pas les instructions, n'acquiert pas elle-même les transactions/comptes et ne décide pas quelle matérialisation ou projection N4 exécuter. Elle persiste les faits produits par `ks-lib` et orchestrés par `ks-pipeline`.
|
||||||
|
|
||||||
## Relations
|
## Relations
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
<!-- file: ks-store/TODO.md -->
|
<!-- file: ks-store/TODO.md -->
|
||||||
<!-- version: 13 -->
|
<!-- version: 14 -->
|
||||||
|
|
||||||
# TODO — ks-store
|
# TODO — ks-store
|
||||||
|
|
||||||
## Série `0.5.x`
|
## Série `0.5.x`
|
||||||
|
|
||||||
- [ ] `0.5.3` — reconstruire `kb_sol_* -> k_sol_*`, ajouter `block_time` aux ancres raw/Core, conserver top-level/CPI dans deux tables Core avec lifecycle/replay équivalents, `stack_height`/parent CPI audités et renommer le journal N3 en `k_sol_mat_outputs`.
|
- [ ] `0.5.3` — finaliser en `pre.004` les repositories/replay au-dessus du baseline de 16 tables : sélection autonome des CPI, lecture logique top-level+CPI, invalidation descendante, replay N1 -> N2 / N2 -> N3 / frontière N3 -> N4 et ingestion/replay du nouveau couple observation/état de compte.
|
||||||
- [ ] `0.5.3` — déplacer les ressources sous `migrations/postgres/`, imposer une instruction SQL par fichier avec exécution privée via `include_str!`, séparer tables/keys/index/drop/truncate et supprimer le DDL dupliqué en Rust.
|
- [ ] `0.5.3` — finaliser en `pre.004` pagination/limites, conversions BIGINT et plans/index justifiés par les requêtes réelles ; les 75 index du baseline `pre.003` restent optimisables avant gel.
|
||||||
- [ ] `0.5.3` — corriger l'invalidation descendante, formaliser les replays N1 -> N2 / N2 -> N3 / frontière N3 -> N4, les paginations/limites, les conversions BIGINT et les index justifiés par les requêtes réelles.
|
- [ ] `0.5.3` — finaliser en `pre.005` la présentation fonctionnelle des surfaces `demo_store_*`, `demo_config` et le splash autour des modèles/compteurs du nouveau baseline sans noms d'objets physiques.
|
||||||
- [ ] `0.5.3` — finaliser en `pre.005` la présentation fonctionnelle des surfaces `demo_store_*`, `demo_config` et le splash autour des modèles/compteurs du nouveau baseline sans noms d'objets physiques ; le renommage technique `demo_sql_* -> demo_store_*` est réalisé en `pre.002`.
|
- [ ] `0.5.3` — produire le snapshot machine-readable du baseline N1/N2/N3 réellement stabilisé, ajouter les canaris de gel/anti-`kb_sol_*`/anti-backend leak/anti-secret, confirmer le canari future-decoder sur transactions + comptes et documenter la politique N4 plus souple.
|
||||||
- [ ] `0.5.3` — exécuter avant gel le canari future-decoder/Anchor-readiness sur N1/N2/N3 : bytes d'instruction, comptes ordonnés/flags, hiérarchie CPI, logs, `returnData`, observations/états de comptes et provenance decoder/schema ; ajouter seulement les contrats Solana génériques manquants, sans table `anchor_*`.
|
- [ ] `0.5.3` — valider PostgreSQL réel sur base vide/recréée : 16 tables, index attendus, réouverture idempotente, refus de l'ancien namespace, rollback et roundtrips raw/Core/decode/materialization.
|
||||||
- [ ] `0.5.3` — produire le snapshot machine-readable des tables N1/N2/N3 réellement stabilisées après ce canari (13 tables existantes de baseline, plus éventuels contrats génériques nécessaires), documenter la politique N4 plus souple et les futures projections metadata asset/token + program metadata, puis ajouter les canaris anti-`kb_sol_*`, anti-backend leak, anti-secret et logging sous le target canonique `ks-store` avec domaine structuré `ks-store.pg`.
|
- [ ] `0.5.x` — concevoir les projections N4 metadata : asset/token metadata commune Metaplex + Token-2022 et program metadata séparée pour SPM, toutes deux reconstructibles depuis `k_sol_mat_outputs`.
|
||||||
- [ ] PostgreSQL - auditer le dimensionnement du pool selon les charges réelles.
|
|
||||||
- [ ] Résilience - décider et implémenter, si justifié, un retry borné pour les timeouts transitoires.
|
## Plus tard
|
||||||
- [ ] Administration - définir les outils supplémentaires prévus par le ROADMAP avant leur implémentation.
|
|
||||||
- [ ] Historique - documenter et tester les futures opérations historiques ajoutées à la crate.
|
- [ ] définir une stratégie de pool/réessai configurable commune aux futurs backends sans exposer leurs types de connexion ;
|
||||||
|
- [ ] décider si un journal append-only des tentatives de processing doit compléter le ledger courant ;
|
||||||
|
- [ ] ajouter des projections spécialisées uniquement lorsqu'un décodeur/materializer et un besoin queryable réel en démontrent les invariants.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-store/USAGE.md -->
|
<!-- file: ks-store/USAGE.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Utilisation de ks-store
|
# Utilisation de ks-store
|
||||||
|
|
||||||
@@ -27,7 +27,30 @@ let store = match ks_store::Store::open(options).await {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
`Store::open` interprète les options du backend actif, crée la connexion, exécute l'auto-initialisation lorsqu'elle est configurée et capture un résumé d'initialisation sanitisé. PostgreSQL reste le seul backend opérationnel exigé en `0.5.3-pre.002`; un code backend inconnu est refusé proprement.
|
`Store::open` interprète les options du backend actif, crée la connexion, exécute l'auto-initialisation lorsqu'elle est configurée et capture un résumé d'initialisation sanitisé. PostgreSQL reste le seul backend opérationnel exigé en `0.5.3`; un code backend inconnu est refusé proprement.
|
||||||
|
|
||||||
|
Avec PostgreSQL, l'auto-initialisation utilise un **seul orchestrateur transactionnel global**. Il ne faut pas initialiser séparément raw/Core/decode : la cohérence du baseline est vérifiée comme un contrat unique.
|
||||||
|
|
||||||
|
## Baseline PostgreSQL actif
|
||||||
|
|
||||||
|
`0.5.3-pre.003` utilise directement le namespace `k_sol_*` et un baseline candidat au gel de 16 tables N1-N3. Une base contenant encore des objets `kb_sol_*` est refusée ; elle doit être supprimée/reconstruite explicitement.
|
||||||
|
|
||||||
|
Les ressources actives sont sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ks-store/migrations/postgres/
|
||||||
|
schema/
|
||||||
|
tables/
|
||||||
|
constraints/
|
||||||
|
indexes/
|
||||||
|
maintenance/
|
||||||
|
truncate/
|
||||||
|
drop/
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque fichier contient une seule instruction SQL. Les fonctions privées du backend l'embarquent par `include_str!`; le DDL n'est pas dupliqué dans des chaînes Rust.
|
||||||
|
|
||||||
|
Le baseline courant compte 236 ressources SQL atomiques et 75 index attendus.
|
||||||
|
|
||||||
## Résumés sûrs
|
## Résumés sûrs
|
||||||
|
|
||||||
@@ -42,7 +65,7 @@ let runtime = match store.runtime_summary().await {
|
|||||||
|
|
||||||
Ces contrats ne contiennent pas les options backend brutes. Le descripteur de connexion éventuellement exposé par `runtime.backend` est masqué par le backend propriétaire.
|
Ces contrats ne contiennent pas les options backend brutes. Le descripteur de connexion éventuellement exposé par `runtime.backend` est masqué par le backend propriétaire.
|
||||||
|
|
||||||
Le résumé d'initialisation classe les ressources par modèles logiques et fournit des compteurs. Il expose aussi `objects`, une liste de catégories physiques sans noms d'objets : PostgreSQL publie déjà le total `table`, tandis que `pre.003` ajoutera le total `index` avec le nouveau baseline. Les noms physiques de tables, index et contraintes restent internes au backend.
|
Le résumé d'initialisation classe les ressources par modèles logiques et fournit des compteurs. `objects` expose des catégories physiques sans noms d'objets. PostgreSQL publie actuellement les catégories `table` et `index`; un futur backend peut publier d'autres catégories sans modifier le contrat desktop.
|
||||||
|
|
||||||
## Diagnostics de ressources
|
## Diagnostics de ressources
|
||||||
|
|
||||||
@@ -61,7 +84,7 @@ for resource in raw_resources {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Les méthodes `core_resource_diagnostics`, `decode_resource_diagnostics` et `known_resource_diagnostics` suivent le même contrat backend-agnostique.
|
Les méthodes `core_resource_diagnostics`, `decode_resource_diagnostics` et `known_resource_diagnostics` suivent le même contrat backend-agnostique. Les codes de ressources publics sont logiques ; les noms physiques PostgreSQL restent internes.
|
||||||
|
|
||||||
## Contrats de repositories
|
## Contrats de repositories
|
||||||
|
|
||||||
@@ -73,7 +96,33 @@ Les traits publics effectivement implémentés sont :
|
|||||||
- `CoreExtractionStore` ;
|
- `CoreExtractionStore` ;
|
||||||
- `DecodePipelineStore`.
|
- `DecodePipelineStore`.
|
||||||
|
|
||||||
`Store` implémente ces traits et délègue au backend privé. Les anciens traits historiques sans implémentation réelle ont été retirés avant gel de l'API.
|
`Store` implémente ces traits et délègue au backend privé.
|
||||||
|
|
||||||
|
Le schéma `pre.003` réserve aussi les contrats génériques d'observation/état de comptes nécessaires au gel N1/N2. Leur ingestion/replay opérationnel est raccordé dans la tranche repositories/replay suivante ; leur présence dans le baseline évite une modification structurelle ultérieure pour un futur décodeur de comptes.
|
||||||
|
|
||||||
|
## Core et replay N2 -> N3
|
||||||
|
|
||||||
|
Le replay d'instruction Core transporte désormais :
|
||||||
|
|
||||||
|
- `block_time` nullable ;
|
||||||
|
- `stack_height` nullable ;
|
||||||
|
- `parent_instruction_path` pour les CPI ;
|
||||||
|
- le contexte ordonné des instructions top-level ;
|
||||||
|
- la `returnData` transactionnelle lorsqu'elle existe.
|
||||||
|
|
||||||
|
Le contrat de replay Core est en version 3. Les anciens constructeurs restent utilisables pour les tests/sources synthétiques ; `ks-store` enrichit les inputs issus du Core persistant avec le contexte disponible.
|
||||||
|
|
||||||
|
Les instructions top-level et CPI restent deux tables physiques. La sélection logique commune et le replay autonome des CPI sont finalisés dans la tranche repositories/replay.
|
||||||
|
|
||||||
|
## Matérialisation N3
|
||||||
|
|
||||||
|
Le journal générique est `k_sol_mat_outputs`. L'API publique utilise désormais :
|
||||||
|
|
||||||
|
- `MaterializedOutputFilter` ;
|
||||||
|
- `MaterializedOutputQueryRow` ;
|
||||||
|
- `DecodePipelineStore::list_materialized_outputs`.
|
||||||
|
|
||||||
|
`k_sol_mat_outputs` reste obligatoire même lorsque des projections N4 spécialisées existeront.
|
||||||
|
|
||||||
## Replay et requêtes bornées
|
## Replay et requêtes bornées
|
||||||
|
|
||||||
@@ -92,7 +141,7 @@ let entities = match store.replay_entity_summaries(entity_filter).await {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Les contrats publics utilisent `ReplayTransaction*`, `ReplayProgram*` et `ReplayEntity*`. Le scope d'instruction utilise désormais `TopLevel`, `Inner` et `Logs`; le terme historique `outer` n'est pas introduit dans la nouvelle API.
|
Les contrats publics utilisent `ReplayTransaction*`, `ReplayProgram*` et `ReplayEntity*`. Le scope d'instruction utilise `TopLevel`, `Inner` et `Logs`; le terme historique `outer` n'est pas introduit dans la nouvelle API store.
|
||||||
|
|
||||||
## Pagination bornée
|
## Pagination bornée
|
||||||
|
|
||||||
@@ -106,7 +155,7 @@ assert_eq!(first_page.limit, ks_store::DEFAULT_PAGE_SIZE);
|
|||||||
assert!(next_page.limit <= ks_store::MAX_PAGE_SIZE);
|
assert!(next_page.limit <= ks_store::MAX_PAGE_SIZE);
|
||||||
```
|
```
|
||||||
|
|
||||||
La refonte cursorisée prévue par le plan `0.5.3` reste dans la tranche repositories/replay ultérieure.
|
La refonte cursorisée et la suppression des gros offsets comme contrat durable appartiennent à la tranche repositories/replay.
|
||||||
|
|
||||||
## Transactions atomiques
|
## Transactions atomiques
|
||||||
|
|
||||||
@@ -116,9 +165,20 @@ Les bundles `CoreExtractionBundle`, `DecodePersistenceBundle` et `Materializatio
|
|||||||
|
|
||||||
Les tests opt-in utilisent `KS_SECRET_POSTGRES_TEST_URL`. Aucun credential réel ne doit être enregistré dans les fixtures ou logs. Les erreurs d'ouverture de connexion sont volontairement sanitisées.
|
Les tests opt-in utilisent `KS_SECRET_POSTGRES_TEST_URL`. Aucun credential réel ne doit être enregistré dans les fixtures ou logs. Les erreurs d'ouverture de connexion sont volontairement sanitisées.
|
||||||
|
|
||||||
## Limites de `pre.002`
|
Pour `pre.003`, les validations PostgreSQL à exécuter après compilation sont particulièrement importantes :
|
||||||
|
|
||||||
- les tables restent temporairement sous leur namespace historique jusqu'à `pre.003` ;
|
- init sur base vide ;
|
||||||
- les migrations PostgreSQL ne sont pas encore déplacées sous `migrations/postgres/` ;
|
- réouverture idempotente ;
|
||||||
- la pagination cursorisée et l'invalidation descendante sont traitées dans la tranche repositories/replay ;
|
- présence des 16 tables et 75 index attendus ;
|
||||||
- la refonte complète des fenêtres desktop `demo_store_*` est prévue dans la tranche consommateurs.
|
- refus d'un schéma historique `kb_sol_*` ;
|
||||||
|
- roundtrip raw/Core/decode/materialization ;
|
||||||
|
- rollback transactionnel Core/decode.
|
||||||
|
|
||||||
|
## Travaux encore ouverts après `pre.003`
|
||||||
|
|
||||||
|
- sélection/replay autonome des CPI et lecture logique top-level+CPI ;
|
||||||
|
- invalidation descendante N1 -> N2 -> N3 -> N4 ;
|
||||||
|
- replay opérationnel du nouveau flux d'observations/états de comptes ;
|
||||||
|
- pagination cursorisée et revue finale des index par plans réels ;
|
||||||
|
- snapshot machine-readable et gel formel en tranche de réconciliation technique ;
|
||||||
|
- refonte desktop fonctionnelle finale autour du nouveau baseline.
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
-- file: ks-store/maintenance/drop_raw_core_store.sql
|
|
||||||
-- version: 2
|
|
||||||
|
|
||||||
-- Drop raw and core Solana storage tables in reverse dependency order.
|
|
||||||
-- This script is intended for local development and test databases only.
|
|
||||||
-- It does not drop unrelated schemas, migrations, roles or configuration state.
|
|
||||||
-- It intentionally avoids CASCADE so unexpected dependencies fail loudly.
|
|
||||||
-- No custom PostgreSQL enum/domain types are owned by these 0.2.3/0.2.4 tables.
|
|
||||||
|
|
||||||
BEGIN;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_balance_changes;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_logs;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_inner_instructions;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_instructions;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_account_keys;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_core_transactions;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_obs_transaction_observations;
|
|
||||||
DROP TABLE IF EXISTS kb_sol_raw_transactions;
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
-- file: ks-store/maintenance/reset_derived_store_keep_raw.sql
|
|
||||||
-- version: 3
|
|
||||||
|
|
||||||
-- Reset every reconstructible Solana projection while preserving acquisition data.
|
|
||||||
-- Intended for controlled local/research rebuilds only.
|
|
||||||
-- Preserved tables:
|
|
||||||
-- - kb_sol_raw_transactions
|
|
||||||
-- - kb_sol_obs_transaction_observations
|
|
||||||
-- No CASCADE is used: unexpected dependencies fail loudly.
|
|
||||||
|
|
||||||
BEGIN;
|
|
||||||
TRUNCATE TABLE
|
|
||||||
kb_sol_mat_events,
|
|
||||||
kb_sol_decode_coverage_observations,
|
|
||||||
kb_sol_decode_coverage_declarations,
|
|
||||||
kb_sol_decode_events,
|
|
||||||
kb_sol_ops_processing_ledger,
|
|
||||||
kb_sol_core_balance_changes,
|
|
||||||
kb_sol_core_logs,
|
|
||||||
kb_sol_core_inner_instructions,
|
|
||||||
kb_sol_core_instructions,
|
|
||||||
kb_sol_core_account_keys,
|
|
||||||
kb_sol_core_transactions
|
|
||||||
RESTART IDENTITY;
|
|
||||||
UPDATE kb_sol_raw_transactions
|
|
||||||
SET
|
|
||||||
processing_state = 'received',
|
|
||||||
lifecycle_reason = NULL,
|
|
||||||
updated_at = NOW();
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
-- file: ks-store/migrations/0001_canonical_transaction_store.sql
|
|
||||||
-- version: 2
|
|
||||||
|
|
||||||
-- Current canonical transaction and acquisition observation store.
|
|
||||||
-- This baseline contains only active tables and uses the current PostgreSQL schema.
|
|
||||||
-- Historical 0.2.x upgrade logic was validated before this baseline was consolidated.
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_raw_transactions (
|
|
||||||
id BIGSERIAL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
canonical_json JSONB,
|
|
||||||
canonical_json_hash TEXT,
|
|
||||||
canonical_format_version INTEGER NOT NULL DEFAULT 1,
|
|
||||||
retention_state TEXT NOT NULL DEFAULT 'full',
|
|
||||||
processing_state TEXT NOT NULL DEFAULT 'received',
|
|
||||||
lifecycle_reason TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_raw_transactions PRIMARY KEY (id),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged')),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed')),
|
|
||||||
CONSTRAINT ck_kb_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_raw_transactions_signature ON kb_sol_raw_transactions (signature);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_slot ON kb_sol_raw_transactions (slot);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_created_at ON kb_sol_raw_transactions (created_at);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_processing ON kb_sol_raw_transactions (processing_state);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_canonical_hash ON kb_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_obs_transaction_observations (
|
|
||||||
id BIGSERIAL,
|
|
||||||
raw_transaction_id BIGINT,
|
|
||||||
observation_key TEXT NOT NULL,
|
|
||||||
signature TEXT,
|
|
||||||
slot BIGINT,
|
|
||||||
provider TEXT NOT NULL,
|
|
||||||
endpoint_code TEXT,
|
|
||||||
protocol TEXT NOT NULL,
|
|
||||||
acquisition_method TEXT NOT NULL,
|
|
||||||
origin TEXT NOT NULL,
|
|
||||||
commitment TEXT,
|
|
||||||
capture_session_id TEXT,
|
|
||||||
filter_code TEXT,
|
|
||||||
detected_at TIMESTAMPTZ,
|
|
||||||
received_at TIMESTAMPTZ NOT NULL,
|
|
||||||
normalized_at TIMESTAMPTZ,
|
|
||||||
persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
payload_size_bytes BIGINT,
|
|
||||||
source_payload_hash TEXT,
|
|
||||||
status TEXT NOT NULL DEFAULT 'received',
|
|
||||||
error_code TEXT,
|
|
||||||
error_message TEXT,
|
|
||||||
CONSTRAINT pk_kb_sol_obs_transaction_observations PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration')),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing')),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_obs_transaction_observations_key ON kb_sol_obs_transaction_observations (observation_key);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_signature ON kb_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_slot ON kb_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_provider_method ON kb_sol_obs_transaction_observations (provider, acquisition_method);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_received_at ON kb_sol_obs_transaction_observations (received_at);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_raw_transaction ON kb_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_status ON kb_sol_obs_transaction_observations (status);
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
-- file: ks-store/migrations/0002_core_store.sql
|
|
||||||
-- version: 2
|
|
||||||
|
|
||||||
-- Current normalized Solana core store.
|
|
||||||
-- This migration uses the current PostgreSQL schema from the active profile.
|
|
||||||
-- It must not create application schemas such as raw, core, obs, decode, mat or catalog.
|
|
||||||
-- Object naming convention: pk_ for primary keys, fk_ for foreign keys, ux_ for unique indexes and ix_ for non-unique indexes.
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_transactions (
|
|
||||||
id BIGSERIAL,
|
|
||||||
raw_transaction_id BIGINT,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
failed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
err_json JSONB,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_transactions PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL,
|
|
||||||
CONSTRAINT ck_kb_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_transactions_slot_non_negative CHECK (slot >= 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_transactions_signature
|
|
||||||
ON kb_sol_core_transactions (signature);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_slot
|
|
||||||
ON kb_sol_core_transactions (slot);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_created_at
|
|
||||||
ON kb_sol_core_transactions (created_at);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_account_keys (
|
|
||||||
id BIGSERIAL,
|
|
||||||
transaction_id BIGINT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
account_index INTEGER NOT NULL,
|
|
||||||
account_key TEXT NOT NULL,
|
|
||||||
source TEXT NOT NULL,
|
|
||||||
writable BOOLEAN NOT NULL,
|
|
||||||
signer BOOLEAN NOT NULL,
|
|
||||||
executable BOOLEAN,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_account_keys PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT ck_kb_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_account_keys_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_account_keys_index_non_negative CHECK (account_index >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_account_keys_sig_index
|
|
||||||
ON kb_sol_core_account_keys (signature, account_index);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_account_keys_key
|
|
||||||
ON kb_sol_core_account_keys (account_key);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_instructions (
|
|
||||||
id BIGSERIAL,
|
|
||||||
transaction_id BIGINT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
instruction_path TEXT NOT NULL,
|
|
||||||
program_id TEXT NOT NULL,
|
|
||||||
accounts_json JSONB NOT NULL,
|
|
||||||
payload_json JSONB,
|
|
||||||
payload_json_hash TEXT,
|
|
||||||
processing_state TEXT NOT NULL DEFAULT 'pending',
|
|
||||||
processor_name TEXT,
|
|
||||||
processor_version TEXT,
|
|
||||||
lifecycle_reason TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_instructions PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT ck_kb_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_instructions_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_instructions_sig_path
|
|
||||||
ON kb_sol_core_instructions (signature, instruction_path);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_program
|
|
||||||
ON kb_sol_core_instructions (program_id);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_slot
|
|
||||||
ON kb_sol_core_instructions (slot);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_processing
|
|
||||||
ON kb_sol_core_instructions (processing_state);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_inner_instructions (
|
|
||||||
id BIGSERIAL,
|
|
||||||
transaction_id BIGINT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
parent_instruction_path TEXT NOT NULL,
|
|
||||||
instruction_path TEXT NOT NULL,
|
|
||||||
program_id TEXT NOT NULL,
|
|
||||||
accounts_json JSONB NOT NULL,
|
|
||||||
payload_json JSONB,
|
|
||||||
payload_json_hash TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_inner_instructions PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT ck_kb_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_inner_instructions_sig_path
|
|
||||||
ON kb_sol_core_inner_instructions (signature, instruction_path);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_parent
|
|
||||||
ON kb_sol_core_inner_instructions (signature, parent_instruction_path);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_program
|
|
||||||
ON kb_sol_core_inner_instructions (program_id);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_logs (
|
|
||||||
id BIGSERIAL,
|
|
||||||
transaction_id BIGINT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
log_index INTEGER NOT NULL,
|
|
||||||
instruction_path TEXT,
|
|
||||||
program_id TEXT,
|
|
||||||
log_text TEXT,
|
|
||||||
log_text_hash TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_logs PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_index_non_negative CHECK (log_index >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_text_not_empty CHECK (log_text IS NULL OR length(btrim(log_text)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_logs_sig_index
|
|
||||||
ON kb_sol_core_logs (signature, log_index);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_program
|
|
||||||
ON kb_sol_core_logs (program_id)
|
|
||||||
WHERE program_id IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_path
|
|
||||||
ON kb_sol_core_logs (signature, instruction_path)
|
|
||||||
WHERE instruction_path IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_core_balance_changes (
|
|
||||||
id BIGSERIAL,
|
|
||||||
transaction_id BIGINT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
balance_change_index INTEGER NOT NULL,
|
|
||||||
balance_kind TEXT NOT NULL,
|
|
||||||
account_index INTEGER,
|
|
||||||
account_key TEXT,
|
|
||||||
mint TEXT,
|
|
||||||
owner TEXT,
|
|
||||||
pre_balance_json JSONB,
|
|
||||||
post_balance_json JSONB,
|
|
||||||
delta_json JSONB,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_core_balance_changes PRIMARY KEY (id),
|
|
||||||
CONSTRAINT fk_kb_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount')),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_balance_changes_sig_index
|
|
||||||
ON kb_sol_core_balance_changes (signature, balance_change_index);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_account
|
|
||||||
ON kb_sol_core_balance_changes (account_key)
|
|
||||||
WHERE account_key IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_mint
|
|
||||||
ON kb_sol_core_balance_changes (mint)
|
|
||||||
WHERE mint IS NOT NULL;
|
|
||||||
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
-- file: ks-store/migrations/0003_processing_ledger.sql
|
|
||||||
-- version: 2
|
|
||||||
|
|
||||||
-- Current processing ledger for canonical to core extraction and later pipeline stages.
|
|
||||||
-- This migration uses the current PostgreSQL schema from the active profile.
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_ops_processing_ledger (
|
|
||||||
id BIGSERIAL,
|
|
||||||
stage TEXT NOT NULL,
|
|
||||||
processor_name TEXT NOT NULL,
|
|
||||||
processor_version TEXT NOT NULL,
|
|
||||||
input_key TEXT NOT NULL,
|
|
||||||
input_hash TEXT NOT NULL,
|
|
||||||
status TEXT NOT NULL,
|
|
||||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
||||||
started_at TIMESTAMPTZ,
|
|
||||||
finished_at TIMESTAMPTZ,
|
|
||||||
error_code TEXT,
|
|
||||||
error_message TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_ops_processing_ledger PRIMARY KEY (id),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed')),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0),
|
|
||||||
CONSTRAINT ck_kb_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_ops_processing_ledger_identity
|
|
||||||
ON kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_status
|
|
||||||
ON kb_sol_ops_processing_ledger (stage, processor_name, status, updated_at);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_input_hash
|
|
||||||
ON kb_sol_ops_processing_ledger (input_hash);
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
-- file: ks-store/migrations/0004_decode_materialization_store.sql
|
|
||||||
-- version: 2
|
|
||||||
|
|
||||||
-- Common versioned decode, coverage and materialization store.
|
|
||||||
-- This migration uses the current PostgreSQL schema from the active profile.
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_decode_events (
|
|
||||||
id BIGSERIAL,
|
|
||||||
processor_name TEXT NOT NULL,
|
|
||||||
processor_version TEXT NOT NULL,
|
|
||||||
input_key TEXT NOT NULL,
|
|
||||||
input_hash TEXT NOT NULL,
|
|
||||||
event_key TEXT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
instruction_path TEXT NOT NULL,
|
|
||||||
program_id TEXT NOT NULL,
|
|
||||||
protocol_code TEXT NOT NULL,
|
|
||||||
surface_code TEXT NOT NULL,
|
|
||||||
event_code TEXT NOT NULL,
|
|
||||||
event_name TEXT NOT NULL,
|
|
||||||
event_family TEXT NOT NULL,
|
|
||||||
source_kind TEXT NOT NULL,
|
|
||||||
confidence TEXT NOT NULL,
|
|
||||||
proof_kind TEXT NOT NULL,
|
|
||||||
proof_jsonb JSONB NOT NULL,
|
|
||||||
payload_jsonb JSONB NOT NULL,
|
|
||||||
transaction_failed BOOLEAN NOT NULL,
|
|
||||||
transaction_error_jsonb JSONB,
|
|
||||||
observation_committed BOOLEAN NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_decode_events PRIMARY KEY (id),
|
|
||||||
CONSTRAINT ck_kb_sol_decode_events_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_events_processor_input_event
|
|
||||||
ON kb_sol_decode_events (processor_name, processor_version, input_key, event_key);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_signature_path
|
|
||||||
ON kb_sol_decode_events (signature, instruction_path);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_program_surface
|
|
||||||
ON kb_sol_decode_events (program_id, surface_code, event_code);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_family_commit
|
|
||||||
ON kb_sol_decode_events (event_family, transaction_failed, observation_committed);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_declarations (
|
|
||||||
id BIGSERIAL,
|
|
||||||
processor_name TEXT NOT NULL,
|
|
||||||
processor_version TEXT NOT NULL,
|
|
||||||
program_id TEXT NOT NULL,
|
|
||||||
surface_code TEXT,
|
|
||||||
entry_kind TEXT NOT NULL,
|
|
||||||
entry_code TEXT NOT NULL,
|
|
||||||
discriminator_hex TEXT,
|
|
||||||
historical BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_decode_coverage_declarations PRIMARY KEY (id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_declarations_identity
|
|
||||||
ON kb_sol_decode_coverage_declarations (
|
|
||||||
processor_name,
|
|
||||||
processor_version,
|
|
||||||
program_id,
|
|
||||||
COALESCE(surface_code, ''),
|
|
||||||
entry_kind,
|
|
||||||
entry_code,
|
|
||||||
COALESCE(discriminator_hex, '')
|
|
||||||
);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_declarations_program
|
|
||||||
ON kb_sol_decode_coverage_declarations (program_id, processor_name, processor_version);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_observations (
|
|
||||||
id BIGSERIAL,
|
|
||||||
processor_name TEXT NOT NULL,
|
|
||||||
processor_version TEXT NOT NULL,
|
|
||||||
input_key TEXT NOT NULL,
|
|
||||||
input_hash TEXT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
instruction_path TEXT NOT NULL,
|
|
||||||
program_id TEXT NOT NULL,
|
|
||||||
surface_code TEXT,
|
|
||||||
entry_code TEXT,
|
|
||||||
discriminator_hex TEXT,
|
|
||||||
status TEXT NOT NULL,
|
|
||||||
recognized BOOLEAN NOT NULL,
|
|
||||||
decoded_count INTEGER NOT NULL DEFAULT 0,
|
|
||||||
materialized_count INTEGER NOT NULL DEFAULT 0,
|
|
||||||
error_count INTEGER NOT NULL DEFAULT 0,
|
|
||||||
transaction_failed BOOLEAN NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_decode_coverage_observations PRIMARY KEY (id),
|
|
||||||
CONSTRAINT ck_kb_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0),
|
|
||||||
CONSTRAINT ck_kb_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_observations_identity
|
|
||||||
ON kb_sol_decode_coverage_observations (processor_name, processor_version, input_key);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_program_status
|
|
||||||
ON kb_sol_decode_coverage_observations (program_id, status, transaction_failed);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_entry
|
|
||||||
ON kb_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS kb_sol_mat_events (
|
|
||||||
id BIGSERIAL,
|
|
||||||
processor_name TEXT NOT NULL,
|
|
||||||
processor_version TEXT NOT NULL,
|
|
||||||
input_key TEXT NOT NULL,
|
|
||||||
input_hash TEXT NOT NULL,
|
|
||||||
output_key TEXT NOT NULL,
|
|
||||||
source_event_key TEXT NOT NULL,
|
|
||||||
source_decoder_name TEXT NOT NULL,
|
|
||||||
source_decoder_version TEXT NOT NULL,
|
|
||||||
source_decode_input_key TEXT NOT NULL,
|
|
||||||
signature TEXT NOT NULL,
|
|
||||||
slot BIGINT NOT NULL,
|
|
||||||
materialized_family TEXT NOT NULL,
|
|
||||||
payload_jsonb JSONB NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
CONSTRAINT pk_kb_sol_mat_events PRIMARY KEY (id),
|
|
||||||
CONSTRAINT ck_kb_sol_mat_events_slot_non_negative CHECK (slot >= 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_mat_events_processor_input_output
|
|
||||||
ON kb_sol_mat_events (processor_name, processor_version, input_key, output_key);
|
|
||||||
CREATE INDEX IF NOT EXISTS ix_kb_sol_mat_events_signature_family
|
|
||||||
ON kb_sol_mat_events (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family);
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_keys.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_account_keys explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_account_keys CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_account_states.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_account_states explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_account_states CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_balance_changes.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_balance_changes explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_balance_changes CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_inner_instructions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_inner_instructions explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_inner_instructions CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_instructions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_instructions explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_instructions CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_logs.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_logs explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_logs CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_return_data.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_return_data explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_return_data CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_core_transactions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_core_transactions explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_core_transactions CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_declarations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_decode_coverage_declarations explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_decode_coverage_declarations CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_coverage_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_decode_coverage_observations explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_decode_coverage_observations CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_decode_events.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_decode_events explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_decode_events CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_mat_outputs.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_mat_outputs explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_mat_outputs CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_account_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_obs_account_observations explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_obs_account_observations CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_obs_transaction_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_obs_transaction_observations explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_obs_transaction_observations CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_ops_processing_ledger.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_ops_processing_ledger explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_ops_processing_ledger CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/drop/drop_table_if_exists_k_sol_raw_transactions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Drop k_sol_raw_transactions explicitly for maintenance.
|
||||||
|
DROP TABLE IF EXISTS k_sol_raw_transactions CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_keys.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_account_keys explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_account_keys RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_account_states.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_account_states explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_account_states RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_balance_changes.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_balance_changes explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_balance_changes RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_inner_instructions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_inner_instructions explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_inner_instructions RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_instructions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_instructions explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_instructions RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_logs.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_logs explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_logs RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_return_data.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_return_data explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_return_data RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_core_transactions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_core_transactions explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_core_transactions RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_declarations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_decode_coverage_declarations explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_decode_coverage_declarations RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_coverage_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_decode_coverage_observations explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_decode_coverage_observations RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_decode_events.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_decode_events explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_decode_events RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_mat_outputs.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_mat_outputs explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_mat_outputs RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_account_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_obs_account_observations explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_obs_account_observations RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_obs_transaction_observations.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_obs_transaction_observations explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_obs_transaction_observations RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_ops_processing_ledger.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_ops_processing_ledger explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_ops_processing_ledger RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- file: migrations/postgres/maintenance/truncate/truncate_table_k_sol_raw_transactions.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Truncate k_sol_raw_transactions explicitly for maintenance.
|
||||||
|
TRUNCATE TABLE k_sol_raw_transactions RESTART IDENTITY CASCADE;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_index_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_keys_index_non_negative to k_sol_core_account_keys when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_keys_index_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_keys')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_index_non_negative CHECK (account_index >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_key_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_keys_key_not_empty to k_sol_core_account_keys when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_keys_key_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_keys')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_signature_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_keys_signature_not_empty to k_sol_core_account_keys when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_keys_signature_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_keys')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_slot_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_keys_slot_non_negative to k_sol_core_account_keys when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_keys_slot_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_keys')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_slot_non_negative CHECK (slot >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_keys_source.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_keys_source to k_sol_core_account_keys when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_keys_source'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_keys')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_keys ADD CONSTRAINT ck_k_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly'));
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_account_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_account_not_empty to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_account_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_account_not_empty CHECK (length(btrim(account_key)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_hash_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_hash_not_empty to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_hash_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_hash_not_empty CHECK (length(btrim(data_hash)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_lamports_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_lamports_non_negative to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_lamports_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_lamports_non_negative CHECK (lamports >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_owner_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_owner_not_empty to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_owner_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_owner_not_empty CHECK (length(btrim(owner)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_rent_epoch_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_rent_epoch_non_negative to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_rent_epoch_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_rent_epoch_non_negative CHECK (rent_epoch >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_slot_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_slot_non_negative to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_slot_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_slot_non_negative CHECK (slot >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_account_states_space_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_account_states_space_non_negative to k_sol_core_account_states when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_account_states_space_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_account_states')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_account_states ADD CONSTRAINT ck_k_sol_core_account_states_space_non_negative CHECK (space >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_index_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_account_index_non_negative to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_account_index_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_account_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_account_not_empty to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_account_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_index_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_index_non_negative to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_index_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_kind.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_kind to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_kind'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount'));
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_mint_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_mint_not_empty to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_mint_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_owner_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_owner_not_empty to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_owner_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_signature_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_signature_not_empty to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_signature_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_balance_changes_slot_non_negative.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_balance_changes_slot_non_negative to k_sol_core_balance_changes when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_balance_changes_slot_non_negative'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_balance_changes')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_balance_changes ADD CONSTRAINT ck_k_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_parent_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_inner_instructions_parent_not_empty to k_sol_core_inner_instructions when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_inner_instructions_parent_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_inner_instructions')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_path_not_empty.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_inner_instructions_path_not_empty to k_sol_core_inner_instructions when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_inner_instructions_path_not_empty'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_inner_instructions')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- file: migrations/postgres/schema/constraints/add_constraint_if_not_exists_ck_k_sol_core_inner_instructions_processing_state.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
-- Add ck_k_sol_core_inner_instructions_processing_state to k_sol_core_inner_instructions when absent.
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'ck_k_sol_core_inner_instructions_processing_state'
|
||||||
|
AND conrelid = to_regclass('k_sol_core_inner_instructions')
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE k_sol_core_inner_instructions ADD CONSTRAINT ck_k_sol_core_inner_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested'));
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user