Files
khadhroony-solana-project/docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md

29 KiB
Raw Blame History

Validation 0.3.4 — Store/PostgreSQL RawAccountState + complétude RAW

1. Portée

Cette matrice est ouverte par 0.3.4-pre.001. 0.3.4-pre.002 matérialise uniquement la fondation V002 : registry, deux tables et PK/FK de base. Son gate opérateur révèle deux canaris unitaires obsolètes sans défaut de migration ; 0.3.4-pre.002-fix.001 les corrige sans modifier les resources SQL. Les contraintes de domaine, l'index, la compatibilité externe complète, les repositories et les quatre capabilities account restent volontairement pending.

Base :

v0.3.3

Cible release :

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 / Page 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 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 :

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 :

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 / état physique État
V000 bytes/checksum inchangés PASS
V001 40 resources + checksum inchangés PASS
V002 logical name raw_account_state PASS pre.002
table state ksp_raw_account_states PASS pre.002
table observation ksp_raw_account_observations PASS pre.002
PK state (pubkey,slot,state_hash) PASS pre.002
PK observation (observation_key) PASS pre.002
FK observation -> state (account_pubkey,account_slot,account_state_hash) -> state composite PK PASS pre.002
contraintes domaine/fixed-width non encore ajoutées PENDING pre.003
index métier (slot,pubkey,state_hash) non encore ajouté PENDING pre.003
index owner/provider/time/status aucun prévu PASS negative scope
archive/tombstone account aucune PASS negative scope
V002 checksum intermédiaire 30ac8749…26311f55 sur les 5 resources pre.002 PROVISIONAL
V002 checksum final après contraintes/index de pre.003 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 dun 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 :

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 DONE
pre.002 V002 registry + deux tables + PK/FK de base, sans repository FIX.001
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

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.002

Fondation V002 minimale : PASS statique. Le registry logique V002 expose exactement cinq resources et aucune surface repository/runtime account.

Gate opérateur du 2026-08-30 : audits Rust/Markdown, cargo check --workspace, Clippy all-targets, ksp-store-api, ksp-store-lib, ksp-config-lib et ksp-store-lib --no-default-features passent. cargo test -p ksp-store-postgres-lib exécute 43 tests unitaires et échoue uniquement sur deux canaris obsolètes :

  • pre_003_fix_001_newer_history_is_rejected_without_down_migration attendait SchemaNewer avec une fausse version future 2, désormais occupée par V002 ;
  • pre_002_v002_resources_are_exactly_two_tables_plus_base_pk_fk_without_indexes_or_domain_checks comparait la FK à une chaîne sensible au saut de ligne avant REFERENCES.

Ces deux défauts appartiennent strictement au couloir pre.002 et sont corrigés par pre.002-fix.001. Ils ne remettent en cause ni les cinq resources V002 ni leur checksum.

Contraintes complètes/index/schema compatibility/checksum final : PENDING pre.003. Repository et capabilities account : PENDING pre.004+.

18. Correctif pre.002-fix.001

Le correctif applique deux recalibrages de tests seulement :

  1. le canari d'historique construit désormais le préfixe valide V000/V001/V002 puis ajoute une version 3 inconnue, ce qui teste réellement le cas « schéma plus récent » ;
  2. le canari de fondation V002 normalise les espaces ASCII du SQL embedded avant ses assertions sémantiques, sans relâcher l'inventaire exact des cinq resources ni les interdictions CREATE INDEX, CHECK, cascade, BIGSERIAL, BIGINT ou TEXT pour les données account.

Invariants du correctif :

  • aucune ressource sous migrations/v002_raw_account_state/ modifiée ;
  • checksum V002 intermédiaire inchangé : 30ac87496f1bb3805d816660891d7eab2127c599a636eb40c17ade5926311f55 ;
  • V000/V001 inchangées ;
  • aucun module repository account, dispatch Store ou implémentation RawAccount* ouvert ;
  • workspace.package.version = 0.3.4-pre.2.fix.1 conformément à VER-ID-007/010, car des sources Rust de tests sont modifiées.

Gate opérateur du correctif : À REJOUER.