# kb_rpc Ce crate contient les clients et adaptateurs Solana JSON-RPC HTTP/WebSocket du workspace. Il reste indépendant des stores concrets, des décodeurs, des matérialiseurs, du wallet et de Tauri. ## API publique principale | Famille d’exports | Usage | |---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| | `RpcEndpoint`, `SolanaRpcClient` | Configuration d’un endpoint et abstraction minimale d’un client RPC. | | `HttpClient`, `HttpEndpointPool` | Appels JSON-RPC HTTP et routage déterministe par rôle/request kind. | | `WsClient`, `WsEndpointPool` | Configuration WebSocket et sélection d’un endpoint compatible par rôle. | | `StandardWsRequest`, `StandardWsNotification`, `WsSession` | Neuf contrats PubSub typés et runtime persistant multiplexé avec unsubscribe, reconnexion et réabonnement bornés. | | `JsonRpcRequest`, `JsonRpcResponse`, `parse_json_rpc_text(...)` | Contrats et parsing JSON-RPC 2.0 communs aux transports. | | `GetTransactionConfig`, `GetTransactionAdapter`, `CanonicalTransactionAdapter` | Acquisition et adaptation d’une transaction vers le modèle canonique. | | `GetSignaturesForAddressConfig`, `AddressSignatureInfo` | Pagination bornée des signatures pour les backfills. | | configurations/résultats `execution_rpc` | Paramètres et réponses typés pour cluster, blockhash, frais, simulation, comptes, airdrop, envoi et confirmation. | | fonctions `adapt_*` | Conversion explicite d’une valeur JSON-RPC en contrat typé validé. | | `validate_solana_hash_text`, `validate_solana_pubkey_text`, `validate_transaction_signature_text` | Validation base58 et longueur des identifiants Solana. | | `request_kind_from_method(...)` | Normalisation stable du nom d’une méthode pour le routage d’endpoint. | | `StandardHttpRequest`, requêtes `Get*Request`, configurations `Rpc*Config` | Contrats configurables propres à `kb_rpc` pour toutes les méthodes HTTP standard. | Les méthodes réseau retournent `kb_core::Result`. Les fonctions `adapt_*` sont utiles aux tests, aux transports alternatifs et à l’analyse de réponses déjà acquises ; elles n’effectuent aucun I/O. ### Exemple minimal ```rust let config = kb_rpc::GetLatestBlockhashConfig::confirmed(); let result = client.get_latest_blockhash(&config).await?; let blockhash = result.blockhash; ``` L’application choisit un `HttpClient` direct ou un `HttpEndpointPool`. Le pool vérifie les rôles et request kinds avant d’appeler le client ; il ne modifie pas la sémantique du résultat. ## Transports et routage - client HTTP JSON-RPC standard basé sur `reqwest` ; - pool HTTP sélectionné par `role` et `request_kind` ; - client WebSocket standard basé sur `tokio-tungstenite` ; - pool WebSocket par rôle ; - contrats JSON-RPC 2.0 communs ; - tracing canonique `kb_rpc` sans journalisation des secrets intégrés aux URL. ## Acquisition canonique ### `getTransaction` `GetTransactionConfig`, `GetTransactionAdapter` et `CanonicalTransactionAdapter` transforment une réponse JSON-RPC standard en `kb_model::CanonicalTransaction` indépendante du fournisseur. ### `getSignaturesForAddress` `GetSignaturesForAddressConfig` construit les pages bornées `before`/`until` et `AddressSignatureInfo` conserve les métadonnées nécessaires au backfill. ## RPC d’exécution — `0.4.2-pre.004` à `pre.013` Les méthodes suivantes possèdent maintenant des configurations, résultats et adaptateurs typés : | Méthode | Entrée principale | Résultat | |------------------------------------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| | `HttpClient::get_genesis_hash` | aucune | `GenesisHashResult` avec hash et classification Devnet/Testnet/Mainnet éventuelle. | | `HttpClient::get_latest_blockhash` | `GetLatestBlockhashConfig` | `LatestBlockhashResult` avec contexte, blockhash et dernière block height valide. | | `HttpClient::get_fee_for_message` | message legacy/v0 sérialisé en base64, `GetFeeForMessageConfig` | `FeeForMessageResult`, dont la valeur peut être absente si le blockhash n’est plus valide. | | `HttpClient::simulate_transaction` | transaction sérialisée en base64, `SimulateTransactionConfig` | `SimulateTransactionResult` avec erreur runtime, logs, unités, frais, blockhash de remplacement et diagnostics optionnels. | | `HttpClient::get_account_info` | public key, `GetAccountInfoConfig` | `AccountInfoResult` avec contexte, métadonnées, `space` et données complètes optionnelles bornées. | | `HttpClient::get_minimum_balance_for_rent_exemption` | longueur de données, `GetMinimumBalanceForRentExemptionConfig` | minimum rent-exempt en lamports pour cette taille. | | `HttpClient::get_balance` | public key, `GetBalanceConfig` | `BalanceResult` avec contexte et lamports. | | `HttpClient::request_airdrop` | public key, lamports, `RequestAirdropConfig` | `AirdropResult` avec signature du faucet. | | `HttpClient::send_transaction` | transaction signée base64, signature primaire attendue, `SendTransactionConfig` | `SendTransactionResult` uniquement si la signature RPC correspond à la transaction locale. | | `HttpClient::get_signature_statuses` | une à 256 signatures, `GetSignatureStatusesConfig` | `SignatureStatusesResult` positionnel. | | `HttpClient::get_block_height` | `GetBlockHeightConfig` | `BlockHeightResult`. | Chaque méthode existe également sur `HttpEndpointPool` avec le suffixe `_for_role` afin de conserver le routage configuré. `SimulateTransactionConfig::unsigned_with_replacement()` prépare une simulation sans vérification de signature avec remplacement du recent blockhash. Elle permet de simuler avant la phase de signature, conformément au pipeline d’exécution du projet. `SimulateTransactionResult::to_execution_result(...)` convertit le résultat RPC vers `kb_execution_api::ExecutionSimulationResult`. Le contexte de cluster, de blockhash récent ou de nonce durable reste fourni explicitement par l’orchestrateur ; `kb_rpc` ne l’invente pas. Les adaptateurs appliquent : - validation base58 des genesis hashes et blockhashes ; - validation base64 non vide et bornée des messages/transactions ; - limite locale du nombre de snapshots de comptes demandés ; - conservation provider-neutral des erreurs, return data, inner instructions et comptes simulés ; - lecture metadata-only de l’existence, du solde, de l’owner, de `space` et du statut executable avec `dataSlice.length = 0` ; - lecture optionnelle des données complètes en base64 avec limite locale, contrôle de l’encodage et égalité stricte entre `space` et longueur décodée ; - conservation explicite du minimum rent-exempt et de la longueur de données demandée ; - distinction entre erreur de transport/JSON-RPC et erreur runtime de la transaction simulée. ### Modes `getAccountInfo` `GetAccountInfoConfig::confirmed()` conserve le mode léger historique. La requête utilise `dataSlice = { offset: 0, length: 0 }` et l’adaptateur exige un payload décodé vide tout en conservant le champ `space` complet. `GetAccountInfoConfig::confirmed_with_data(max_data_bytes)` demande les données complètes en omettant `dataSlice`. La limite doit être comprise entre 1 et 65 536 octets. L’adaptateur refuse avant utilisation : - un `space` supérieur à la limite demandée ; - un tuple `data` mal formé ; - un encodage différent de `base64` ; - un payload encodé ou décodé trop grand ; - une longueur décodée différente de `space`. Ce mode est utilisé pour charger exactement les 80 octets d’un compte durable nonce avant validation dans `kb_execution_solana`. Le RPC ne décode pas lui-même la sémantique du compte. ## Classification du cluster Les genesis hashes officiels Devnet, Testnet et Mainnet sont reconnus. Le hash Mainnet n’a pas changé ; `mainnet-beta` reste un alias historique encore utilisé par les endpoints RPC et la CLI. Le contrat public utilise `ExecutionCluster::Mainnet`, sérialisé `mainnet`, tout en acceptant `mainnet_beta` à la désérialisation pour compatibilité. Un hash différent reste non classifié : il peut correspondre à un validateur local ou à un cluster privé et doit être rapproché du contexte attendu par l’orchestrateur. Pour le chemin de signature, `SimulateTransactionConfig::unsigned_exact()` conserve le blockhash du message. `unsigned_with_replacement()` reste disponible pour le diagnostic, mais son résultat est marqué par le contrat commun et ne peut pas autoriser la signature ou l’envoi du message original. ## Envoi et confirmation bornée `SendTransactionConfig` interdit `skipPreflight = true` dans le chemin d’exécution. Il peut être construit depuis `ExecutionConfig`, transmet `maxRetries` et `minContextSlot`, puis vérifie que la signature retournée par le nœud est exactement la signature primaire calculée localement. `ConfirmTransactionConfig` borne l’intervalle et le nombre de polls. `HttpEndpointPool::confirm_transaction_for_roles` interroge `getSignatureStatuses`, contrôle l’erreur runtime, compare le commitment demandé et utilise `getBlockHeight` lorsqu’une dernière block height valide est connue. Le résultat commun contient le statut, le slot éventuel, le nombre de tentatives, la dernière block height observée et le diagnostic éventuel. ## Frontières `kb_rpc` ne construit pas le message, ne résout aucun signer et ne signe rien. Il accepte uniquement un payload déjà signé et conserve l’envoi séparé de la confirmation : ```text PreparedExecutionPlan -> kb_execution_solana build/simulation/signature -> kb_rpc sendTransaction -> kb_rpc confirmation bornée -> orchestration post-exécution ``` ## Validation Les tests offline vérifient les paramètres JSON-RPC, les réponses valides/nulles, les erreurs runtime, les hashes invalides, les payloads base64 invalides, les limites des données de compte, les divergences `space`/longueur, la classification de cluster et la conversion vers le contrat d’exécution commun. ## Informations d’époque pour l’exécution stateful `GetEpochInfoConfig`, `EpochInfoResult`, `HttpClient::get_epoch_info` et `HttpEndpointPool::get_epoch_info_for_role` encapsulent `getEpochInfo` avec commitment et `minContextSlot` optionnel. Cette primitive est utilisée pour les fenêtres de slot ALT/Slashing et les délais de fermeture. Elle reste indépendante de toute logique métier d’exécution. ## Inventaire HTTP standard configurable — `0.4.2-pre.024` `STANDARD_HTTP_METHODS` enregistre exactement les 52 méthodes HTTP de la référence Solana. Elles possèdent maintenant toutes un contrat typé : les 14 adaptateurs spécialisés existants restent inchangés et les 38 méthodes auparavant limitées au JSON brut utilisent des requêtes `Get*Request` propres à `kb_rpc`. La couche configurable ne réexporte aucun DTO d’Agave. Les options sont des champs `Option` indépendants : une valeur absente n’est pas sérialisée, tandis que `Some(false)`, `Some(0)` ou `Some([])` reste distinguable lorsqu’il s’agit d’une valeur valide du contrat RPC. Les types officiels peuvent servir d’oracle dans les tests, mais ne constituent pas l’API publique du crate. ```rust let request = kb_rpc::GetProgramAccountsRequest { program_id, config: Some(kb_rpc::RpcProgramAccountsConfig { filters: Some(vec![kb_rpc::RpcProgramAccountFilter::DataSize(165)]), encoding: Some(kb_rpc::RpcAccountEncoding::Base64Zstd), data_slice: None, commitment: Some(kb_rpc::RpcCommitmentLevel::Confirmed), min_context_slot: Some(anchor_slot), with_context: Some(true), sort_results: Some(true), }), }; let response = client.execute_standard_request(&request).await?; ``` Le même contrat est disponible via `HttpEndpointPool::execute_standard_request_for_role`. La requête fournit son nom de méthode, construit le tableau positionnel exact et définit son type de réponse associé. Le transport refuse un type absent du registre ou déclaré avec un contrat autre que `typed_adapter`. Les validations locales couvrent notamment : - 100 comptes maximum pour `getMultipleAccounts` ; - 128 comptes verrouillés maximum pour `getRecentPrioritizationFees` ; - 500 000 slots maximum pour les plages et limites de blocs ; - 720 entrées maximum pour `getRecentPerformanceSamples` ; - 128 octets décodés maximum pour un filtre memcmp ; - refus de `processed` pour les méthodes historiques qui ne l’acceptent pas ; - incompatibilité `jsonParsed` / `dataSlice` ; - validation base58 de toutes les clés et blockhashes structurants. | Surface | Inventaire | Contrat actuel | |--------------------|-----------------------:|-----------------------------------------------------------------------------------| | HTTP | 52 méthodes | 52 adaptateurs typés, 0 contrat standard limité au JSON brut | | WebSocket | 9 paires / 18 méthodes | 9 requêtes typées, 9 notifications typées et runtime persistant réutilisable | | WebSocket instable | 3 paires | `block`, `slotsUpdates` et `vote`, refusées sans capacité explicite de l’endpoint | `execute_standard_method_raw` reste disponible pour les outils bas niveau qui manipulent explicitement un `StandardHttpMethodSpec`, mais n’est plus nécessaire pour couvrir une méthode HTTP standard. ## PubSub WebSocket standard — `0.4.2-pre.025` `StandardWsRequest` représente exactement les neuf familles officielles. Chaque variante construit son tableau de paramètres sans valeur implicite imposée par la bibliothèque : encodage, `dataSlice`, filtres, commitment, détails de bloc, rewards, version maximale et notification anticipée de signature restent sélectionnables indépendamment. `adapt_standard_ws_notification` transforme les neuf méthodes de notification en `StandardWsNotification`. Les réponses contextuelles, les comptes directs ou contextualisés de `programNotification`, le marqueur `receivedSignature`, les mises à jour de slots taguées et les votes gossip sont conservés dans des types propres à `kb_rpc`. ```rust let client = pool.select_client_for_role_and_method("slot_notifications", "slotSubscribe")?; let session = kb_rpc::WsSession::connect( client, kb_rpc::StandardWsCapabilities::default(), kb_rpc::WsReconnectPolicy::bounded(3, 250, 2_000)?, ) .await?; let mut events = session.subscribe_events(); let acknowledgement = session .subscribe(kb_rpc::StandardWsRequest::Slot(kb_rpc::SlotSubscribeRequest)) .await?; let remote_id = acknowledgement.subscription.remote_subscription_id; ``` La session possède un identifiant local stable par souscription et suit séparément l’identifiant distant attribué par le nœud. Après reconnexion, elle réémet les requêtes actives, remappe les IDs distants et publie `SubscriptionRemapped`. L’unsubscribe explicite attend une réponse booléenne vraie ; les requêtes et désabonnements ont leurs timeouts propres. Une notification terminale `signatureNotification` retire automatiquement la souscription one-shot, tandis que `receivedSignature` la conserve jusqu’au statut terminal. La fermeture envoie des désabonnements best-effort avant la frame Close. Les méthodes instables `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` ne sont jamais déduites d’un simple nom de méthode demandé : `StandardWsCapabilities::from_endpoint` exige qu’un rôle activé de l’endpoint annonce explicitement le request kind correspondant, ou un wildcard volontaire. La première souscription réelle sert de probe du nœud. Lorsqu’une réponse JSON-RPC indique que la méthode est absente, non activée, indisponible ou non supportée, `WsSession` désactive uniquement la capacité concernée, met à jour son snapshot et refuse localement les appels suivants. Une erreur de paramètres, d’authentification ou de rate limit ne désactive pas la capacité. La validation finale `0.4.2` compte 113 tests `kb_rpc`. Les logs Tauri réels confirment des souscriptions et désabonnements `slot`, `root` et `program`, ainsi que le garde-fou interdisant de changer d’endpoint sans fermer la session active. L’état détaillé est documenté dans `docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md`.