v0.3.12-pre.006

This commit is contained in:
2026-09-09 13:30:15 +02:00
parent 50c01a19c9
commit 901a0515b7
10 changed files with 1011 additions and 115 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md -->
<!-- version: 10 -->
<!-- version: 11 -->
# Validation v0.3.12 — Yellowstone + hydration HTTP + continuité de run du Worker RawTransaction
@@ -1128,3 +1128,181 @@ cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-worker-raw-transaction-ingest-lib
```
## 47. Gate opérateur `pre.005-fix.001`
Le gate opérateur communiqué pour `0.3.12-pre.5.fix.1` est entièrement vert :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (340 tables, 809 fichiers)
cargo check --workspace: PASS
cargo clippy --workspace --all-targets --all-features -- -D warnings: PASS
cargo test -p ksp-worker-raw-transaction-ingest-lib: PASS
56 unit tests Worker: PASS
6 dependency-boundary tests: PASS
12 hardening tests: PASS
8 public-api tests: PASS
3 release-completeness tests: PASS
Doc-tests: PASS
```
Cette base ferme donc `pre.005` et son fix de conversion `YellowstoneTransactionSignature::as_bytes()` avant ouverture de la source productive.
## 48. `pre.006` — source Yellowstone productive
`start_with_runtime_resources` ne délègue plus au foundation sans source. Après les validations synchrones Store/settings/runtime-resources déjà qualifiées, il :
```text
consomme RawTransactionIngestRuntimeResources
extrait exactement une RawTransactionIngestYellowstoneSource
clone les settings nécessaires au child privé
spawn exactement un child dans le SourceTasks JoinSet existant
laisse le supervisor 0.3.11 posséder stop/drain/fault
```
Le child ouvre la session uniquement via :
```text
YellowstoneGrpcChannel::open_standard_subscribe
SolanaYellowstoneGrpcSubscribeSession::next_update
SolanaYellowstoneGrpcSubscribeSession::close
```
Le Worker ne crée aucun `tonic::Channel`, client Geyser, client Reqwest ou actor gRPC parallèle. Transport reste propriétaire des metadata, credentials, ping/pong, message-size bounds et reconnect interne.
Le stop privé est prioritaire pendant l'ouverture, pendant la lecture de stream et pendant l'envoi vers l'admission. Un stop avant ouverture abandonne le futur d'ouverture sans session orpheline. À la sortie, les hydrations privées sont abort/join puis la session Transport est fermée via son close borné.
## 49. Routage productif des updates
Le dispatch productif est désormais :
```text
Transaction -> 1 RawTransactionIngestSourceSignal -> hydration
TransactionStatus -> 1 RawTransactionIngestSourceSignal -> hydration
Block -> N RawTransactionIngestSourceSignal dans l'ordre source -> hydration
BlockMeta -> RawTransactionIngestContinuitySignal continuity-only
Slot -> RawTransactionIngestContinuitySignal continuity-only
Account/Entry/Ping/Pong -> aucun RAW Worker
```
`BlockMeta` et `Slot` restent délibérément sans admission RAW. `dead_error` et `TransactionStatus.error` ne sont pas lus. Aucun `getBlock`/`get_block_observed` n'est ajouté.
Les adapters `pre.003-pre.005`, précédemment gardés sous `#[cfg(test)]` faute de consumer productif, sont maintenant compilés dans le build normal. Le wrapper fixture `hydrate_yellowstone_signal` reste test-only ; la production utilise les deux étapes séparées `fetch_yellowstone_hydration` puis `finalize_yellowstone_hydration`.
## 50. Coalescence bornée d'hydration
La clé privée est exactement :
```text
network
signature
commitment
```
Elle ne contient ni family ni filter. Ainsi `Transaction`, `TransactionStatus` et `Block` portant la même signature/network/commitment partagent au plus un `getTransaction` in-flight.
Le coordinator privé conserve :
```text
BTreeMap<HydrationKey, PendingHydration>
JoinSet<HydrationFetch>
max_in_flight = persistence_concurrency validée
max_pending_signals = MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY
```
Le nombre de tâches HTTP simultanées est donc borné par le réglage existant de concurrence, sans nouveau knob public. Le nombre de signaux coalescés/pending reste borné par la borne maximale déjà publiée du Worker. Quand cette borne est atteinte, la lecture Yellowstone est backpressurée ; un dépassement à l'intérieur d'une update composite produit un fault source sûr au lieu d'un drop silencieux.
À la complétion d'un unique fetch HTTP, chaque signal coalescé est finalisé séparément. Le fan-out reconstruit donc sa propre provenance :
```text
transaction_get_transaction
status_get_transaction
block_get_transaction
filter_id direct ou fingerprint propre au signal
observed_at propre au signal quand représentable
```
Store idempotence reste ensuite la seconde barrière. Aucun cache de déduplication terminé/non borné n'est conservé.
## 51. Hydration productive et admission
Le fetch utilise toujours le contrat `pre.004` :
```text
get_transaction_observed
encoding = base64
commitment = confirmed | finalized de la source
maxSupportedTransactionVersion = 0
```
Le résultat observé est partagé uniquement entre les signaux ayant la même clé. Chaque finalisation réapplique avant admission :
```text
network cohérent
route Yellowstone cohérente
slot HTTP == slot signal
transaction_index cohérent lorsqu'il est présent des deux côtés
signature embarquée == signature signal
provenance composite sûre depuis le winner HTTP réel
```
`null` reste `Missing` : aucun ingress n'est créé. Une erreur Transport est réduite à son `ErrorCode` KSP sûr. Aucun texte distant n'entre dans l'erreur Worker.
Les ingresses disponibles sont envoyés uniquement via le `tokio::sync::mpsc::Sender<RawTransactionIngress>` déjà créé par `RawTransactionAdmission::new`. Aucun second canal d'admission ou chemin Store n'est introduit.
## 52. Canaris `pre.006`
Les nouveaux canaris vérifient au minimum :
```text
start_with_runtime_resources -> source.run -> children.spawn
open_standard_subscribe/next_update/close présents uniquement via la façade Transport
get_transaction_observed reste l'unique hydration HTTP
aucun get_block_observed
aucun Config/Backfill/backend/reqwest/tonic/proto direct
coalescence key = network/signature/commitment
Transaction + Status de même signature -> une seule entrée pending et une seule tâche HTTP in-flight
signature différente -> clé différente
pending et in-flight bornés
stop preemptible pendant admission
abort_all + join des hydrations privées
aucun remote error/dead_error/secret dans la source productive
public root inchangé
module inventory inchangé
```
## 53. Non-claims `pre.006`
`pre.006` ne prétend pas encore :
```text
maintenir une processing frontier
interpréter le snapshot reconnect Transport
modifier from_slot au niveau Worker
interpréter SubscribeReplayInfo
prouver une continuité blockchain
fault sur gap de rétention prouvé
faire un repair/backfill implicite
qualifier les races duplicate storm/Store lent finales de pre.009
réussir un smoke provider live sans credentials opérateur
```
Ces responsabilités restent respectivement `pre.007`, `pre.008`, `pre.009` et `pre.011` conformément au plan.
## 54. Gate opérateur requis pour `pre.006`
```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
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-worker-raw-transaction-ingest-lib
cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal
cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features
cargo tree --duplicates
```
Dans l'environnement d'assemblage, `cargo`, `rustc` et `rustfmt` ne sont pas installés ; aucun gate Cargo local n'est donc revendiqué.