469 lines
22 KiB
Markdown
469 lines
22 KiB
Markdown
<!-- file: docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md -->
|
||
<!-- version: 27 -->
|
||
|
||
# Validation `0.2.9` — Yellowstone gRPC standard + PublicNode
|
||
|
||
**Statut courant : la validation technique `pre.013-fix.004` est fermée et verte : `clippy --all-targets`, workspace, graphes Cargo, puis smoke PublicNode authentifié Mainnet + Testnet `2/2 PASS`. `pre.014` réconcilie et ferme cette matrice documentaire ; seule la tranche `pre.015` de préparation de publication restera avant `rel.001`.**
|
||
|
||
## 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 | graphes `pre.013` inspectés | **PASS** |
|
||
|
||
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
|
||
secret_metadata = x-token <- ${KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN}
|
||
```
|
||
|
||
Le mapping produit un endpoint TLS Yellowstone standard et une metadata secrète redacted. Le smoke final ouvre `Subscribe`, reçoit un update `Slot` non nul et ferme de manière bornée : **PASS**.
|
||
|
||
### 8.5 PublicNode Testnet
|
||
|
||
Profil committé et validé :
|
||
|
||
```text
|
||
profile_id = publicnode_testnet
|
||
provider = publicnode
|
||
cluster = testnet
|
||
protocol = solana_yellowstone
|
||
url = https://solana-testnet-yellowstone-grpc.publicnode.com:443
|
||
secret_metadata = x-token <- ${KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN}
|
||
```
|
||
|
||
Le hostname exact a été fourni par l'opérateur puis validé par le smoke live. `Subscribe` reçoit un update `Slot` non nul : **PASS**.
|
||
|
||
Le même personal token opérateur a été validé sur les deux réseaux. Les deux variables KSP restent distinctes afin de permettre des valeurs différentes si la policy provider change ; cette modélisation ne constitue pas une preuve que les tokens PublicNode sont network-scoped.
|
||
|
||
## 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
|
||
```
|
||
|
||
La baseline `pre.011` reste conservée. Les changements `pre.013` ont porté uniquement le gate live, la Config PublicNode/Testnet et le harness de smoke ; le gate final `pre.013-fix.004` confirme les non-régressions workspace.
|
||
|
||
## 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é |
|
||
| `pre.012` + `fix.001` | réaudit + règles de fermeture |
|
||
| `pre.013` + `fix.001`–`fix.004` | live PublicNode + graphes fermé |
|
||
| `pre.014` | réconciliation documentaire candidate |
|
||
|
||
Cette table remplace les anciens appendices numérotés successivement `19.x`, `20`, `21`, etc. qui rendaient le document ambigu.
|
||
|
||
## 12. Verdict opérateur consolidé jusqu'à `pre.013-fix.004`
|
||
|
||
Le gate technique final reçu le 2026-08-24 est :
|
||
|
||
| Exigence | Résultat opérateur |
|
||
|-----------------------------------------------|--------------------------------------|
|
||
| `cargo fmt --all` | **PASS** |
|
||
| audit Rust workspace | **PASS, 0 candidate export** |
|
||
| audit Markdown | **PASS, 87 tableaux / 258 fichiers** |
|
||
| `cargo check --workspace` | **PASS** |
|
||
| `cargo clippy --workspace --all-targets` | **PASS sans warning** |
|
||
| Transport unit | **383/383 PASS** |
|
||
| Transport public API | **49/49 PASS** |
|
||
| Transport release completeness | **43/43 PASS** |
|
||
| Transport doctests | **4/4 PASS** |
|
||
| Config unit dans workspace | **113/113 PASS** |
|
||
| Config ownership dans workspace | **5/5 PASS** |
|
||
| Config public API dans workspace | **15/15 PASS** |
|
||
| workspace dependencies | **3/3 PASS** |
|
||
| `cargo test --workspace` | **PASS** |
|
||
| graphes Cargo finaux | **PASS / inspectés** |
|
||
| smoke PublicNode Mainnet `Subscribe` + `Slot` | **PASS** |
|
||
| smoke PublicNode Testnet `Subscribe` + `Slot` | **PASS** |
|
||
| smoke live total | **2/2 PASS** |
|
||
|
||
Les graphes résolus confirment la stack attendue :
|
||
|
||
```text
|
||
yellowstone-grpc-proto 12.6.0
|
||
tonic 0.14.6
|
||
tonic-prost 0.14.6
|
||
prost 0.14.4
|
||
prost-types 0.14.4
|
||
yellowstone-grpc-client absent du runtime KSP
|
||
```
|
||
|
||
Les graphes ont été exécutés avant `pre.013-fix.004`; ils restent valides car `fix.004` n'a modifié ni dépendance ni feature Cargo.
|
||
|
||
## 13. Verdict PublicNode live final
|
||
|
||
Les essais intermédiaires ont établi deux faits utiles : les appels sans credential peuvent atteindre le service mais recevoir `PERMISSION_DENIED`, et la validation représentative de la foundation doit porter sur `Subscribe` plutôt que sur une unary provider éventuellement restreinte.
|
||
|
||
Le smoke final :
|
||
|
||
```text
|
||
reçoit deux personal tokens par stdin, Mainnet puis Testnet
|
||
accepte que les deux lignes contiennent la même valeur
|
||
construit une metadata secrète x-token
|
||
ouvre TLS puis Subscribe
|
||
filtre slots
|
||
attend un YellowstoneSubscribeUpdate::Slot sous timeout
|
||
exige slot > 0
|
||
ferme de manière bornée
|
||
n'expose aucun secret dans URL/Debug/arguments
|
||
```
|
||
|
||
Résultat opérateur final : **Mainnet PASS, Testnet PASS, 2/2**.
|
||
|
||
Le timeout KSP de half-close est un résultat accepté par le harness uniquement après réception prouvée d'un slot ; le runtime conserve son contrat de timeout explicite.
|
||
|
||
## 14. Gate `pre.014` — réconciliation documentaire
|
||
|
||
Cette tranche ne modifie ni runtime, ni Config, ni schema, ni smoke. Elle synchronise le plan, la présente validation, README/USAGE Transport et les références durables avec les faits exécutés en `pre.013`.
|
||
|
||
Le verdict technique de `0.2.9` est donc **PASS**. Après le gate documentaire `pre.014`, seuls le prompt suivant, `CHANGELOG.md` et `ROADMAP.md` restent à finaliser en `pre.015`.
|
||
|
||
## 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 Subscribe + Slot PASS
|
||
PublicNode Testnet live Subscribe + Slot PASS
|
||
x-token secret requis par les profils PublicNode, aucun secret versionné
|
||
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
|
||
```
|