13 KiB
Utilisation de ksp-onchain-transport-lib
Ce guide présente les surfaces publiques destinées aux consumers. Les notes de release restent dans CHANGELOG.md et les deltas.
1. Construction directe du runtime
Transport peut être utilisé sans Config. Le consumer construit les settings publics puis le pool :
let url = match ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://api.devnet.solana.com") {
Ok(value) => value,
Err(error) => return Err(error),
};
let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new(
ksp_onchain_transport_lib::HttpRoleName::new("default"),
true,
vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()],
100,
ksp_onchain_transport_lib::HttpRoleLimits::new(None, None, None, None),
);
let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new(
"solana_devnet_public",
true,
ksp_onchain_transport_lib::HttpProviderName::new("solana-public"),
ksp_onchain_transport_lib::HttpClusterName::new("devnet"),
url,
std::time::Duration::from_secs(5),
std::time::Duration::from_secs(15),
Some(8),
vec![role],
);
let settings = ksp_onchain_transport_lib::HttpTransportSettings::new(
vec![endpoint],
ksp_onchain_transport_lib::HttpRetrySettings::new(
2,
std::time::Duration::from_millis(100),
std::time::Duration::from_secs(2),
),
);
let pool = match ksp_onchain_transport_lib::HttpTransportPool::new(settings) {
Ok(value) => value,
Err(error) => return Err(error),
};
HttpTransportSettings::validate() peut être appelé explicitement avant la construction du pool lorsque le consumer veut séparer validation et initialisation.
2. Construction via ksp-config-lib
Lorsque le consumer utilise Config, la direction reste Config -> Transport :
let resolved = match engine.load_resolved_transport_config(Some("devnet_public"), &environment) {
Ok(value) => value,
Err(error) => return Err(error),
};
let pool = match ksp_onchain_transport_lib::HttpTransportPool::new(resolved.into_settings()) {
Ok(value) => value,
Err(error) => return Err(error),
};
Le document standard peut contenir une URL provenant d'un KSP_SECRET_*. La valeur réelle est transmise au runtime, mais les projections sûres et Debug restent redacted.
3. Session physique WebSocket
À partir de 0.2.7-pre.004, un consumer peut créer explicitement une session physique :
let ws_url = match ksp_onchain_transport_lib::WsEndpointUrl::parse("wss://api.devnet.solana.com") {
Ok(value) => value,
Err(error) => return Err(error),
};
let endpoint = ksp_onchain_transport_lib::WsEndpointSettings::new(
"devnet_public",
true,
ksp_onchain_transport_lib::WsProviderName::new("solana-public"),
ksp_onchain_transport_lib::WsClusterName::new("devnet"),
ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard,
ws_url,
ksp_onchain_transport_lib::WsSessionSettings::default(),
);
let session = match ksp_onchain_transport_lib::WsSession::connect(endpoint).await {
Ok(value) => value,
Err(error) => return Err(error),
};
let snapshot = session.snapshot();
Deux appels WsSession::connect avec le même endpoint créent volontairement deux connexions physiques distinctes. Il n'existe encore aucun pool de sessions automatique.
Le socket brut et la primitive JSON-RPC générique ne sont pas publics. À partir de pre.006, le moteur générique de subscription typed existe dans la crate mais sa création reste pub(crate) jusqu'aux wrappers standard publics des tranches pre.009+ ; il ne constitue donc toujours pas une escape hatch provider-specific.
WsSubscription<T> est déjà le handle public commun que ces wrappers retourneront. Il porte un WsSubscriptionId local stable, jamais le remote ID numérique du serveur. Les notifications arrivent via un receiver typed borné et unsubscribe().await exécute le *Unsubscribe correspondant en préservant son résultat booléen.
Le snapshot de session expose les subscriptions actuellement enregistrées via WsSubscriptionSnapshot, avec remote_bound: bool seulement. Le remote ID réel n'est jamais projeté.
Reconnect automatique borné
Depuis 0.2.7-pre.007, les settings de session contrôlent réellement le reconnect physique. Une perte de socket publie Reconnecting { attempt }, invalide les remote IDs et incrémente continuity_gap_count. Avec la policy par défaut ActiveSubscriptions, les handles logiques gardent leur WsSubscriptionId et passent temporairement en Resubscribing; l'actor recrée leurs subscriptions dans l'ordre local avant de republier Active.
WsResubscribePolicy::Never reconnecte uniquement la session physique : les subscriptions existantes deviennent terminales et doivent être recréées explicitement par le consumer. Dans les deux modes, les requests applicatives qui étaient en vol lors de la coupure échouent et ne sont pas rejouées implicitement.
unsubscribe().await peut être appelé pendant Reconnecting ou Resubscribing. La cancellation locale gagne et le handle ne redevient jamais Active. Un ACK distant tardif est nettoyé best-effort par l'actor. Aucun backfill HTTP n'est déclenché automatiquement ; le consumer doit traiter continuity_gap_count comme un signal de réconciliation éventuelle.
Fermeture explicite
À partir de 0.2.7-pre.005, fermer explicitement la session est la voie normale de shutdown :
let session = ksp_onchain_transport_lib::WsSession::connect(endpoint).await?;
// ... utilisation future des subscriptions typed ...
session.close().await?;
close() agit sur toute la session physique, y compris les clones du handle. Il annule les requests en attente, publie Closing, tente le Close WebSocket dans le budget configuré, puis publie Closed. Une session Closed refuse les nouvelles requests internes.
Les limites de taille et de capacité sont des policies KSP configurables par WsSessionSettings; elles ne doivent pas être interprétées comme des limites protocolaires Solana officielles.
Le snapshot expose seulement l'identité locale, les metadata logiques de l'endpoint, l'état, les compteurs sûrs et les projections locales de subscriptions. L'URL et les remote subscription IDs ne sont jamais projetés. La disparition de tous les handles de session déclenche le cleanup actor best-effort ; close().await reste la voie normale de shutdown.
4. Appels typés
Les wrappers typés se trouvent directement sur HttpTransportPool.
let role = ksp_onchain_transport_lib::HttpRoleName::new("default");
let health = pool.get_health(&role).await;
let genesis_hash = pool.get_genesis_hash(&role).await;
let version = pool.get_version(&role).await;
let balance = pool
.get_balance(
&role,
&ksp_core_lib::PRGIDPK_SOLANA_SYSTEM,
Some(&ksp_onchain_transport_lib::GetBalanceConfig::new(
Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed),
None,
)),
)
.await;
Les quatre canaris 0.2.1 restent disponibles. 0.2.2 ajoute les wrappers typés Accounts, Tokens et Cluster. Exemples représentatifs :
let account = pool
.get_account_info(&role, &ksp_core_lib::PRGIDPK_SOLANA_SYSTEM, None)
.await;
let epoch = pool.get_epoch_info(&role, None).await;
let vote_accounts = pool.get_vote_accounts(&role, None).await;
La release stable 0.2.4 contient les 52 wrappers typés courants : 4 foundation + 22 Accounts/Tokens/Cluster + 11 Transactions + 10 Blocks + 5 Economics. Les DTOs Transport conservent les null, options, overloads et formes wire sans décodage Program/SPL métier.
Exemples Transaction représentatifs :
let context = ksp_onchain_transport_lib::SolanaContextConfig::new(
std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized),
std::option::Option::None,
);
let latest = pool
.get_latest_blockhash(&role, std::option::Option::Some(&context))
.await;
let transaction_count = pool
.get_transaction_count(&role, std::option::Option::Some(&context))
.await;
getTransaction expose la config moderne complète et une forme bare-encoding legacy séparée et deprecated. requestAirdrop et sendTransaction sont des write submissions : elles utilisent la protection centrale NeverAfterDispatch. simulateTransaction reste une simulation retry-safe et conserve son résultat riche sans introduire de décodage Program.
Exemples Blocks/Economics représentatifs :
let block_height = pool.get_block_height(&role, Some(&context)).await;
let inflation_rate = pool.get_inflation_rate(&role).await;
let stake_minimum = pool.get_stake_minimum_delegation(&role, Some(&context)).await;
getBlock possède également une forme bare-encoding legacy séparée et deprecated. Les valeurs Economics restent celles du runtime : le consumer ne doit pas supposer localement un taux d'inflation ou un minimum de délégation constant.
5. Exécution JSON-RPC standard générique
Une méthode courante auditée peut être appelée via son descriptor :
if let Some(descriptor) = ksp_onchain_transport_lib::find_http_rpc_method("getSlot") {
let _result = pool.execute_standard_rpc(&role, descriptor, vec![]).await;
}
Cette API retourne un serde_json::Value. Elle reste utile pour les extensions provider, les diagnostics et les méthodes hors registre standard, mais elle ne remplace jamais le wrapper typé d'une méthode HTTP standard désormais couverte par KSP.
Avant exécution, ensure_runtime_supported() est appliqué. Une méthode historique Removed retourne ERROR_CODE_METHOD_REMOVED au lieu d'émettre un appel réseau fictif.
6. Sélection et admission sans exécuter la requête
Pour inspecter le routing :
if let Some(descriptor) = ksp_onchain_transport_lib::find_http_rpc_method("getBalance") {
let _selection = pool.select_for_method(&role, descriptor);
let _permit = pool.acquire_for_method(&role, descriptor).await;
}
Dans le même bloc, acquire_for_method() réserve réellement la capacité RPS/concurrence sous deadline.
HttpRequestPermit détient la capacité de concurrence jusqu'à sa destruction. Aucun verrou synchrone n'est conservé pendant l'attente réseau.
7. Snapshots runtime
HttpTransportPool::snapshot() fournit une vue sûre des endpoints/rôles : disponibilité, limites, requêtes en vol, cooldown restant et compteurs runtime.
Les URLs d'endpoint n'y apparaissent jamais.
8. Retry et write submissions
La policy de retry est portée par la metadata des méthodes et evaluate_transport_retry().
Les reads/simulations classés RetrySafe peuvent être réessayés dans le budget configuré lorsqu'une cause transport est explicitement retryable.
Pour une opération WriteSubmission / NeverAfterDispatch, un timeout ou autre résultat ambigu après dispatch arrête la resoumission automatique. Le consumer métier ne doit pas contourner cette protection avec une boucle de retry externe aveugle.
9. Logging
Les événements Transport utilisent le target :
ksp-onchain-transport-lib
Ne jamais journaliser l'URL complète, un token provider, un body massif, une transaction complète ou une réponse complète.
La configuration standard route les événements info de Transport vers un fichier dédié. Pour une investigation temporaire, élever uniquement ce target/sink à debug ou trace, puis revenir à info avant clôture du développement.
10. Smokes Devnet opt-in
Le smoke Transport pur construit ses settings programmatiquement et exerce un sous-ensemble représentatif d'Accounts/Tokens/Cluster, trois reads Transactions, puis des reads Blocks/Economics de la release stable 0.2.4 :
cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignored --nocapture
Il appelle getAccountInfo, getTokenAccountsByOwner, getEpochInfo, getVoteAccounts, puis getLatestBlockhash, isBlockhashValid, getTransactionCount, getBlockHeight, getInflationRate et getStakeMinimumDelegation. La branche Token suit la forme Devnet documentée : owner Pubkey ordinaire de l'exemple officiel, selector programId avec l'ID canonique du programme SPL Token, puis config explicite commitment: finalized + encoding: jsonParsed. Une réponse vide reste acceptable. La branche Transaction reste read-only : elle ne déclenche ni airdrop ni soumission de transaction et ne remplace pas les fixtures déterministes couvrant les 11 wrappers.
Le smoke historique de composition Config -> Transport reste également disponible :
cargo test -p ksp-config-lib --test transport_devnet_smoke -- --ignored --nocapture
Il valide le profil committé devnet_public et les quatre canaris foundation. Il reste transitoirement hébergé dans Config : les futurs smokes cross-crates ne doivent pas faire de Config leur destination générale et devront migrer vers une surface d'intégration/orchestration dédiée lorsqu'elle existera.
Les endpoints publics Solana sont rate-limités et non destinés à la production. Un échec réseau externe n'est pas assimilé automatiquement à une régression locale ; les fixtures HTTP locales restent les gates reproductibles.