542 lines
14 KiB
Markdown
542 lines
14 KiB
Markdown
<!-- file: deltas/0.2.4/pre.006.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# 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<RpcEncodingConfigWrapper<RpcBlockConfig>>
|
|
```
|
|
|
|
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<UiTransactionEncoding>
|
|
transactionDetails: Option<TransactionDetails>
|
|
rewards: Option<bool>
|
|
commitment: Option<CommitmentConfig>
|
|
maxSupportedTransactionVersion: Option<u8>
|
|
```
|
|
|
|
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<Option<SolanaConfirmedBlock>>
|
|
```
|
|
|
|
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<SolanaConfirmedBlock>
|
|
```
|
|
|
|
Le `null` top-level du RPC est conservé comme `None`.
|
|
|
|
Pour un bloc présent, KSP conserve :
|
|
|
|
```text
|
|
previousBlockhash
|
|
blockhash
|
|
parentSlot
|
|
transactions : SolanaWireField<Vec<SolanaBlockTransaction>>
|
|
signatures : SolanaWireField<Vec<String>>
|
|
rewards : SolanaWireField<Vec<SolanaBlockReward>>
|
|
numRewardPartitions : SolanaWireField<u64>
|
|
blockTime : Option<i64>
|
|
blockHeight : Option<u64>
|
|
```
|
|
|
|
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<serde_json::Value>
|
|
```
|
|
|
|
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<String>
|
|
commission: Option<u8>
|
|
commissionBps: SolanaWireField<u16>
|
|
```
|
|
|
|
`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<u64>
|
|
```
|
|
|
|
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
|
|
```
|