182 lines
6.5 KiB
Markdown
182 lines
6.5 KiB
Markdown
<!-- file: kb-onchain-transport/USAGE.md -->
|
||
<!-- version: 5 -->
|
||
|
||
# Utilisation de kb-onchain-transport
|
||
|
||
## Objectif
|
||
|
||
La crate expose les contrats publics nécessaires aux communications RPC HTTP et WebSocket avec Solana.
|
||
|
||
## Prérequis
|
||
|
||
- une configuration `kb-config` contenant au moins un endpoint compatible ;
|
||
- un runtime Tokio pour les opérations asynchrones ;
|
||
- des limites et engagements adaptés à l’opération demandée.
|
||
|
||
## Construire un client HTTP
|
||
|
||
```rust
|
||
fn build_http_client(
|
||
endpoint: kb_config::HttpEndpointConfig,
|
||
) -> kb_core::Result<kb_onchain_transport::HttpClient> {
|
||
let result = kb_onchain_transport::HttpClient::new(endpoint);
|
||
|
||
match result {
|
||
Ok(client) => Ok(client),
|
||
Err(error) => Err(error),
|
||
}
|
||
}
|
||
```
|
||
|
||
## Construire et interroger un pool HTTP
|
||
|
||
```rust
|
||
fn select_query_client(
|
||
profile: &kb_config::ProfileConfig,
|
||
) -> kb_core::Result<kb_onchain_transport::HttpClient> {
|
||
let pool_result = kb_onchain_transport::HttpEndpointPool::from_profile(profile);
|
||
let pool = match pool_result {
|
||
Ok(pool) => pool,
|
||
Err(error) => return Err(error),
|
||
};
|
||
|
||
pool.select_client_for_role_and_method("http_queries", "getBalance")
|
||
}
|
||
```
|
||
|
||
`HttpEndpointPool::snapshot` fournit un état sérialisable des endpoints actifs et de leurs rôles.
|
||
|
||
## Classer une méthode RPC
|
||
|
||
```rust
|
||
fn classify_method(method: &str) -> String {
|
||
kb_onchain_transport::request_kind_from_method(method)
|
||
}
|
||
|
||
assert_eq!(classify_method("getTransaction"), "transaction_read");
|
||
```
|
||
|
||
La valeur retournée sert au routage par rôle. Elle ne remplace pas le nom RPC exact utilisé sur le wire.
|
||
|
||
## Parser une réponse JSON-RPC
|
||
|
||
```rust
|
||
fn parse_response(
|
||
text: &str,
|
||
) -> kb_core::Result<kb_onchain_transport::JsonRpcResponse> {
|
||
let result = kb_onchain_transport::parse_json_rpc_text(text);
|
||
|
||
match result {
|
||
Ok(response) => Ok(response),
|
||
Err(error) => Err(error),
|
||
}
|
||
}
|
||
```
|
||
|
||
Le parseur distingue les réponses de succès, les erreurs JSON-RPC et les notifications.
|
||
|
||
## Charger un solde
|
||
|
||
```rust
|
||
async fn load_balance(
|
||
client: &kb_onchain_transport::HttpClient,
|
||
address: &str,
|
||
) -> kb_core::Result<u64> {
|
||
let result = client
|
||
.get_balance(
|
||
address,
|
||
kb_onchain_transport::GetBalanceConfig::default(),
|
||
)
|
||
.await;
|
||
|
||
match result {
|
||
Ok(balance) => Ok(balance.value),
|
||
Err(error) => Err(error),
|
||
}
|
||
}
|
||
```
|
||
|
||
## Adapter une transaction canonique
|
||
|
||
`GetTransactionAdapter` et `CanonicalTransactionAdapter` convertissent une réponse RPC standard en acquisition canonique sans imposer au pipeline le format brut du fournisseur.
|
||
|
||
```rust
|
||
fn adapt_transaction(
|
||
raw: serde_json::Value,
|
||
config: &kb_onchain_transport::GetTransactionConfig,
|
||
) -> kb_core::Result<kb_onchain_transport::GetTransactionAcquisition> {
|
||
let result = kb_onchain_transport::adapt_get_transaction_result(raw, config);
|
||
|
||
match result {
|
||
Ok(acquisition) => Ok(acquisition),
|
||
Err(error) => Err(error),
|
||
}
|
||
}
|
||
```
|
||
|
||
## Exécution RPC
|
||
|
||
Les types suivants couvrent les opérations d’exécution :
|
||
|
||
- `SimulateTransactionConfig` et `SimulateTransactionResult` ;
|
||
- `SendTransactionConfig` et `SendTransactionResult` ;
|
||
- `ConfirmTransactionConfig` ;
|
||
- `GetLatestBlockhashConfig` ;
|
||
- `GetSignatureStatusesConfig`.
|
||
|
||
La soumission ne remplace pas les contrôles de sécurité, préflights et confirmations opérateur réalisés par les couches supérieures.
|
||
|
||
## Lectures complètes et limites de débit
|
||
|
||
`MAX_COMPLETE_ACCOUNT_DATA_BYTES` expose la borne commune des données décodées retournées par une lecture complète `getAccountInfo`. Les couches supérieures doivent utiliser cette constante au lieu d’aligner une lecture RPC sur la capacité maximale d’un décodeur offline.
|
||
|
||
Le client applique d’abord les limites préventives du rôle sélectionné :
|
||
|
||
- `requests_per_second` alimente un token bucket partagé ;
|
||
- `burst_capacity` borne le burst initial et les crédits accumulés ;
|
||
- `max_concurrent_requests` borne les requêtes simultanément en vol.
|
||
|
||
Ces limiteurs sont partagés entre les clones d’un même endpoint. Les statuts HTTP `429` et erreurs JSON-RPC `429` restent réessayés avec un backoff borné, car un fournisseur peut appliquer un quota global, dynamique ou partagé que la configuration locale ne peut pas connaître. Le délai configuré par `pause_after_rate_limit_ms` est alors appliqué comme cooldown partagé, sauf lorsqu’un en-tête `Retry-After` impose une attente supérieure. Le nombre de retries reste limité et une erreur terminale conserve le nombre de tentatives.
|
||
|
||
## Erreurs et invariants
|
||
|
||
- les réponses sont validées et adaptées avant exposition aux couches supérieures ;
|
||
- les tailles, encodages et listes sont bornés par les contrats de la crate ;
|
||
- une erreur RPC distante reste distincte d’une erreur de transport ou d’adaptation ;
|
||
- les endpoints ne doivent pas être supposés interchangeables lorsqu’ils ont des rôles différents.
|
||
|
||
## Tests de référence
|
||
|
||
- tests d’équivalence des fixtures `getTransaction` ;
|
||
- tests legacy, v0, ALT, CPI et Token-2022 dans `tests/fixtures/` ;
|
||
- tests de `standard_methods.rs` vérifiant la matrice contractuelle RPC ;
|
||
- tests des pools HTTP/WebSocket et du routage par rôle.
|
||
|
||
## Limites durables
|
||
|
||
- la crate traite les transports on-chain ; elle ne gère pas les métadonnées HTTP, IPFS ou Arweave ;
|
||
- elle ne décode pas les instructions de programmes ;
|
||
- elle ne décide pas seule de l’autorisation d’envoyer une transaction.
|
||
|
||
## Limitation préventive et réponses `429`
|
||
|
||
Le client applique les limites du rôle sélectionné avant chaque tentative :
|
||
|
||
- `requests_per_second` et `burst_capacity` alimentent un token bucket partagé ;
|
||
- `max_concurrent_requests` borne les requêtes simultanées ;
|
||
- `pause_after_rate_limit_ms` fournit le cooldown de repli ;
|
||
- `Retry-After` est prioritaire lorsqu’il est fourni par l’endpoint.
|
||
|
||
Ces limites sont propres aux rôles du client. Elles ne créent pas de quotas indépendants côté fournisseur. Lorsque plusieurs rôles utilisent le même endpoint, leur débit et leurs bursts doivent être additionnés pour rester sous la limite globale de cet endpoint.
|
||
|
||
Pour `api.devnet.solana.com`, le profil d’exemple utilise volontairement :
|
||
|
||
```text
|
||
http_queries 3 r/s, burst 3, concurrence 2
|
||
http_transactions 1 r/s, burst 1, concurrence 1
|
||
cooldown 10000 ms
|
||
```
|
||
|
||
Un warning `retry_http_json_rpc_after_rate_limit` signifie que le serveur a tout de même renvoyé `429` et que la seconde ligne de défense a été activée. La requête n’est perdue que si le retry borné se termine en erreur.
|
||
|