287 lines
12 KiB
Markdown
287 lines
12 KiB
Markdown
<!-- file: deltas/0.3.6/pre.005.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# Delta `0.3.6-pre.005` — fondation Backfill et découverte bornée
|
|
|
|
## Base requise
|
|
|
|
```text
|
|
0.3.6-pre.004 appliquée
|
|
workspace.package.version = 0.3.6-pre.4
|
|
```
|
|
|
|
Le gate opérateur fourni pour cette base confirme :
|
|
|
|
```text
|
|
cargo fmt --all PASS
|
|
python3 scripts/audit_rust_workspace_rules.py PASS / clean
|
|
python3 scripts/audit_markdown_tables.py ... PASS / clean (264 tables / 146 fichiers)
|
|
cargo check --workspace PASS
|
|
cargo clippy --workspace --all-targets PASS
|
|
cargo test -p ksp-onchain-transport-lib PASS
|
|
unitaires 385 PASS
|
|
public_api 50 PASS
|
|
release_completeness 43 PASS
|
|
doctests 4 PASS
|
|
cargo tree -p ksp-onchain-transport-lib --edges normal exécuté
|
|
cargo tree -p ksp-onchain-transport-lib -e features exécuté
|
|
```
|
|
|
|
Les smokes réseau restent opt-in et ignorés dans le gate normal, conformément à leur contrat.
|
|
|
|
## Objectif
|
|
|
|
Matérialiser exclusivement la tranche `pre.005` du plan 027 : créer `ksp-job-backfill-lib` et figer la requête, les scopes, l'identité des candidats, les bornes ainsi que la découverte paginée déterministe avant toute hydratation `getTransaction`, conversion RAW, persistance Store, checkpoint ou runtime de Job.
|
|
|
|
Cette tranche résout explicitement un invariant d'identité qui ne doit jamais dépendre de la topologie d'acquisition : une transaction/signature est identifiée logiquement par son réseau et sa signature, pas par le provider, l'endpoint ou le protocole qui l'a retrouvée.
|
|
|
|
## Identité réseau contre provenance Transport
|
|
|
|
Le contrat de découverte introduit :
|
|
|
|
```text
|
|
BackfillCandidateIdentity = (RawNetworkId, BackfillSignature)
|
|
```
|
|
|
|
`RawNetworkId` est un identifiant logique ouvert réexporté par `ksp-store-lib`. Des namespaces comme `mainnet`, `devnet`, `testnet`, `localnet`, `synthetic` ou un futur réseau nommé restent donc distincts sans imposer un enum fermé dans Job.
|
|
|
|
En conséquence :
|
|
|
|
- la même signature observée sur deux réseaux différents représente deux identités distinctes ;
|
|
- la même signature acquise plusieurs fois sur le même réseau via HTTP, WebSocket ou gRPC converge vers la même identité transactionnelle ;
|
|
- rôle Transport, provider, endpoint et protocole décrivent la sélection ou la provenance d'acquisition ; ils ne participent jamais à l'identité transactionnelle ;
|
|
- `JobId` et la future concurrence d'hydratation ne participent pas non plus au fingerprint sémantique d'un scope.
|
|
|
|
Le Store API possède déjà `RawTransactionReference { network, signature }`. Le backend PostgreSQL actuel lie en outre un Store physique à un seul `RawNetworkId` via `ksp_store_identity`; une clé physique locale basée sur la signature y reste donc dans un namespace réseau unique. Tout futur backend hébergeant plusieurs réseaux dans le même namespace physique devra préserver l'identité logique `(network, signature)` par sa clé, sa partition ou un mécanisme équivalent.
|
|
|
|
## Crate `ksp-job-backfill-lib`
|
|
|
|
La nouvelle crate est une bibliothèque comportementale indépendante, sans exécutable ni Worker. Ses dépendances normales sont limitées à :
|
|
|
|
```text
|
|
ksp-core-lib
|
|
ksp-job-api
|
|
ksp-logging-lib
|
|
ksp-onchain-transport-lib
|
|
ksp-store-lib (default-features = false)
|
|
sha2 (workspace)
|
|
```
|
|
|
|
Tokio est uniquement une `dev-dependency` avec les features de test nécessaires aux canaries async. Aucun backend Store n'est activé par défaut par Backfill.
|
|
|
|
La crate ne dépend pas directement de Config, Interface, Program, Wallet, Store API, Store PostgreSQL, reqwest, tonic ou d'une crate protocolaire Solana.
|
|
|
|
## Requête bornée
|
|
|
|
`BackfillRequest` impose explicitement :
|
|
|
|
- `JobId` ;
|
|
- `RawNetworkId` ;
|
|
- rôle HTTP Transport ;
|
|
- engagement `Confirmed` ou `Finalized` ;
|
|
- un des quatre scopes ;
|
|
- page size `1..=1000` ;
|
|
- max pages `1..=10000` ;
|
|
- max candidates `1..=10000` ;
|
|
- future concurrence d'hydratation `1..=64` ;
|
|
- `min_context_slot` optionnel uniquement pour les scopes adresse.
|
|
|
|
Le rôle HTTP sert uniquement à la sélection Transport. Il est volontairement exclu du fingerprint du scope avec provider, endpoint et protocole.
|
|
|
|
## Scopes
|
|
|
|
Quatre scopes sont matérialisés :
|
|
|
|
- `LatestAddress(address)` : fenêtre courante depuis le plus récent ;
|
|
- `BeforeAddress(address, anchor)` : historique plus ancien que l'ancre exclusive ;
|
|
- `AfterAddress(address, anchor)` : fenêtre plus récente la plus proche d'une ancre exclusive ;
|
|
- `ExplicitSignatures(signatures)` : liste explicite bornée sans découverte adresse.
|
|
|
|
Les signatures explicites sont dédupliquées selon la première occurrence et conservent leur ordre d'entrée.
|
|
|
|
`BackfillSignature` valide dans cette tranche uniquement la forme texte Base58 bornée compatible avec une signature Solana de 64 octets. Le décodage Base58 exact vers la signature RAW canonique de 64 octets reste volontairement réservé à `pre.006`; `pre.005` ne prétend donc pas encore valider le contenu binaire canonique.
|
|
|
|
## Fingerprint de scope
|
|
|
|
`BackfillScopeFingerprint` utilise SHA-256 avec séparation de domaine et couvre les paramètres qui changent réellement la sémantique de découverte :
|
|
|
|
- réseau ;
|
|
- commitment ;
|
|
- type de scope ;
|
|
- adresse ou liste explicite ;
|
|
- direction/ancre ;
|
|
- page size ;
|
|
- max pages ;
|
|
- max candidates ;
|
|
- `min_context_slot`.
|
|
|
|
Il exclut intentionnellement :
|
|
|
|
- `JobId` ;
|
|
- rôle HTTP ;
|
|
- provider ;
|
|
- endpoint ;
|
|
- protocole ;
|
|
- concurrence d'hydratation.
|
|
|
|
Changer de source d'acquisition ne change donc pas l'identité du scope ni celle des transactions candidates.
|
|
|
|
## Découverte paginée
|
|
|
|
La voie de production appelle uniquement le wrapper typé existant :
|
|
|
|
```text
|
|
HttpTransportPool::get_signatures_for_address
|
|
```
|
|
|
|
Aucun client HTTP, retry loop, pacing, cooldown, sélection d'endpoint ou politique fournisseur n'entre dans Job.
|
|
|
|
Les règles figées sont :
|
|
|
|
- Latest et Before utilisent le curseur `before` et conservent l'ordre RPC du plus récent au plus ancien ;
|
|
- Before démarre avec l'ancre exclusive fournie ;
|
|
- After utilise `until = anchor`, avance par `before` et ne conserve que les `max_candidates` les plus proches de l'ancre parmi les signatures plus récentes ;
|
|
- la déduplication est stable entre pages ;
|
|
- une page fournisseur plus grande que la limite demandée est rejetée ;
|
|
- un curseur qui n'avance pas est rejeté ;
|
|
- une page courte ou vide matérialise `RpcBoundary` ;
|
|
- atteindre la limite de candidats matérialise `CandidateLimit` ;
|
|
- épuiser `max_pages` en Latest/Before matérialise `PageLimit` et un résultat partiel ;
|
|
- épuiser `max_pages` en After avant preuve de la frontière matérialise `AfterAnchorNotReached` et un résultat partiel.
|
|
|
|
La borne After est volontairement conservative : un Job ne prétend jamais avoir rejoint l'ancre si la limite de pages empêche de le prouver.
|
|
|
|
## Tests matérialisés
|
|
|
|
La tranche ajoute **11 tests unitaires** :
|
|
|
|
- validation et redaction de signature ;
|
|
- déduplication explicite stable ;
|
|
- bornes exactes de requête ;
|
|
- fingerprint indépendant de JobId/rôle/concurrence mais distinct par réseau ;
|
|
- distinction sémantique Before/After ;
|
|
- Latest paginé avec déduplication inter-pages ;
|
|
- Before avec ancre exclusive et progression du curseur ;
|
|
- After avec fenêtre la plus proche de l'ancre ;
|
|
- After borné partiel ;
|
|
- Latest borné partiel ;
|
|
- explicite sans appel Transport et avec identité réseau.
|
|
|
|
Elle ajoute **6 canaries d'intégration** :
|
|
|
|
- deux canaries de dépendances/firewalls ;
|
|
- deux canaries d'API publique, dont l'identité `(network, signature)` ;
|
|
- deux canaries de complétude qui empêchent l'ouverture anticipée de RAW/persistance/checkpoint/runtime.
|
|
|
|
Les doubles de page sont privés aux unit tests ; aucune abstraction fake n'entre dans l'API publique.
|
|
|
|
## Documentation courante réconciliée
|
|
|
|
Les documents d'architecture actifs sont alignés avec le nom désormais matérialisé `ksp-job-backfill-lib` et son graphe réel sans Config :
|
|
|
|
```text
|
|
docs/architecture/004-COMPONENT_INVENTORY.md
|
|
docs/architecture/005-DEPENDENCY_GRAPH.md
|
|
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md
|
|
```
|
|
|
|
Les anciens plans historiques ne sont pas réécrits. README, USAGE, CHANGELOG et ROADMAP restent fermés jusqu'aux tranches prévues de réconciliation/fermeture.
|
|
|
|
## Fichiers ajoutés
|
|
|
|
```text
|
|
crates/ksp-job-backfill-lib/Cargo.toml
|
|
crates/ksp-job-backfill-lib/src/constants.rs
|
|
crates/ksp-job-backfill-lib/src/discovery.rs
|
|
crates/ksp-job-backfill-lib/src/error.rs
|
|
crates/ksp-job-backfill-lib/src/lib.rs
|
|
crates/ksp-job-backfill-lib/src/request.rs
|
|
crates/ksp-job-backfill-lib/tests/dependency_boundary.rs
|
|
crates/ksp-job-backfill-lib/tests/public_api.rs
|
|
crates/ksp-job-backfill-lib/tests/release_completeness.rs
|
|
crates/ksp-job-backfill-lib/unit_tests/discovery.rs
|
|
crates/ksp-job-backfill-lib/unit_tests/request.rs
|
|
deltas/0.3.6/pre.005.md
|
|
```
|
|
|
|
## Fichiers modifiés
|
|
|
|
```text
|
|
Cargo.toml
|
|
docs/architecture/004-COMPONENT_INVENTORY.md
|
|
docs/architecture/005-DEPENDENCY_GRAPH.md
|
|
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md
|
|
docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md
|
|
docs/validation/023-V0_3_6_JOB_API_BACKFILL.md
|
|
```
|
|
|
|
Mécanique Cargo :
|
|
|
|
```text
|
|
header version: 399 -> 400
|
|
workspace.package.version: 0.3.6-pre.4 -> 0.3.6-pre.5
|
|
workspace members: + crates/ksp-job-backfill-lib
|
|
workspace dependencies versionnées: inchangées
|
|
```
|
|
|
|
## Fichiers supprimés
|
|
|
|
Aucun.
|
|
|
|
## Frontières conservées
|
|
|
|
- aucun code kbot3 n'est copié ; kbot3 reste uniquement une référence fonctionnelle ;
|
|
- aucune hydratation `getTransaction` n'est encore implémentée dans Job ;
|
|
- aucune conversion RAW v1 n'est ouverte ;
|
|
- aucune écriture Store, observation d'acquisition ou politique `ForceRehydrate` n'est ouverte ;
|
|
- aucun checkpoint, frontier contiguë, runtime Job ou notification concrète n'est ouvert ;
|
|
- aucune lecture Config/env n'est introduite ;
|
|
- aucun endpoint/provider/protocole ne devient une clé transactionnelle ;
|
|
- aucun backend Store n'est imposé ;
|
|
- aucun README/USAGE/CHANGELOG/ROADMAP n'est rouvert.
|
|
|
|
## 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.6
|
|
-> Markdown table audit: clean (264 tables / 147 fichiers, présent delta inclus)
|
|
|
|
python3 -m unittest scripts/tests/test_audit_markdown_tables.py
|
|
-> 5 tests / OK
|
|
```
|
|
|
|
Le premier audit Markdown après réconciliation de l'inventaire a détecté uniquement l'élargissement mécanique requis par `ksp-job-backfill-lib`; la table a été réalignée sur le contenu le plus large avant le second passage propre.
|
|
|
|
L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`. Les 11 unitaires et 6 canaries Rust sont matérialisés mais ne sont pas annoncés comme exécutés localement.
|
|
|
|
## Gate opérateur demandé
|
|
|
|
```bash
|
|
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.6
|
|
cargo check --workspace
|
|
cargo clippy --workspace --all-targets
|
|
cargo test -p ksp-job-backfill-lib
|
|
cargo tree -p ksp-job-backfill-lib --edges normal
|
|
cargo tree -p ksp-job-backfill-lib -e features
|
|
```
|
|
|
|
Le gate doit notamment confirmer :
|
|
|
|
- 11 unitaires + 6 canaries ;
|
|
- `ksp-store-lib` sans activation implicite du backend PostgreSQL ;
|
|
- aucun accès direct à Store API/backend, Config ou dépendance protocolaire ;
|
|
- pagination Latest/Before/After et explicite déterministe ;
|
|
- résultat partiel conservateur aux bornes ;
|
|
- identité candidate scellée par réseau + signature, indépendamment du rôle/provider/endpoint/protocole.
|
|
|
|
## Questions ouvertes
|
|
|
|
Aucune question ne bloque `pre.006` après un gate opérateur vert de `pre.005`.
|