# Delta `0.2.4-pre.006` — `getBlock` complet ## Base requise Livraison précédente : ```text 0.2.4-pre.005 workspace.package.version = "0.2.4-pre.5" ``` Les validations locales fournies pour cette base sont propres : ```text cargo fmt --all -> terminé cargo check --workspace -> terminé sans warning cargo clippy --workspace --all-targets -> terminé sans warning cargo test -p ksp-onchain-transport-lib -> 212 unit tests OK 23 public API tests OK 18 release-completeness tests OK 1 smoke Devnet ignoré comme prévu 0 échec ``` ## Objectif Achever la famille Blocks de `0.2.4` avec le wrapper le plus riche de la release : ```text getBlock ``` La méthode est déjà enregistrée : ```text V0_2_4 / Blocks / Read / RetrySafe / StableWithDeprecatedLegacy ``` Le registre central n'est pas modifié. Après cette tranche, les dix wrappers Blocks de `0.2.4` sont matérialisés. Les cinq wrappers Economics restent volontairement pour `pre.007` et `pre.008`. ## Réaudit primaire `getBlock` — Agave `v4.2.1` La source runtime stable confirme que `getBlock` reçoit : ```text slot Option> ``` La forme wrapper conserve donc deux familles de requêtes : ```text moderne : [slot, { ...RpcBlockConfig }] legacy : [slot, "encoding"] ``` La forme legacy bare encoding reste supportée par le runtime pour backwards compatibility, mais n'est pas la forme KSP recommandée. `RpcBlockConfig` expose exactement dans la baseline stable : ```text encoding: Option transactionDetails: Option rewards: Option commitment: Option maxSupportedTransactionVersion: Option ``` Aucun champ `footer` n'existe dans `RpcBlockConfig` stable `v4.2.1`. Défauts appliqués côté runtime lorsqu'ils ne sont pas fournis : ```text encoding = json transactionDetails = full rewards = true ``` Le commitment explicitement fourni doit être au moins `confirmed`; KSP rejette donc `processed` avant I/O avec le domaine d'erreur partagé. ## Réaudit SIMD footer / transaction version ### SIMD-0307 — Add Block Footer Le document est toujours en `Review` et propose : ```text config getBlock : footer réponse : footer fields, dont blockProducerTimeNanos / blockUserAgent ``` Ces champs sont absents de la baseline Agave `v4.2.1`; ils ne sont pas inventés dans KSP. ### SIMD-0298 — Add `bank_hash` to block footer Le document SIMD reste au statut `Idea`. Il propose un futur `bank_hash` dans le block footer. Ce champ n'existe pas dans le wire RPC stable audité et n'est pas ajouté spéculativement. Le tracker amont lié à Bankless Leader/Alpenglow rend néanmoins ce sujet important pour le réaudit final : l'existence d'une implémentation future du mécanisme ne transforme pas sa forme RPC en contrat stable `v4.2.1`. ### SIMD-0301 — `parent_bank_hash` Le PR SIMD-0301 proposait de remplacer `bank_hash` par `parent_bank_hash` et de supersede SIMD-0298. Il a été fermé le 28 janvier 2026 sans merge. Il ne constitue donc ni un SIMD adopté ni un wire stable. Le plan vivant ajoute `0301` à la watchlist de `pre.009` avec `0298` et `0307`, afin de ne pas figer prématurément le nom ou même la présence d'un futur hash de footer. ### SIMD-0385 — Transaction V1 SIMD-0385 reste en `Review`, tandis que la baseline Agave `v4.2.1` possède déjà le plumbing transaction-status nécessaire aux messages V1. KSP conserve une représentation générique : ```text SolanaTransactionVersion::Legacy SolanaTransactionVersion::Number(u8) ``` Le wrapper `getBlock` ajoute une canary avec : ```text version = 1 ``` Il ne régresse donc pas vers un modèle limité à `legacy` ou `v0`. ## Version Cargo Nouvelle prerelease technique : ```text 0.2.4-pre.5 -> 0.2.4-pre.6 ``` Aucune dépendance ni feature Cargo n'est ajoutée ou modifiée. ## Forme moderne Signature publique : ```text HttpTransportPool::get_block( role, slot, Option<&SolanaGetBlockConfig>, ) -> Result> ``` Les trois états de la config restent distingués lorsque leur forme wire est utile : ```text None -> [slot] Some(default config) -> [slot, {}] Some(config) -> [slot, {...}] ``` La config moderne acquise en `pre.002` est réutilisée, sans DTO parallèle. ## Forme legacy retained La surface publique dédiée est : ```text #[deprecated] HttpTransportPool::get_block_legacy(role, slot, encoding) ``` Elle émet le warning KSP centralisé et encode exactement : ```text [slot, "binary"] [slot, "base58"] [slot, "base64"] [slot, "json"] [slot, "jsonParsed"] ``` Les cinq labels de `UiTransactionEncoding` stable sont couverts, y compris `binary`, lui-même legacy mais encore retenu par Agave pour compatibilité. ## `transactionDetails` complet Les quatre variantes stables sont couvertes : ```text full signatures none accounts ``` Leurs conséquences wire Agave sont conservées sans synthèse : ```text full -> transactions présent, signatures omis signatures -> transactions omis, signatures présent none -> transactions omis, signatures omis accounts -> transactions présent sous représentation account-list, signatures omis ``` La représentation `accounts` reste un payload transaction JSON lossless; elle n'est pas forcée dans la structure plus riche d'une transaction `full`. ## Encodings et union transaction Les cinq encodings modernes et legacy sont testés. Le DTO partagé `SolanaEncodedTransaction` couvre les trois formes de réponse effectivement nécessaires : ```text LegacyBinary(String) <- encoding "binary" Binary { data, Base58 ou Base64 } <- encodings "base58" / "base64" Json(serde_json::Value) <- encodings "json" / "jsonParsed" et transactionDetails=accounts ``` Aucun décodage binaire, Program-specific ou message-specific n'est introduit dans Transport. ## Résultat bloc riche `getBlock` retourne : ```text Option ``` Le `null` top-level du RPC est conservé comme `None`. Pour un bloc présent, KSP conserve : ```text previousBlockhash blockhash parentSlot transactions : SolanaWireField> signatures : SolanaWireField> rewards : SolanaWireField> numRewardPartitions : SolanaWireField blockTime : Option blockHeight : Option ``` Les champs dont la présence dépend du mode de réponse gardent donc leurs états wire `omitted` / `null` / `value` au lieu d'être normalisés. ## Transactions de bloc Un élément `transactions[]` possède son propre DTO, distinct de `SolanaConfirmedTransaction` : ```text transaction meta version ``` Le DTO de `getTransaction` n'est pas réutilisé car son `slot` et son `blockTime` sont top-level et n'appartiennent pas à chaque transaction d'un bloc. `meta` reste : ```text SolanaWireField ``` Cela préserve sans perte les champs transaction-status actuels et leurs extensions, notamment les rewards imbriqués, sans transférer de logique métier dans Transport. ## Rewards et SIMD-0291 Le reward de bloc conserve : ```text pubkey: Pubkey lamports: i64 postBalance: u64 rewardType: Option commission: Option commissionBps: SolanaWireField ``` `commission` et `commissionBps` restent indépendants : KSP ne dérive jamais l'un depuis l'autre. Les fixtures couvrent : ```text commission = null + commissionBps présent commission présent + commissionBps omis commissionBps imbriqué dans transaction.meta ``` ## Reward partitions et SIMD-0118 `numRewardPartitions` reste un champ wire propre : ```text SolanaWireField ``` Les fixtures couvrent `value`, omission et `null` de compatibilité. Aucun `0` n'est synthétisé. Point important confirmé dans l'encodeur Agave : `numRewardPartitions` est renseigné depuis le bloc indépendamment de `show_rewards`. Avec : ```text rewards: false ``` KSP accepte donc correctement : ```text rewards omis numRewardPartitions présent ``` sans lier artificiellement les deux champs. ## Rewards désactivés dans `meta` Le runtime Agave n'utilise pas exactement la même sérialisation interne selon les chemins `full/jsonParsed`, `accounts`, etc. pour masquer les rewards de metadata. Transport ne normalise pas ces sous-arbres. La fixture `full` avec `rewards:false` conserve explicitement un `meta.rewards = null`, tandis que les formes qui omettent ce champ restent également préservées grâce au `serde_json::Value` lossless. ## Validation déterministe avant I/O La seule validation locale ajoutée à `getBlock` est : ```text commitment = processed -> ERROR_CODE_INVALID_RPC_PARAMETERS avant I/O commitment = confirmed -> autorisé commitment = finalized -> autorisé ``` KSP ne tente pas de prévalider : ```text disponibilité du bloc slot cleaned/skipped historique local ou long-term storage support réel d'une version de transaction par le caller/runtime ``` Ces états appartiennent au runtime RPC. ## Erreurs RPC runtime préservées Deux erreurs Agave caractéristiques sont couvertes par fixtures : ```text -32004 BlockNotAvailable -32015 UnsupportedTransactionVersion ``` Toutes deux restent des `ERROR_CODE_RPC_APPLICATION_ERROR` dans la façade KSP; elles ne sont ni transformées en erreurs de validation locale ni avalées en `None`. Le `null` success result reste distinct d'une erreur RPC. ## Discipline `#[cfg(test)]` La règle introduite par `pre.002-fix.001` reste appliquée. Deviennent runtime dans cette tranche parce que `getBlock` les consomme réellement : ```text SolanaGetBlockConfig::to_json_value SolanaBlockReward::decode_wire SolanaBlockTransaction::decode_wire SolanaConfirmedBlock::decode_wire decode_block_transaction_version decode_block_transactions decode_block_rewards WireBlockReward WireBlockTransaction WireConfirmedBlock ``` `SolanaGetBlockConfig::is_empty` reste test-only : le wrapper doit justement préserver un `Some(default config)` explicite comme `{}` et n'a aucun besoin runtime de le canoniser. Les helpers Economics restent test-only jusqu'à `pre.007`/`pre.008`. Aucun `#[allow(dead_code)]` n'est introduit. ## Fixtures HTTP ajoutées ```text get_block.full.success.json get_block.full_no_rewards.success.json get_block.signatures.success.json get_block.none.success.json get_block.accounts.success.json get_block.binary.success.json get_block.base58.success.json get_block.base64.success.json get_block.json.success.json get_block.json_parsed.success.json get_block.null.json get_block.omitted_fields.success.json get_block.null_fields.success.json get_block.error_block_not_available.json get_block.error_unsupported_version.json ``` Elles couvrent notamment : - rich full wire ; - cinq encodings ; - quatre `transactionDetails` ; - legacy bare encoding ; - result `null` ; - états omitted/null/value des champs top-level conditionnels ; - `rewards:false` sans disparition de `numRewardPartitions` ; - SIMD-0118 ; - SIMD-0291 ; - transaction version numérique `1` pour la compatibilité préparée SIMD-0385 ; - erreurs RPC bloc/version. ## Tests ajoutés Neuf unit tests HTTP sont ajoutés : ```text typed_get_block_modern_full_config_preserves_rich_wire_and_simd_fields typed_get_block_preserves_absent_and_explicit_empty_modern_config typed_get_block_legacy_covers_all_bare_encoding_labels_and_response_shapes typed_get_block_modern_covers_all_agave_v4_2_1_encoding_labels typed_get_block_covers_full_signatures_none_and_accounts_transaction_details typed_get_block_preserves_null_and_omitted_top_level_wire_states typed_get_block_preserves_unsupported_version_rpc_error typed_get_block_preserves_block_not_available_rpc_error typed_get_block_rejects_processed_commitment_before_io ``` Une canary public API est ajoutée pour les deux formes publiques moderne/legacy et les getters de config. Une canary release-completeness vérifie désormais que la famille Blocks `V0_2_4` possède exactement ses dix descriptors `Read / RetrySafe` et que `getBlock` conserve son marqueur `StableWithDeprecatedLegacy`. Compteurs attendus après validation : ```text 221 unit tests 24 public API tests 19 release-completeness tests 1 smoke Devnet ignoré ``` ## Documentation vivante `docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md` passe en version 4. Modifications : - réaudit `pre.006` de SIMD-0298/0307 ; - ajout de SIMD-0301 à la watchlist finale après constat de fermeture sans merge ; - ajout des sources Agave transaction-status/custom-error effectivement utilisées pour vérifier le wire riche et les erreurs ; - `pre.009` réauditera ensemble au minimum `0298/0301/0307` pour le futur footer. Les deux tableaux du plan conservent leur structure source alignée : ```text 19 lignes au total 4 colonnes 5 caractères `|` par ligne largeurs 31 / 152 / 163 / 302 ``` Aucun ancien delta n'est modifié. ## Frontières architecturales Cette tranche ne change aucune frontière : ```text Transport -X-> Config Transport -X-> Store Transport -X-> Program Transport -X-> tracing direct ``` Le wrapper passe uniquement par : ```text getBlock typed -> descriptor central -> execute_standard_rpc -> admission/pool/executor HTTP commun -> parser JSON-RPC central -> decode typed/lossless ``` Aucun client RPC Solana haut niveau, reqwest parallèle, retry local ou décodage Program n'est ajouté. ## Validation attendue Après application : ```bash cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-onchain-transport-lib ``` Attendus : ```text 0 warning introduit 221 unit tests OK 24 public API tests OK 19 release-completeness tests OK 1 smoke Devnet ignoré comme prévu 0 échec ``` Le sandbox d'échange ne possède pas de toolchain Rust ; ces validations compilées doivent donc être confirmées localement. ## Fichiers touchés Modifiés : ```text Cargo.toml crates/ksp-onchain-transport-lib/src/rpc_blocks.rs crates/ksp-onchain-transport-lib/unit_tests/rpc_blocks.rs crates/ksp-onchain-transport-lib/tests/public_api.rs crates/ksp-onchain-transport-lib/tests/release_completeness.rs docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md ``` Ajoutés : ```text 15 fixtures get_block*.json deltas/0.2.4/pre.006.md ``` ## Identifiant de commit attendu Après validation locale : ```text v0.2.4-pre.006 ``` La tranche suivante reste : ```text 0.2.4-pre.007 — réaudit SIMD-0490/0550 + Economics simples ```