476 lines
22 KiB
Markdown
476 lines
22 KiB
Markdown
<!-- file: docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md -->
|
|
<!-- version: 26 -->
|
|
|
|
# Validation `0.2.9` — Yellowstone gRPC standard + PublicNode
|
|
|
|
**Statut courant : le gate déterministe de `pre.012` est vert sans warning ; le smoke PublicNode live et les graphes Cargo finaux ne figurent pas comme exécutés dans le log opérateur fourni. `pre.012-fix.001` ajoute la règle durable de séparation de fermeture et déplace les gates restants vers `pre.013` technique/live, `pre.014` réconciliation documentaire et `pre.015` publication minimale.**
|
|
|
|
## 1. Autorités et baseline courante
|
|
|
|
Autorités :
|
|
|
|
```text
|
|
RULES.md + docs/rules/*
|
|
plan 016 courant
|
|
code réel Transport/Config
|
|
prompts/014-V0_2_9_START_PROMPT.md pour les contraintes non supersédées
|
|
deltas/0.2.9/* pour l'historique immuable et les décisions postérieures au prompt
|
|
upstream Yellowstone primaire
|
|
```
|
|
|
|
Baseline opérateur immédiatement avant `pre.012` (`0.2.9-pre.011`) :
|
|
|
|
| Gate | Résultat |
|
|
|------------------------------------------|--------------------------|
|
|
| `cargo fmt --all` | PASS |
|
|
| audit Rust workspace | PASS, 0 candidate export |
|
|
| `cargo check --workspace` | PASS |
|
|
| `cargo clippy --workspace --all-targets` | PASS sans warning |
|
|
| Config unit | 113/113 PASS |
|
|
| Config public API | 15/15 PASS |
|
|
| Config ownership | 5/5 PASS |
|
|
| Transport unit | 383/383 PASS |
|
|
| Transport public API | 49/49 PASS |
|
|
| Transport release completeness | 43/43 PASS |
|
|
| Transport doctests | 4/4 PASS |
|
|
| workspace dependency canary | 3/3 PASS |
|
|
| `cargo test --workspace` | PASS |
|
|
|
|
Cette baseline est le seuil de non-régression de `pre.012`.
|
|
|
|
## 2. Gate dépendances et licence
|
|
|
|
| Exigence | Décision / preuve | État |
|
|
|--------------------------------|------------------------------------------|-------------------|
|
|
| proto officiel sans vendoring | `yellowstone-grpc-proto ^12.6` | **PASS** |
|
|
| pas de client upstream runtime | `yellowstone-grpc-client` absent | **PASS** |
|
|
| runtime Tonic KSP-owned | `tonic ^0.14` + `tonic-prost ^0.14` | **PASS** |
|
|
| proto subtree compatible | Apache-2.0 selon `LICENSING.md` upstream | **PASS** |
|
|
| source AGPL copiée | aucune | **PASS** |
|
|
| raw client Tonic public | aucun | **PASS** |
|
|
| graph final | réinspection `pre.012` | **PENDING FINAL** |
|
|
|
|
Le graphe de dépendances n'est pas modifié par `pre.012`; aucune nouvelle dépendance Cargo n'est introduite dans cette tranche.
|
|
|
|
## 3. Matrice service `Geyser`
|
|
|
|
| RPC | Classification | Cible | Preuve | Verdict |
|
|
|-----------------------|--------------------------------|-------|---------------------------------|----------|
|
|
| `Subscribe` | standard | oui | stream bidi local + lifecycle | **PASS** |
|
|
| `SubscribeDeshred` | extension/pré-exécution Triton | non | exclusion documentée | **OUT** |
|
|
| `SubscribeReplayInfo` | standard unary | oui | fixture unary + reconnect tests | **PASS** |
|
|
| `Ping` | standard unary | oui | fixture exact echo | **PASS** |
|
|
| `GetLatestBlockhash` | standard unary | oui | fixture typed | **PASS** |
|
|
| `GetBlockHeight` | standard unary | oui | fixture typed | **PASS** |
|
|
| `GetSlot` | standard unary | oui | fixture typed | **PASS** |
|
|
| `IsBlockhashValid` | standard unary | oui | fixture typed + invalid input | **PASS** |
|
|
| `GetVersion` | standard unary | oui | fixture typed | **PASS** |
|
|
|
|
## 4. `SubscribeRequest` — coverage normative
|
|
|
|
### 4.1 Top-level
|
|
|
|
| Champ | Verdict | Preuve minimale |
|
|
|-----------------------|----------|----------------------------------|
|
|
| `accounts` | **PASS** | maps typed + wire exact |
|
|
| `slots` | **PASS** | maps typed + wire exact |
|
|
| `transactions` | **PASS** | maps typed + wire exact |
|
|
| `transactions_status` | **PASS** | same filter family, separate map |
|
|
| `blocks` | **PASS** | maps typed + wire exact |
|
|
| `blocks_meta` | **PASS** | named empty marker preserved |
|
|
| `entry` | **PASS** | named empty marker preserved |
|
|
| `commitment` | **PASS** | optional wire semantics |
|
|
| `accounts_data_slice` | **PASS** | order + bounds |
|
|
| `ping` | **PASS** | optional id |
|
|
| `from_slot` | **PASS** | optional + replay mutation |
|
|
|
|
### 4.2 Accounts
|
|
|
|
```text
|
|
account[]
|
|
owner[]
|
|
filters[]
|
|
nonempty_txn_signature?
|
|
cuckoo_accounts_filter?
|
|
memcmp bytes/base58/base64
|
|
datasize
|
|
token_account_state
|
|
lamports eq/ne/lt/gt
|
|
```
|
|
|
|
Verdict : **PASS** — wire complet retenu, bounds déterministes, payload Debug redacted, malformed fixed-width rejeté.
|
|
|
|
### 4.3 Slots
|
|
|
|
```text
|
|
filter_by_commitment?
|
|
interslot_updates?
|
|
processed
|
|
confirmed
|
|
finalized
|
|
first_shred_received
|
|
completed
|
|
created_bank
|
|
dead + dead_error?
|
|
```
|
|
|
|
Verdict : **PASS**.
|
|
|
|
### 4.4 Transactions et transaction_status
|
|
|
|
```text
|
|
vote?
|
|
failed?
|
|
signature?
|
|
account_include[]
|
|
account_exclude[]
|
|
account_required[]
|
|
cuckoo_account_include?
|
|
token_accounts? = ALL | BALANCE_CHANGED
|
|
```
|
|
|
|
Verdict : **PASS** — request wire, transaction storage/meta, status error et malformed signature couverts.
|
|
|
|
### 4.5 Blocks, block_meta, entry
|
|
|
|
```text
|
|
account_include[]
|
|
include_transactions?
|
|
include_accounts?
|
|
include_entries?
|
|
cuckoo_account_include?
|
|
blocks_meta marker
|
|
entry marker
|
|
```
|
|
|
|
Verdict : **PASS** — block complet, metadata, entries et champs optional/legacy couverts.
|
|
|
|
## 5. `SubscribeUpdate` — coverage normative
|
|
|
|
| Variante | Verdict |
|
|
|--------------------------------------|----------|
|
|
| account | **PASS** |
|
|
| slot | **PASS** |
|
|
| transaction | **PASS** |
|
|
| transaction_status | **PASS** |
|
|
| block | **PASS** |
|
|
| ping | **PASS** |
|
|
| pong | **PASS** |
|
|
| block_meta | **PASS** |
|
|
| entry | **PASS** |
|
|
| top-level `filters[]` / `created_at` | **PASS** |
|
|
|
|
Les raw protobufs ne sortent pas de la façade publique. Les décodeurs rejettent les formes structurellement invalides sans copier des payloads arbitraires dans les erreurs.
|
|
|
|
## 6. Settings, bounds et redaction
|
|
|
|
| Contrôle | État |
|
|
|-----------------------------------------------|--------------------|
|
|
| URL `http/https` seulement et longueur bornée | **PASS** |
|
|
| URL Debug redacted | **PASS** |
|
|
| metadata key/value/count bornés | **PASS** |
|
|
| metadata secret/public distincte | **PASS** Transport |
|
|
| timeouts non nuls et bornés | **PASS** |
|
|
| reconnect bounds cohérents | **PASS** |
|
|
| inbound/outbound message sizes | **PASS** |
|
|
| request/update queue capacities | **PASS** |
|
|
| filter group/name bounds | **PASS** |
|
|
| account/owner/memcmp/data slice bounds | **PASS** |
|
|
| transaction/block selectors bounds | **PASS** |
|
|
| remote `Status` message/details non recopiés | **PASS** |
|
|
| Transport -> env/config | absent ou **PASS** |
|
|
|
|
## 7. Lifecycle, backpressure et replay
|
|
|
|
| Exigence | Verdict | Preuve |
|
|
|------------------------------|-----------------|------------------------------------------------|
|
|
| stream bidi unique | **PASS** | round-trip fixture |
|
|
| mutation request | **PASS** | same request channel |
|
|
| server Ping / client reply | **PASS** | actor test |
|
|
| Pong decode | **PASS** | update decode |
|
|
| server half-close | **PASS** | state terminal observable |
|
|
| explicit client close | **PASS** | half-close avant deadline |
|
|
| hostile server shutdown | **PASS** | close timeout borné |
|
|
| slow receiver | **PASS** | overflow terminal observable |
|
|
| oversized inbound | **PASS** | Tonic decoder bound |
|
|
| oversized outbound | **PASS** | reject avant queue dispatch |
|
|
| reconnect backoff | **PASS** | budget borné |
|
|
| shutdown pendant backoff | **PASS** | interruption sans nouvelle connexion |
|
|
| resubscribe déterministe | **PASS** | last accepted full request |
|
|
| reprise `from_slot` | **PASS** | dernier slot observé + demande explicite |
|
|
| ReplayInfo `first_available` | **PASS** | clamp/gap seulement si prouvé |
|
|
| duplicate observability | **PASS** | compteur borné, pas de suppression silencieuse |
|
|
| exactly-once/lossless | **NON GARANTI** | contrat explicite |
|
|
|
|
Snapshot public requis et présent :
|
|
|
|
```text
|
|
reconnect_count
|
|
replay_attempt_count
|
|
continuity_gap_count
|
|
duplicate_update_count
|
|
last_requested_from_slot
|
|
last_observed_slot
|
|
state/error code safe
|
|
```
|
|
|
|
## 8. Gate Config V3 — `pre.011`
|
|
|
|
### 8.1 Schema et backward compatibility
|
|
|
|
Le gate opérateur `pre.011` ferme les exigences suivantes :
|
|
|
|
| Exigence | Preuve | Verdict |
|
|
|------------------------------------------|-----------------------------------------|----------|
|
|
| schema `$id` V3 | `urn:ksp:schema:std.transport:v3` | **PASS** |
|
|
| branches V1/V2 conservées | fixtures V1, V2 et document committé | **PASS** |
|
|
| V1 HTTP-only | `v1_transport_fixture...` | **PASS** |
|
|
| V2 HTTP+WS | fixture V2 + compatibilité constructeur | **PASS** |
|
|
| V3 HTTP+WS+gRPC | 113 tests Config | **PASS** |
|
|
| `grpc_endpoints` optionnel par profil V3 | profil `devnet_public` sans gRPC | **PASS** |
|
|
| propriétés inconnues refusées | branches schema strictes | **PASS** |
|
|
|
|
### 8.2 Mapping runtime
|
|
|
|
V3 doit mapper :
|
|
|
|
```text
|
|
grpc_defaults -> YellowstoneGrpcSessionSettings
|
|
grpc_endpoints[].url -> YellowstoneGrpcEndpointUrl
|
|
provider -> YellowstoneGrpcProviderName
|
|
cluster -> YellowstoneGrpcClusterName
|
|
protocol = solana_yellowstone -> gate Config explicite
|
|
metadata -> YellowstoneGrpcMetadataEntry::public
|
|
secret_metadata -> YellowstoneGrpcMetadataEntry::secret
|
|
session overrides -> merge avec grpc_defaults
|
|
```
|
|
|
|
API :
|
|
|
|
| Surface | Exigence | Statut source |
|
|
|---------------------------------|-------------------------------------|---------------|
|
|
| `http_settings()` | inchangée | **OK** |
|
|
| `ws_settings()` | V1 None, V2/V3 selon profil | **OK** |
|
|
| `grpc_settings()` | V1/V2 None, V3 optionnel | **PASS** |
|
|
| `into_transport_settings()` | tuple historique HTTP + WS inchangé | **PASS** |
|
|
| `into_all_transport_settings()` | nouveau tuple HTTP + WS + gRPC | **PASS** |
|
|
|
|
### 8.3 Provenance et secrets
|
|
|
|
Règles validées :
|
|
|
|
```text
|
|
metadata publique + provenance KSP_SECRET_* -> reject
|
|
secret_metadata sans provenance secret -> reject
|
|
secret_metadata + variable KSP_PUBLIC_/KSP_* -> reject
|
|
secret_metadata + KSP_SECRET_/KSPB_SECRET_ -> accept
|
|
segments littéraux autour du secret -> accept
|
|
safe_value -> secret segment ********
|
|
Transport Debug -> URL/metadata secret absents
|
|
```
|
|
|
|
Les tests V3 démontrent le cas valide et les deux croisements invalides sans exposer les canaris.
|
|
|
|
### 8.4 PublicNode Mainnet
|
|
|
|
Profil committé et validé :
|
|
|
|
```text
|
|
profile_id = publicnode_mainnet
|
|
provider = publicnode
|
|
cluster = mainnet-beta
|
|
protocol = solana_yellowstone
|
|
url = https://solana-yellowstone-grpc.publicnode.com:443 # provider affiche host:port ; KSP ajoute le scheme TLS requis
|
|
metadata = aucune
|
|
```
|
|
|
|
Le mapping produit un endpoint TLS Yellowstone standard ; le test `committed_v3_publicnode_mainnet_maps_provider_neutral_yellowstone_grpc` passe et `Debug` ne doit pas exposer l'URL.
|
|
|
|
### 8.5 PublicNode Testnet
|
|
|
|
Verdict final documentaire : **EXTERNAL BLOCK, NOT GUESSED**.
|
|
|
|
Le réaudit officiel du 2026-08-24 confirme une capacité Solana Testnet gRPC chez PublicNode, mais l'hostname exact n'a pas été obtenu depuis une source PublicNode suffisamment autoritative. Aucun profil, aucun hostname déduit et aucun smoke Testnet ne sont versionnés.
|
|
|
|
```text
|
|
capacité Testnet gRPC confirmée
|
|
hostname exact non confirmé
|
|
profil KSP absent
|
|
smoke KSP absent
|
|
raison interdiction d'inventer un endpoint provider
|
|
```
|
|
|
|
## 9. Provider-neutrality
|
|
|
|
| Point | Verdict |
|
|
|-----------------------------------------------|------------------|
|
|
| type public `PublicNodeGrpc*` sans divergence | absent, **PASS** |
|
|
| protocol standard encodé comme provider | non, **PASS** |
|
|
| provider descriptif séparé | oui, **PASS** |
|
|
| auth PublicNode hardcodée dans Transport | non, **PASS** |
|
|
| Helius/OrbitFlare runtime gRPC dans `0.2.9` | non, **PASS** |
|
|
| provider extensions dans N2 | aucune, **PASS** |
|
|
|
|
## 10. Non-régressions obligatoires
|
|
|
|
Le gate final doit préserver :
|
|
|
|
```text
|
|
HTTP current typed 52/52
|
|
HTTP historical 14/14 Deprecated/Removed
|
|
KSP-TRANSPORT-007 vert
|
|
Standard WebSocket 9 familles / 18 opérations
|
|
Helius LaserStream WebSocket 7 familles standard + transaction + slotsUpdates, heartbeat provider-owned
|
|
Transport -> Config interdit
|
|
Transport -> std::env KSP_* interdit
|
|
tracing direct Transport interdit
|
|
```
|
|
|
|
`pre.012` ne modifie pas le runtime Transport ; elle ajoute uniquement un smoke live ignoré par défaut et de la documentation/audit. Son seuil de non-régression est la baseline `pre.011`.
|
|
|
|
## 11. Historique des gates fermé
|
|
|
|
Les détails de commandes, warnings corrigés et fichiers exacts restent dans leurs deltas immuables.
|
|
|
|
| Tranche | Gate consolidé |
|
|
|-----------------------------------|--------------------------------------|
|
|
| `pre.001` + `fix.001` + `fix.002` | audit/sizing/providers/licence fermé |
|
|
| `pre.002` + `fix.001` + `fix.002` | moteur/settings/channel fermé |
|
|
| `pre.003` + `fix.001` | TLS/metadata/unary fermé |
|
|
| `pre.004` + `fix.001` | Subscribe common fermé |
|
|
| `pre.005` + `fix.001` | Accounts/Slots fermé |
|
|
| `pre.006` | namespace HTTP fermé |
|
|
| `pre.007` | Transactions fermé |
|
|
| `pre.008` + `fix.001` | Blocks fermé |
|
|
| `pre.009` + `fix.001` | bidi/backpressure fermé |
|
|
| `pre.010` + `fix.001` | reconnect/replay fermé sans warning |
|
|
| `pre.011` | Config V3/PublicNode mapping fermé |
|
|
|
|
Cette table remplace les anciens appendices numérotés successivement `19.x`, `20`, `21`, etc. qui rendaient le document ambigu.
|
|
|
|
## 12. Verdict opérateur `pre.012`
|
|
|
|
### 12.1 Gate déterministe reçu
|
|
|
|
| Exigence | Résultat opérateur |
|
|
|----------------------------------------------|--------------------------------------|
|
|
| `cargo fmt --all` | **PASS** |
|
|
| audit Rust workspace | **PASS, 0 candidate export** |
|
|
| audit Markdown | **PASS, 87 tableaux / 252 fichiers** |
|
|
| `cargo check --workspace` | **PASS** |
|
|
| `cargo clippy --workspace --all-targets` | **PASS sans warning** |
|
|
| Config unit | **113/113 PASS** |
|
|
| Config public API | **15/15 PASS** |
|
|
| Config ownership | **5/5 PASS** |
|
|
| Transport unit | **383/383 PASS** |
|
|
| Transport public API | **49/49 PASS** |
|
|
| Transport release completeness | **43/43 PASS** |
|
|
| Transport doctests | **4/4 PASS** |
|
|
| workspace dependencies | **3/3 PASS** |
|
|
| `cargo test --workspace` | **PASS** |
|
|
| smoke Mainnet programmatique sans credential | **NOT RUN, ignored** |
|
|
| graphes Cargo finaux | **NOT PROVIDED / NOT RUN** |
|
|
|
|
Le test `yellowstone_publicnode_smoke` compile et apparaît correctement comme `ignored` dans les tests Transport et workspace, mais cela ne constitue pas une exécution live. Aucune preuve de `cargo tree` final n'est présente dans le log fourni.
|
|
|
|
### 12.2 Effet de `pre.012-fix.001`
|
|
|
|
Le fix ne change ni runtime, ni schema, ni Config, ni smoke. Il corrige le workflow de fermeture afin que les responsabilités restantes ne soient plus mélangées :
|
|
|
|
```text
|
|
pre.013 technique/live : smoke PublicNode + graphes Cargo finaux
|
|
pre.014 réconciliation documentaire : plan/validation/README/USAGE/références
|
|
pre.015 publication minimale : prompt suivant + CHANGELOG + ROADMAP
|
|
rel.001 mécanique stable uniquement
|
|
```
|
|
|
|
Le contenu prompt/README/USAGE déjà préparé en `pre.012` n'est pas supprimé rétroactivement. Le prompt reste un brouillon jusqu'à `pre.015` ; README/USAGE seront réconciliés définitivement en `pre.014`.
|
|
|
|
## 13. Gate `pre.013` — technique/live
|
|
|
|
Commandes minimales :
|
|
|
|
```bash
|
|
cargo fmt --all
|
|
python3 scripts/audit_rust_workspace_rules.py
|
|
cargo check --workspace
|
|
cargo clippy --workspace --all-targets
|
|
cargo tree -p ksp-onchain-transport-lib
|
|
cargo tree -p ksp-onchain-transport-lib --duplicates
|
|
cargo tree --duplicates
|
|
cargo test -p ksp-onchain-transport-lib --test yellowstone_publicnode_smoke -- --ignored --nocapture
|
|
```
|
|
|
|
Verdicts attendus :
|
|
|
|
```text
|
|
smoke Mainnet réellement exécuté
|
|
GetVersion + GetSlot via TLS sans credential ou incident externe précisément classifié
|
|
graphes Cargo inspectés
|
|
aucun changement README/USAGE/prompt/CHANGELOG/ROADMAP
|
|
```
|
|
|
|
Un défaut local découvert dans ce gate reste dans `pre.013-fix.NNN`. Un incident externe n'est jamais maquillé en PASS local.
|
|
|
|
## 14. Gate `pre.014` — réconciliation documentaire
|
|
|
|
Cette tranche ferme les documents durables après le gate technique réel :
|
|
|
|
```text
|
|
plan 016
|
|
validation 012
|
|
README/USAGE Transport
|
|
documents de référence 0.2.9 réellement concernés
|
|
audit Markdown des documents touchés
|
|
```
|
|
|
|
Elle n'introduit aucun nouveau runtime/smoke et ne finalise pas `CHANGELOG.md`, `ROADMAP.md` ni le prompt suivant.
|
|
|
|
Le verdict de la validation 012 devient définitif dans cette tranche, sur la base exclusive des preuves réellement exécutées en `pre.013` et des gates antérieurs.
|
|
|
|
## 15. Gate `pre.015` — préparation de publication
|
|
|
|
Payload fonctionnel autorisé :
|
|
|
|
```text
|
|
prompts/015-V0_2_10_START_PROMPT.md
|
|
CHANGELOG.md
|
|
ROADMAP.md
|
|
```
|
|
|
|
Seuls les fichiers mécaniques de version et de traçabilité (`Cargo.toml`, delta `pre.015`) peuvent s'ajouter à ce payload. Aucun README, USAGE, plan, validation, règle, code, test, schema ou config ne doit être corrigé dans `pre.015`.
|
|
|
|
Si un tel défaut est découvert, une nouvelle prerelease dédiée est ouverte puis la préparation de publication est rejouée sous un nouveau numéro.
|
|
|
|
## 16. Verdict stable attendu avant `rel.001`
|
|
|
|
`0.2.9` peut passer à `rel.001` seulement après la séquence suivante :
|
|
|
|
```text
|
|
pre.013 technique/live fermé
|
|
pre.014 réconciliation documentaire fermée
|
|
pre.015 prompt/CHANGELOG/ROADMAP fermé
|
|
```
|
|
|
|
Et si tous les invariants suivants sont vrais :
|
|
|
|
```text
|
|
standard vs extension explicitement classifié
|
|
SubscribeDeshred OUT documenté
|
|
7 unary verts
|
|
Subscribe + 9 updates verts
|
|
resource/backpressure/lifecycle verts
|
|
reconnect/replay sans promesse lossless
|
|
Config V3 backward V1/V2
|
|
provider/protocol distincts
|
|
PublicNode Mainnet live exécuté avec résultat consigné
|
|
Testnet jamais inventé
|
|
HTTP/WS/Helius non régressés
|
|
dependency firewall vert
|
|
cargo graphs inspectés
|
|
README/USAGE réconciliés avant la dernière pre
|
|
validation 012 fermée avant la dernière pre
|
|
prompt/CHANGELOG/ROADMAP seuls dans la dernière pre
|
|
workspace final vert
|
|
```
|