Files
khadhroony-solana-project/deltas/0.3.4/pre.004.md
2026-08-30 21:57:23 +02:00

299 lines
10 KiB
Markdown

<!-- file: deltas/0.3.4/pre.004.md -->
<!-- version: 1 -->
# Delta `0.3.4-pre.004` — mapping PostgreSQL privé et lectures `RawAccountState`
## 1. Base requise
```text
0.3.4-pre.3.fix.1
```
Le gate opérateur fourni le 2026-08-30 pour `pre.003-fix.001` est entièrement vert :
```text
cargo fmt --all PASS
audit Rust général / exports / workspace PASS
audit Markdown PASS — 239 tables / 139 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-store-api PASS
cargo test -p ksp-store-lib PASS
cargo test -p ksp-store-postgres-lib PASS — 45 unit tests + canaris, live ignored
cargo test -p ksp-config-lib PASS — 128 unit tests + ownership/public API
cargo check -p ksp-store-lib --no-default-features PASS
```
V002 finale et son checksum sont donc considérés acquis avant l'ouverture du mapping account.
## 2. Objectif
Implémenter la tranche de lecture de la vertical slice PostgreSQL `RawAccountState` sans ouvrir les writes, la pagination ni les implémentations de traits :
```text
mapping SQL privé state
mapping SQL privé observation
get_raw_account_state
get_raw_account_observation
hostile-row guards
```
Les méthodes du backend retournent uniquement des modèles `ksp-store-api`. Aucun `tokio_postgres::Row`, SQL, bind, SQLSTATE ou type physique ne traverse le bridge public.
## 3. Version
Le workspace passe à :
```text
0.3.4-pre.4
```
Aucune crate ne redéfinit localement la version héritée.
## 4. Module physique privé
Nouveau module :
```text
crates/ksp-store-postgres-lib/src/raw_account.rs
```
Il possède les deux SELECT et les codecs physiques de cette tranche. `runtime.rs` ne contient aucun SQL métier et délègue les lectures au module privé.
La tranche est strictement read-only :
```text
INSERT INTO absent
UPDATE absent
DELETE FROM absent
ON CONFLICT absent
FOR UPDATE absent
list/cursor account absent
```
Les écritures state+observation restent `pre.005`, l'observation supplémentaire `pre.006`, la pagination/cursor `pre.007` et les quatre `impl RawAccount*` `pre.008`.
## 5. `get_raw_account_state`
Le SELECT adresse exactement la clé physique :
```text
(pubkey, slot, state_hash)
```
Le mapping reconstruit :
```text
network <- backend mono-réseau
pubkey <- BYTEA exactement 32 bytes
slot <- NUMERIC(20,0)::text -> u64
state_hash <- BYTEA exactement 32 bytes
lamports <- NUMERIC(20,0)::text -> u64
owner <- BYTEA exactement 32 bytes
executable <- BOOLEAN
rent_epoch <- NUMERIC(20,0)::text -> u64
data <- BYTEA exact -> RawAccountState::try_new
```
Le chemin décimal couvre tout le domaine `u64`, y compris les valeurs supérieures à `i64::MAX` et `u64::MAX` lui-même. Aucun narrowing via `BIGINT` n'est introduit.
`RawAccountState::try_new` revalide la borne backend-agnostic des bytes account : data vide est valide ; une ligne hostile dépassant 16 MiB devient `DataInvalid`.
Après décodage, la référence reconstruite doit rester exactement égale à la référence demandée. Une cardinalité autre que 0/1 est également `DataInvalid`.
## 6. Garde réseau pré-I/O
`get_raw_account_state` vérifie avant `pool.get()` :
```text
reference.network == backend.network -> continuer
sinon -> WrongNetwork
```
Le mauvais réseau ne consomme donc aucune connexion PostgreSQL et n'expose aucune valeur fournie.
## 7. `get_raw_account_observation`
`RawObservationKey` ne porte pas de réseau. Le réseau de la `RawAccountStateReference` reconstruite provient donc exclusivement du backend mono-réseau déjà lié par `ksp_store_identity`.
Le mapping couvre :
```text
observation_key
account.pubkey
account.slot
account.state_hash
provider
protocol
acquisition_method
origin
received_at
capture_session_id optionnel
commitment optionnel
endpoint_id optionnel
filter_id optionnel
observed_at optionnel
source_payload_hash optionnel
source_payload_size_bytes optionnel
is_startup optionnel
transaction_signature optionnelle
write_version optionnel
```
Spécificités physiques :
```text
account.slot / write_version -> NUMERIC(20,0)::text -> u64
transaction_signature -> NULL ou exactement 64 bytes
hash/key/pubkey -> exactement 32 bytes
received/observed -> BIGINT -> u64 -> RawTimestamp
source payload size -> BIGINT -> u64 + borne API 64 MiB
provenance codes -> constructeurs API fallibles
origin -> cinq variantes API uniquement
```
Les metadata Yellowstone restent strictement observation-only. Une colonne NULL reste `None`; aucune valeur `false`, zéro, signature ou write version n'est inventée.
## 8. Hostile-row guards
Les tests unitaires couvrent notamment :
```text
state slot/lamports/rent_epoch = u64::MAX
write_version = u64::MAX
account data vide
account data > 16 MiB -> DataInvalid
pubkey de 31 bytes -> DataInvalid
signature transaction de 63 bytes -> DataInvalid
décimal négatif ou > u64::MAX -> DataInvalid
origin hostile -> DataInvalid
observed_at > received_at -> DataInvalid
metadata optionnelles absentes -> None
wrong network state reference -> WrongNetwork pré-I/O
```
Les erreurs ne retiennent que `PostgresBackendErrorKind` + phase `&'static str`; les chaînes hostiles ne sont jamais interpolées dans l'erreur.
## 9. Surface backend
`PostgresBackend` expose désormais le bridge étroit :
```text
get_raw_account_state
get_raw_account_observation
```
Ces méthodes ne constituent pas encore les implémentations de `RawAccountStateRead` et `RawAccountObservationRead` : le premier trait exige aussi `list_raw_account_states`, réservé à `pre.007`. Les quatre traits account seront ouverts ensemble en `pre.008` afin de passer directement de 6/10 à 10/10 capabilities physiques.
## 10. Canaries transformés
Les canaris historiques qui interdisaient tout module account sont ajustés sans perdre leur rôle :
- `dependency_boundary.rs` exige un module `raw_account` privé, read-only, sans pagination ni trait impl ;
- `hardening_completeness.rs` inclut le nouveau module dans l'inventaire exact et dans les scans anti-secret/anti-reverse-edge ;
- les interdictions des quatre `impl RawAccount* for PostgresBackend` restent actives ;
- `public_api.rs` prouve que les deux méthodes du bridge n'utilisent que les modèles backend-agnostic.
Les canaris V000/health business-free restent inchangés.
## 11. Migrations
Aucune ressource sous `migrations/` n'est modifiée.
Checksums conservés :
```text
V000 = d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
V001 = 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51
V002 = ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e
```
Le bridge du checksum provisoire `pre.002` reste inchangé.
## 12. Fichiers ajoutés
```text
crates/ksp-store-postgres-lib/src/raw_account.rs
crates/ksp-store-postgres-lib/unit_tests/raw_account.rs
deltas/0.3.4/pre.004.md
```
## 13. Fichiers modifiés
```text
Cargo.toml
crates/ksp-store-postgres-lib/src/lib.rs
crates/ksp-store-postgres-lib/src/runtime.rs
crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
crates/ksp-store-postgres-lib/tests/public_api.rs
docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md
docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md
```
## 14. Fichiers supprimés
```text
aucun
```
## 15. Validations exécutées
Dans l'environnement d'assemblage :
```text
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.4
PASS après alignement RustRover des tableaux touchés
```
Le contrôle différentiel doit également confirmer qu'aucune ressource V000/V001/V002 n'a changé.
## 16. Validations non exécutées dans l'environnement d'assemblage
`cargo`, `rustc` et `rustfmt` ne sont pas disponibles dans cet environnement. Ne sont donc pas revendiqués PASS ici :
```text
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-store-api
cargo test -p ksp-store-lib
cargo test -p ksp-store-postgres-lib
cargo test -p ksp-config-lib
cargo check -p ksp-store-lib --no-default-features
```
## 17. Décisions prises
- Réutiliser la frontière éprouvée de `0.3.3-pre.004` : SQL/mapping privé, méthodes backend étroites, traits plus tard.
- Garder les conversions `NUMERIC(20,0)` via `::text -> u64` afin de ne pas ajouter de dépendance décimale ni de narrowing.
- Revalider les invariants API au read même si V002 possède déjà des CHECK SQL ; la DB peut être externe, héritée ou hostile.
- Ne pas factoriser prématurément les codecs transaction/account dans un nouveau module commun : les familles ont des formes différentes et `pre.004` doit rester bornée.
- Ne pas ouvrir `RawAccountStateRead` avant que `list_raw_account_states` existe ; l'inventaire de capabilities reste volontairement 6/10 jusqu'à `pre.008`.
## 18. Questions ouvertes
Aucune question bloquante nouvelle. Les détails des writes, races et cursor restent ceux figés par le plan `pre.001` et appartiennent respectivement à `pre.005`, `pre.006` et `pre.007`.
## 19. Gate opérateur attendu
```text
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.4
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-store-api
cargo test -p ksp-store-lib
cargo test -p ksp-store-postgres-lib
cargo test -p ksp-config-lib
cargo check -p ksp-store-lib --no-default-features
```
Aucun test live PostgreSQL account n'est demandé dans cette tranche ; la preuve live complète reste `pre.009`.