v0.3.4-pre.001
This commit is contained in:
327
docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md
Normal file
327
docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md
Normal file
@@ -0,0 +1,327 @@
|
||||
<!-- file: docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Validation `0.3.4` — Store/PostgreSQL `RawAccountState` + complétude RAW
|
||||
|
||||
## 1. Portée
|
||||
|
||||
Cette matrice est ouverte par `0.3.4-pre.001`. La tranche courante valide **le design et le sizing uniquement** ; elle n'affirme aucune présence de V002 ou d'implémentation account.
|
||||
|
||||
Base :
|
||||
|
||||
```text
|
||||
v0.3.3
|
||||
```
|
||||
|
||||
Cible release :
|
||||
|
||||
```text
|
||||
PostgresBackend = 10/10 capabilities RAW
|
||||
Store = 10/10 capabilities RAW
|
||||
```
|
||||
|
||||
## 2. Gate base `pre.001`
|
||||
|
||||
| Preuve | Attendu | État pre.001 |
|
||||
|------------------------------------|------------------|-------------------------------------------------|
|
||||
| workspace.package.version base | 0.3.3 | PASS audit |
|
||||
| V000 checksum | d29068b8…46a1450 | PASS recalcul indépendant |
|
||||
| V001 checksum | 31488cda…d72cc51 | PASS recalcul indépendant / 40 resources |
|
||||
| capabilities transaction physiques | 6 | PASS inventaire source |
|
||||
| capabilities account physiques | 0 | PASS inventaire source ; volontairement pending |
|
||||
| canaries anti-account 0.3.3 | présentes | PASS inventaire tests |
|
||||
| ksp-store-api change nécessaire | non | PASS audit |
|
||||
| SQL V002 créé en pre.001 | non | PASS scope |
|
||||
|
||||
## 3. Matrice capability finale
|
||||
|
||||
| Capability | Base v0.3.3 | Cible 0.3.4 | Preuve finale |
|
||||
|--------------------------------|-------------|-------------|-------------------------------|
|
||||
| RawTransactionRead | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawTransactionWrite | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawTransactionObservationRead | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawTransactionObservationWrite | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawTransactionRetentionRead | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawTransactionRetentionWrite | PASS | PASS | régression suite 0.3.3 |
|
||||
| RawAccountStateRead | PENDING | PASS | tests backend + façade + live |
|
||||
| RawAccountStateWrite | PENDING | PASS | tests backend + façade + live |
|
||||
| RawAccountObservationRead | PENDING | PASS | tests backend + façade + live |
|
||||
| RawAccountObservationWrite | PENDING | PASS | tests backend + façade + live |
|
||||
|
||||
### 3.1 Contrat opérationnel détaillé des quatre capabilities account
|
||||
|
||||
| Capability | Input | Output | Guard réseau | SQL/transaction | Idempotence | Conflit | Absence/référence | Pagination | Error mapping | Preuve live |
|
||||
|----------------------------|---------------------|---------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------|----------------------------------|----------------------------------------|-----------------------------------|---------------------|---------------------------------------------------------------------------|------------------------------------------------|
|
||||
| RawAccountStateRead | reference / query | Option<State> / Page<Reference> | reference/query network == backend | SELECT ; list keyset | n/a | n/a | get absent -> None | oui, cursor account | WrongNetwork, QueryInvalid, PageLimitUnsupported, DataInvalid, ReadFailed | get exact + list matrix |
|
||||
| RawAccountStateWrite | state + observation | RawAcquisitionWriteOutcome | state + observation networks + exact same reference | transaction state+observation | exact existing -> AlreadyPresent | same full ref divergent -> RawConflict | n/a | non | WrongNetwork, Conflict, DataInvalid, WriteFailed | atomicité + identical/divergent + cancellation |
|
||||
| RawAccountObservationRead | observation_key | Option<Observation> | aucun input réseau ; backend fournit son network à la référence reconstruite | SELECT by PK | n/a | n/a | absent -> None | non | DataInvalid, ReadFailed | round-trip optional metadata |
|
||||
| RawAccountObservationWrite | observation | RawObservationWriteOutcome | observation.account.network == backend | transaction ; state FOR KEY SHARE + observation insert | exact existing -> AlreadyPresent | same key divergent -> RawConflict | state absent -> ReferenceNotFound | non | WrongNetwork, ReferenceNotFound, Conflict, DataInvalid, WriteFailed | missing ref + collision + concurrence |
|
||||
|
||||
## 4. Matrice `RawAccountState`
|
||||
|
||||
| Champ | Représentation décidée | Validation requise |
|
||||
|------------|------------------------------------------|-----------------------------------------------------------------|
|
||||
| network | binding DB existant `ksp_store_identity` | pas de colonne par ligne ; guard exact avant I/O |
|
||||
| pubkey | BYTEA NOT NULL | `octet_length = 32` ; composant PK |
|
||||
| slot | NUMERIC(20,0) NOT NULL | 0..18446744073709551615 ; aucun narrowing |
|
||||
| state_hash | BYTEA NOT NULL | `octet_length = 32` ; composant PK ; jamais recalculé par Store |
|
||||
| lamports | NUMERIC(20,0) NOT NULL | domaine u64 exact |
|
||||
| owner | BYTEA NOT NULL | `octet_length = 32` |
|
||||
| executable | BOOLEAN NOT NULL | mapping direct |
|
||||
| rent_epoch | NUMERIC(20,0) NOT NULL | domaine u64 exact |
|
||||
| data | BYTEA NOT NULL | 0..=16 MiB ; vide autorisé ; aucun base64 |
|
||||
|
||||
Points structurants :
|
||||
|
||||
```text
|
||||
identity = network API + (pubkey, slot, state_hash)
|
||||
network SQL = singleton ksp_store_identity, pas colonne row
|
||||
PK state = (pubkey, slot, state_hash)
|
||||
state_hash n'est pas une preuve suffisante d'idempotence : comparaison contenu exacte obligatoire
|
||||
empty data = valide
|
||||
```
|
||||
|
||||
## 5. Matrice observation
|
||||
|
||||
| Champ | Représentation décidée | Validation requise |
|
||||
|---------------------------|------------------------|--------------------------------------------------|
|
||||
| observation_key | BYTEA NOT NULL | PK family-local ; collision = comparaison exacte |
|
||||
| account.pubkey | BYTEA NOT NULL | FK composite vers state |
|
||||
| account.slot | NUMERIC(20,0) NOT NULL | domaine u64 exact ; FK composite |
|
||||
| account.state_hash | BYTEA NOT NULL | FK composite |
|
||||
| provider | TEXT NOT NULL | 1..128 octets + alphabet logique stable V001 |
|
||||
| protocol | TEXT NOT NULL | idem V001 |
|
||||
| acquisition_method | TEXT NOT NULL | idem V001 |
|
||||
| origin | TEXT NOT NULL | backfill/import/live/repair/replay |
|
||||
| received_at | BIGINT NOT NULL | 0..253402300799999 |
|
||||
| capture_session_id | TEXT NULL | mêmes checks V001 |
|
||||
| commitment | TEXT NULL | mêmes checks V001 |
|
||||
| endpoint_id | TEXT NULL | mêmes checks V001 |
|
||||
| filter_id | TEXT NULL | mêmes checks V001 |
|
||||
| observed_at | BIGINT NULL | borne + `observed_at <= received_at` |
|
||||
| source_payload_hash | BYTEA NULL | NULL ou 32 bytes |
|
||||
| source_payload_size_bytes | BIGINT NULL | 0..=64 MiB ; borne API tient dans BIGINT |
|
||||
| is_startup | BOOLEAN NULL | Yellowstone metadata observation-only |
|
||||
| transaction_signature | BYTEA NULL | NULL ou 64 bytes ; aucune FK transaction |
|
||||
| write_version | NUMERIC(20,0) NULL | NULL ou domaine u64 exact |
|
||||
|
||||
Invariants additionnels :
|
||||
|
||||
```text
|
||||
FK observation -> state composite
|
||||
record_raw_account_observation n'insère jamais le state
|
||||
transaction_signature observation-only, sans FK transaction
|
||||
write_version/is_startup observation-only
|
||||
observation_key family-local unique
|
||||
```
|
||||
|
||||
## 6. Query/pagination
|
||||
|
||||
| Élément | Décision | Canari requis |
|
||||
|-----------------|------------------------------------------------|--------------------------------|
|
||||
| ordre ASC | slot, pubkey, state_hash ASC | multi-page sans duplicate/skip |
|
||||
| ordre DESC | slot, pubkey, state_hash DESC | symétrique ASC |
|
||||
| filtre pubkey | égalité optionnelle | cursor lié au filtre |
|
||||
| slot range | inclusive | cursor lié aux deux bornes |
|
||||
| cursor magic | KSPA | rejette KSPT transaction |
|
||||
| cursor version | 1 | rejette version hostile |
|
||||
| cursor longueur | 109 bytes | rejette taille hostile |
|
||||
| cursor domain | KSP/raw-account-state-cursor/v1 | digest family-bound |
|
||||
| cursor digest | network+pubkey filter+range+direction+last key | anti-replay query |
|
||||
| pagination | keyset seulement | aucun OFFSET |
|
||||
| limit | limite physique PostgreSQL uniquement | pas de batch policy |
|
||||
|
||||
## 7. Migration/schema V002
|
||||
|
||||
| Objet | Décision pre.001 | État |
|
||||
|----------------------------------|---------------------------------------|-------------------------|
|
||||
| V000 | immuable | PASS design |
|
||||
| V001 | immuable | PASS design |
|
||||
| V002 logical name | raw_account_state | PASS design |
|
||||
| table state | ksp_raw_account_states | PASS design |
|
||||
| table observation | ksp_raw_account_observations | PASS design |
|
||||
| archive/tombstone account | aucune | PASS negative scope |
|
||||
| index métier | (slot,pubkey,state_hash) | PASS design |
|
||||
| index owner/provider/time/status | aucun | PASS negative scope |
|
||||
| SQL/resource files | pas encore créés | PENDING pre.002/pre.003 |
|
||||
| V002 checksum | inconnu tant que resources non créées | PENDING pre.003 |
|
||||
|
||||
## 8. Idempotence et concurrence
|
||||
|
||||
| Scénario | Résultat attendu | État |
|
||||
|------------------------------------------------------------|-------------------------------------------------------|-------------------|
|
||||
| 2 acquisitions identiques, même ref + même observation key | 1 Inserted/Inserted ; 1 AlreadyPresent/AlreadyPresent | PENDING impl/live |
|
||||
| 2 acquisitions même ref, contenu divergent | un gagnant durable ; autre `RAW_CONFLICT` | PENDING impl/live |
|
||||
| même pubkey+slot, state_hash différents | les deux states sont représentables | PENDING impl/live |
|
||||
| observation key identique, observations divergentes | une observation gagne ; autre `RAW_CONFLICT` | PENDING impl/live |
|
||||
| record observation identique concurrent | 1 Inserted ; 1 AlreadyPresent | PENDING impl/live |
|
||||
| record observation divergente concurrent | 1 Inserted ; 1 `RAW_CONFLICT` | PENDING impl/live |
|
||||
| annulation après insertion state avant observation | aucune moitié durable | PENDING impl/live |
|
||||
|
||||
Le test de contenu divergent doit comparer les fields complets et ne jamais traiter l'égalité de `state_hash` comme preuve suffisante.
|
||||
|
||||
## 9. Threat model
|
||||
|
||||
| Menace | Mitigation figée | Preuve |
|
||||
|---------------------------------------------------|-----------------------------------------------------------------------|----------------------------|
|
||||
| URI pointe vers DB d’un autre réseau | binding `ksp_store_identity` + guard réseau avant opération | unit + live |
|
||||
| pubkey/hash/signature DB de mauvaise largeur | CHECK SQL + mapping `DataInvalid` statique | unit hostile rows |
|
||||
| slot/lamports/rent_epoch/write_version > i64::MAX | NUMERIC(20,0) + conversion décimale fallible | unit limites u64 |
|
||||
| data account > 16 MiB | admission API + CHECK `octet_length <= 16777216` | unit + live boundary |
|
||||
| data account vide | aucune borne basse SQL ; vide explicitement valide | unit |
|
||||
| observation sans state | FK composite + record pré-vérifie state | unit + live |
|
||||
| échec observation après state insert | une transaction PostgreSQL commune | live cancellation/rollback |
|
||||
| même ref, contenu divergent | ON CONFLICT DO NOTHING + SELECT FOR UPDATE + comparaison exacte | unit + live concurrent |
|
||||
| même pubkey/slot, hashes distincts | PK `(pubkey,slot,state_hash)` | unit + live |
|
||||
| collision observation key | comparaison exacte de tous les champs ; conflict sinon | unit + live |
|
||||
| cursor transaction injecté dans account | magic/domain account distincts | unit cursor |
|
||||
| cursor account rejoué sur autre query | digest lie network/pubkey/range/direction/last key | unit cursor matrix |
|
||||
| ordre même slot ambigu | ordre total `(slot,pubkey,state_hash)` | unit + live pagination |
|
||||
| metadata Yellowstone promue au state | is_startup/transaction_signature/write_version uniquement observation | schema + mapping canary |
|
||||
| transaction_signature impose transaction row | aucune FK vers `ksp_raw_transactions` | schema canary |
|
||||
| SQL/server text fuit | projection PostgresBackendError statique | hardening |
|
||||
| V000/V001 modifiées | checksums figés et canaries exactes | migration tests |
|
||||
| index non justifié | un seul index métier V002 pour query unfiltered | schema completeness |
|
||||
|
||||
## 10. Audit historique kbot3
|
||||
|
||||
| Élément | Classement | Disposition |
|
||||
|----------------------------------------------------------|------------|-----------------------------------------------------------------------|
|
||||
| unicité observation_key et FK comme invariants physiques | REPRENDRE | garder le principe, avec clés KSP actuelles |
|
||||
| index account+slot pour navigation déterministe | REPRENDRE | réutiliser le principe seulement pour les queries réellement exposées |
|
||||
| NUMERIC(20,0) pour lamports/rent_epoch | REPRENDRE | étendre à slot/write_version pour le domaine u64 complet |
|
||||
| BIGSERIAL/id technique comme identité | REDESSINER | identité durable = `(pubkey, slot, state_hash)` |
|
||||
| pubkey/owner/hash en TEXT/base58 | REDESSINER | BYTEA fixed-width |
|
||||
| account data en base64 + `space` indépendant | REDESSINER | BYTEA complet ; longueur dérivée ; aucun double truth |
|
||||
| observation duplique l’état canonique | REDESSINER | observation référence un state complet séparé |
|
||||
| slot BIGINT/i64 | REDESSINER | NUMERIC(20,0) sans narrowing |
|
||||
| normalisation N1 -> CORE + processing ledger/version | REPORTER | future CORE/jobs/workers ; hors Store RAW 0.3.4 |
|
||||
| batch maximum 1000 / policy de sélection | REPORTER | policy worker/job ; pas pagination Store |
|
||||
| indexes owner/provider/method/time/status | REPORTER | aucune query publique actuelle ne les justifie |
|
||||
| status/error_message persistés dans observation RAW | REJETER | observation réussie uniquement ; erreurs sûres hors ligne métier |
|
||||
| sqlx et Store monolithique | REJETER | backend tokio-postgres séparé derrière ksp-store-lib |
|
||||
| maintenance CASCADE/TRUNCATE comme surface normale | REJETER | aucune destructive capability account |
|
||||
|
||||
Le pipeline historique `k_sol_obs_account_observations -> k_sol_core_account_states` reste une source de comparaison, pas une architecture cible. La normalisation versionnée et ses caps de batch sont hors scope `0.3.4`.
|
||||
|
||||
## 11. Canaries `0.3.3` à faire évoluer
|
||||
|
||||
À l'ouverture effective de la surface account :
|
||||
|
||||
```text
|
||||
ksp-store-lib/tests/dependency_boundary.rs
|
||||
remplacer interdiction RawAccount par exigence des 4 impl + absence types SQL
|
||||
|
||||
ksp-store-lib/tests/hardening_completeness.rs
|
||||
remplacer inventaire 6 + RawAccount fermé par inventaire exact 10
|
||||
|
||||
ksp-store-postgres-lib/tests/dependency_boundary.rs
|
||||
ajouter module raw_account privé et exiger les 4 impl dans runtime bridge
|
||||
conserver V000 et health business-free
|
||||
|
||||
ksp-store-postgres-lib/tests/hardening_completeness.rs
|
||||
remplacer interdiction RawAccount globale par inventaire exact 10
|
||||
remplacer l'absence V002/account par checks de surface V002 minimale
|
||||
```
|
||||
|
||||
Les tests historiques nommés `pre_XXX` peuvent conserver leurs noms si le contrat qu'ils vérifient reste valide ; seul leur contenu doit être élargi lorsqu'il fige intentionnellement le scope `0.3.3`.
|
||||
|
||||
## 12. Taxonomie d'erreur à prouver
|
||||
|
||||
| Cas | Code/projection attendue | État |
|
||||
|---------------------------------------------------|------------------------------------------------------|--------------|
|
||||
| mauvais réseau opération | WrongNetwork -> `store.wrong_network` | PENDING impl |
|
||||
| référence absente pour observation supplémentaire | ReferenceNotFound -> `store.raw_reference_not_found` | PENDING impl |
|
||||
| contenu divergent / collision observation | Conflict -> `store_api.raw_conflict` | PENDING impl |
|
||||
| cursor/query invalide | QueryInvalid -> `store_api.raw_query_invalid` | PENDING impl |
|
||||
| page > limite physique PostgreSQL | PageLimitUnsupported | PENDING impl |
|
||||
| row DB incompatible/malformée | DataInvalid | PENDING impl |
|
||||
| échec SELECT | ReadFailed | PENDING impl |
|
||||
| échec INSERT/transaction | WriteFailed | PENDING impl |
|
||||
| V002 absente/incompatible/divergente | MigrationMismatch / MigrationFailed / SchemaNewer | PENDING impl |
|
||||
|
||||
Aucune nouvelle variante backend n'est justifiée au gate `pre.001`.
|
||||
|
||||
## 13. Preuve live finale
|
||||
|
||||
Le test live account devra être opt-in/ignored, URI stdin, sans environnement ni fuite de secret. Il doit couvrir V002 et rejouer un canari transaction dans la même base afin de prouver la coexistence cross-family.
|
||||
|
||||
| Scénario live | Obligation |
|
||||
|-----------------------------------------|-------------|
|
||||
| schema update V001 -> V002 | obligatoire |
|
||||
| reopen idempotent | obligatoire |
|
||||
| u64 > i64::MAX / u64::MAX | obligatoire |
|
||||
| data empty / 16 MiB | obligatoire |
|
||||
| identical/divergent acquisition | obligatoire |
|
||||
| same pubkey+slot / distinct hash | obligatoire |
|
||||
| additional observation + missing ref | obligatoire |
|
||||
| collision observation | obligatoire |
|
||||
| keyset filtered/unfiltered ASC/DESC | obligatoire |
|
||||
| cursor hostile/cross-query/cross-family | obligatoire |
|
||||
| concurrency | obligatoire |
|
||||
| cancellation rollback | obligatoire |
|
||||
| RawTransaction regression canary | obligatoire |
|
||||
| cleanup isolation | obligatoire |
|
||||
|
||||
## 14. Sizing
|
||||
|
||||
| Tranche | Objet | État |
|
||||
|---------|-------------------------------------------------------------------|---------|
|
||||
| pre.001 | audit, kbot3, threat model, V002 design, sizing, plan/validation | CURRENT |
|
||||
| pre.002 | V002 registry + deux tables + PK/FK de base, sans repository | PLANNED |
|
||||
| pre.003 | contraintes complètes, index, schema compatibility, checksum V002 | PLANNED |
|
||||
| pre.004 | mapping privé state/observation + get reads + hostile rows | PLANNED |
|
||||
| pre.005 | acquisition atomique state+observation + idempotence/conflict | PLANNED |
|
||||
| pre.006 | observation supplémentaire + races/cancellation unitaires | PLANNED |
|
||||
| pre.007 | list RawAccountStateQuery + keyset cursor V1 account | PLANNED |
|
||||
| pre.008 | 4 impl backend + 4 dispatch Store + conformance 10/10 | PLANNED |
|
||||
| pre.009 | preuve PostgreSQL live account + coexistence RawTransaction | PLANNED |
|
||||
| pre.010 | hardening/completeness cross-family + canaries ownership | PLANNED |
|
||||
| pre.011 | gate technique final + replay live ciblé + graphes | PLANNED |
|
||||
| pre.012 | réconciliation docs finale sans rouvrir le code | PLANNED |
|
||||
| pre.013 | publication prep minimale + prompt 0.3.5 | PLANNED |
|
||||
| rel.001 | stabilisation/tag après gate opérateur | PLANNED |
|
||||
|
||||
## 15. Negative scope final
|
||||
|
||||
| Surface | Doit rester absent en 0.3.4 |
|
||||
|--------------------------------------------|-----------------------------|
|
||||
| ksp-store-api redesign | oui |
|
||||
| account retention/archive/purge | oui |
|
||||
| CORE/STRUCTURAL persistence | oui |
|
||||
| workers/jobs/backfills | oui |
|
||||
| Store batch/priorité policy | oui |
|
||||
| owner/provider/time/status queries/indexes | oui |
|
||||
| Transport -> Store coupling | oui |
|
||||
| PostgreSQL types via ksp-store-lib | oui |
|
||||
| nouveau backend | oui |
|
||||
|
||||
## 16. Gate opérateur prévu pour clôture technique
|
||||
|
||||
```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
|
||||
cargo test --workspace
|
||||
cargo tree -p ksp-store-lib --edges normal
|
||||
cargo tree -p ksp-store-lib -e features
|
||||
cargo tree -p ksp-store-postgres-lib --edges normal
|
||||
cargo tree --duplicates
|
||||
```
|
||||
|
||||
Les preuves live restent séparées et explicitement `--ignored --nocapture`.
|
||||
|
||||
## 17. Verdict `pre.001`
|
||||
|
||||
Design : **PASS**. Les audits Rust/Markdown de l'overlay sont propres.
|
||||
|
||||
Compilation/Clippy/tests post-overlay : **NON EXÉCUTÉS dans le conteneur d'assemblage**, faute d'exécutable `cargo`; ils restent à rejouer par l'opérateur.
|
||||
|
||||
Implémentation V002/account : **PENDING**, volontairement non ouverte dans cette tranche.
|
||||
Reference in New Issue
Block a user