v0.2.11-pre.004-fix.001

This commit is contained in:
2026-08-25 21:58:05 +02:00
parent 60afb51451
commit 5c97a772be
8 changed files with 146 additions and 46 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 274
# version: 275
[workspace]
resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.2.11-pre.4"
version = "0.2.11-pre.4.fix.1"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,10 +1,10 @@
// file: crates/ksp-offchain-transport-lib/src/error.rs
// version: 6
// version: 7
/// Stable off-chain transport error when local provider admission defers a request.
pub const ERROR_CODE_HTTP_ADMISSION_DEFERRED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_admission_deferred");
/// Stable off-chain transport error for HTTP 401/403 access denial.
pub const ERROR_CODE_HTTP_ACCESS_DENIED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_access_denied");
/// Stable off-chain transport error when local provider admission defers a request.
pub const ERROR_CODE_HTTP_ADMISSION_DEFERRED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_admission_deferred");
/// Stable off-chain transport error when the hardened reqwest client cannot be initialized.
pub const ERROR_CODE_HTTP_CLIENT_BUILD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_client_build_failed");
/// Stable off-chain transport error for a connection failure without exposing the provider URL.
@@ -29,15 +29,15 @@ pub const ERROR_CODE_HTTP_TEMPORARY_FAILURE: ksp_core_lib::ErrorCode = ksp_core_
pub const ERROR_CODE_HTTP_TIMEOUT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_timeout");
/// Stable off-chain transport error for an invalid exact market-price decimal.
pub const ERROR_CODE_MARKET_PRICE_DECIMAL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_decimal_invalid");
/// Stable off-chain transport error when a disabled market-price provider is invoked directly.
pub const ERROR_CODE_MARKET_PRICE_PROVIDER_DISABLED: ksp_core_lib::ErrorCode =
ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_provider_disabled");
/// Stable off-chain transport error for an invalid normalized market-price observation.
pub const ERROR_CODE_MARKET_PRICE_OBSERVATION_INVALID: ksp_core_lib::ErrorCode =
ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_observation_invalid");
/// Stable off-chain transport error for an invalid market-price provider descriptor.
pub const ERROR_CODE_MARKET_PRICE_PROVIDER_DESCRIPTOR_INVALID: ksp_core_lib::ErrorCode =
ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_provider_descriptor_invalid");
/// Stable off-chain transport error when a disabled market-price provider is invoked directly.
pub const ERROR_CODE_MARKET_PRICE_PROVIDER_DISABLED: ksp_core_lib::ErrorCode =
ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_provider_disabled");
/// Stable off-chain transport error for an invalid market-price provider identifier.
pub const ERROR_CODE_MARKET_PRICE_PROVIDER_ID_INVALID: ksp_core_lib::ErrorCode =
ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_provider_id_invalid");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-offchain-transport-lib/src/market_price_coingecko.rs
// version: 1
// version: 2
//! CoinGecko SOL/USD market-price adapter using the official REST API directly through `reqwest`.
@@ -158,10 +158,10 @@ fn build_request(settings: &crate::MarketPriceCoinGeckoSettings) -> ksp_core_lib
request.append_query_pair("ids", "solana");
request.append_query_pair("vs_currencies", "usd");
request.append_query_pair("include_last_updated_at", "true");
if let std::option::Option::Some(api_key) = settings.api_key() {
if let std::result::Result::Err(error) = request.insert_sensitive_header(COINGECKO_DEMO_API_KEY_HEADER, api_key.as_str()) {
return std::result::Result::Err(error);
}
if let std::option::Option::Some(api_key) = settings.api_key()
&& let std::result::Result::Err(error) = request.insert_sensitive_header(COINGECKO_DEMO_API_KEY_HEADER, api_key.as_str())
{
return std::result::Result::Err(error);
}
return std::result::Result::Ok(request);
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-offchain-transport-lib/src/market_price_coinmarketcap.rs
// version: 1
// version: 2
//! CoinMarketCap SOL/USD market-price adapter using the current Simple Price V2 REST surface.
@@ -168,10 +168,10 @@ fn build_request(settings: &crate::MarketPriceCoinMarketCapSettings) -> ksp_core
request.append_query_pair("ids", "5426");
request.append_query_pair("convert", "USD");
request.append_query_pair("include_last_updated", "true");
if let std::option::Option::Some(api_key) = settings.api_key() {
if let std::result::Result::Err(error) = request.insert_sensitive_header(COINMARKETCAP_API_KEY_HEADER, api_key.as_str()) {
return std::result::Result::Err(error);
}
if let std::option::Option::Some(api_key) = settings.api_key()
&& let std::result::Result::Err(error) = request.insert_sensitive_header(COINMARKETCAP_API_KEY_HEADER, api_key.as_str())
{
return std::result::Result::Err(error);
}
return std::result::Result::Ok(request);
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_coinmarketcap.rs
// version: 1
// version: 2
#[test]
fn coinmarketcap_modes_use_v2_and_map_exact_free_capabilities() -> ksp_core_lib::Result<()> {
@@ -53,7 +53,7 @@ fn coinmarketcap_v2_fixture_normalizes_string_status_and_exact_price() -> ksp_co
let observation = match super::parse_response(
concat!(
r#"{"data":[{"id":5426,"symbol":"SOL","quotes":[{"symbol":"USD","price":151.987654321012345678,"#,
r#"last_updated":"2026-04-01T00:00:00.000Z"}]}],"status":{"error_code":"0"}}"#,
r#""last_updated":"2026-04-01T00:00:00.000Z"}]}],"status":{"error_code":"0"}}"#,
)
.as_bytes(),
provider_id,

View File

@@ -0,0 +1,86 @@
<!-- file: deltas/0.2.11/pre.004-fix.001.md -->
<!-- version: 1 -->
# Delta `0.2.11-pre.004-fix.001` — Gate statique, Clippy et fixture CoinMarketCap V2
## 1. Base requise
Ce correctif s'applique exclusivement après `0.2.11-pre.004`.
Version Cargo attendue à l'entrée :
```text
0.2.11-pre.4
```
Version Cargo de sortie :
```text
0.2.11-pre.4.fix.1
```
## 2. Motif du correctif
Le gate opérateur du `2026-08-25` a produit :
```text
cargo fmt --all exécuté
audit Rust workspace 1 x RUST-FMT-104
audit Markdown PASS, 118 tables / 108 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets 2 warnings collapsible_if
cargo test -p ksp-offchain-transport-lib FAIL, 30 PASS / 1 FAIL
```
L'unique test en échec était `coinmarketcap_v2_fixture_normalizes_string_status_and_exact_price`.
## 3. Corrections
### Ordre des constantes
Le bloc `ERROR_CODE_*` de `src/error.rs` est remis dans l'ordre alphabétique complet attendu par `RUST-FMT-104`. Aucun code textuel ni domaine d'erreur ne change.
### Clippy
Les builders CoinGecko et CoinMarketCap remplacent les deux `if let` imbriqués par des `let`-chains. La logique reste identique : le header sensible n'est ajouté que lorsqu'une clé est présente et toute erreur d'insertion est retournée immédiatement.
### Fixture CoinMarketCap V2
La fixture concaténée omettait le guillemet ouvrant de la clé JSON `last_updated` :
```text
avant : ...,151.987654321012345678,last_updated...
après : ...,151.987654321012345678,"last_updated"...
```
Le parser CoinMarketCap, les DTOs wire, l'endpoint Simple Price V2 et le contrat exact du prix ne sont pas modifiés.
## 4. Fichiers modifiés
```text
Cargo.toml
crates/ksp-offchain-transport-lib/src/error.rs
crates/ksp-offchain-transport-lib/src/market_price_coingecko.rs
crates/ksp-offchain-transport-lib/src/market_price_coinmarketcap.rs
crates/ksp-offchain-transport-lib/unit_tests/market_price_coinmarketcap.rs
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
deltas/0.2.11/pre.004-fix.001.md
```
## 5. Gate opérateur requis
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-offchain-transport-lib
```
`cargo test --workspace` reste recommandé avant poursuite lorsque le temps opérateur le permet.
## 6. Hors scope
Aucun provider supplémentaire, registry, Config, refresh multiple, live smoke, changement d'endpoint ou changement de rate limit n'est introduit.

View File

@@ -1,9 +1,9 @@
<!-- file: docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md -->
<!-- version: 9 -->
<!-- version: 10 -->
# Plan `0.2.11` — Off-chain price transport SOL/USD multi-provider
**Statut courant : `0.2.11-pre.004` active les primitives `http_*` en production et implémente les trois premiers adapters `market_price_*` — CoinGecko, CoinMarketCap et CoinPaprika — en SOL/USD uniquement, sans SDK provider. Le staging `cfg(test)` de `pre.003-fix.001` disparaît donc structurellement avec leurs premiers consumers de production. CoinMarketCap utilise la surface Simple Price V2 actuelle ; la surface V1 deprecated n'est pas introduite.**
**Statut courant : `0.2.11-pre.004-fix.001` corrige le gate statique/Clippy et la fixture CoinMarketCap V2 de `pre.004`; `pre.004` active les primitives `http_*` en production et implémente les trois premiers adapters `market_price_*` — CoinGecko, CoinMarketCap et CoinPaprika — en SOL/USD uniquement, sans SDK provider. Le staging `cfg(test)` de `pre.003-fix.001` disparaît donc structurellement avec leurs premiers consumers de production. CoinMarketCap utilise la surface Simple Price V2 actuelle ; la surface V1 deprecated n'est pas introduite.**
## 1. Base et autorité
@@ -712,6 +712,12 @@ Correction de l'ordre alphabétique des constantes d'erreur, utilisation systém
Activation en production du socle `http_*` et implémentation des trois adapters d'agrégateurs avec DTOs wire privés, parsing décimal exact via `RawValue`, timestamps provider lorsque fournis, endpoints officiels fixes, auth correspondant aux capacités réelles et limites provider décrites dans leurs descriptors. CoinGecko supporte keyless ou Demo key, CoinMarketCap keyless ou Basic key sur Simple Price V2, CoinPaprika reste keyless. Aucun registry global, Config ou smoke live n'est avancé.
#### `pre.004-fix.001` — Ordre des constantes, Clippy et fixture CoinMarketCap V2
**Statut : réalisé, gate Cargo opérateur à rejouer.**
Correctif strict de `pre.004` : tri alphabétique complet des constantes d'erreur, suppression des deux warnings `clippy::collapsible_if` dans les builders CoinGecko/CoinMarketCap, et correction de la fixture CoinMarketCap V2 dont le JSON concaténé omettait le guillemet ouvrant de `last_updated`. Aucun contrat provider, endpoint, parsing runtime, rate limit ou scope de release n'est modifié. La version technique workspace devient `0.2.11-pre.4.fix.1`.
### `pre.005` — Kraken et Coinbase Exchange
**Statut : planifié.**

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md -->
<!-- version: 8 -->
<!-- version: 9 -->
# Validation `0.2.11` — Off-chain price transport SOL/USD
@@ -146,32 +146,40 @@ La version Cargo du correctif est `0.2.11-pre.3.fix.1`. Le gate opérateur du `2
## 3.6 Gate `0.2.11-pre.004`
| Critère | Statut | Preuve |
|------------------------------------------------------------------------|---------|-----------------------------------------------------------|
| version workspace `0.2.11-pre.4` | PASS | `Cargo.toml` racine |
| modules `http_*` actifs en production, sans staging crate-root test | PASS | `src/lib.rs` |
| cause `RUST-FMT-108` de `pre.003-fix.001` supprimée | PASS | modules production avant tests |
| CoinGecko SOL/USD adapter public | PASS | `market_price_coingecko.rs` |
| CoinGecko keyless et Demo key distingués | PASS | settings + descriptor + header sensible |
| CoinMarketCap SOL/USD adapter public | PASS | `market_price_coinmarketcap.rs` |
| CoinMarketCap Simple Price V2 utilisé, V1 deprecated absent du runtime | PASS | endpoints V2 fixes + canari dependency boundary |
| CoinMarketCap keyless et Basic key distingués | PASS | settings + descriptor + header sensible |
| CoinPaprika SOL/USD adapter public | PASS | `market_price_coinpaprika.rs` |
| CoinPaprika sans faux credential | PASS | settings keyless uniquement |
| prix JSON provider parsé exactement sans passage par `f64` | PASS | `RawValue` -> `MarketPriceDecimal::parse_json_raw` |
| timestamps provider conservés lorsqu'ils existent | PASS | Unix seconds CoinGecko, RFC3339 CoinMarketCap/CoinPaprika |
| DTOs wire provider privés | PASS | structs privées dans chaque module provider |
| endpoints provider fixes et HTTPS | PASS | constantes privées + `HttpGetRequest::new_https` |
| API keys absentes de `Debug` et diagnostics | PASS | `MarketPriceApiKey` redacted + tests settings |
| `429` alimente le cooldown local partagé | PASS | `get_json` -> `record_rate_limited` |
| aucun SDK provider | PASS | manifest + boundary tests |
| Config, registry global et refresh multiple non avancés | PASS | scopes réservés à `pre.007+` / `pre.009` |
| audit Rust sandbox | PASS | clean, 0 candidate export |
| audit Markdown sandbox | PASS | à recalculer après finalisation documentaire |
| gate Cargo complet `pre.004` | PENDING | opérateur |
| Critère | Statut | Preuve |
|------------------------------------------------------------------------|--------|------------------------------------------------------------------------|
| version workspace `0.2.11-pre.4` | PASS | `Cargo.toml` racine |
| modules `http_*` actifs en production, sans staging crate-root test | PASS | `src/lib.rs` |
| cause `RUST-FMT-108` de `pre.003-fix.001` supprimée | PASS | modules production avant tests |
| CoinGecko SOL/USD adapter public | PASS | `market_price_coingecko.rs` |
| CoinGecko keyless et Demo key distingués | PASS | settings + descriptor + header sensible |
| CoinMarketCap SOL/USD adapter public | PASS | `market_price_coinmarketcap.rs` |
| CoinMarketCap Simple Price V2 utilisé, V1 deprecated absent du runtime | PASS | endpoints V2 fixes + canari dependency boundary |
| CoinMarketCap keyless et Basic key distingués | PASS | settings + descriptor + header sensible |
| CoinPaprika SOL/USD adapter public | PASS | `market_price_coinpaprika.rs` |
| CoinPaprika sans faux credential | PASS | settings keyless uniquement |
| prix JSON provider parsé exactement sans passage par `f64` | PASS | `RawValue` -> `MarketPriceDecimal::parse_json_raw` |
| timestamps provider conservés lorsqu'ils existent | PASS | Unix seconds CoinGecko, RFC3339 CoinMarketCap/CoinPaprika |
| DTOs wire provider privés | PASS | structs privées dans chaque module provider |
| endpoints provider fixes et HTTPS | PASS | constantes privées + `HttpGetRequest::new_https` |
| API keys absentes de `Debug` et diagnostics | PASS | `MarketPriceApiKey` redacted + tests settings |
| `429` alimente le cooldown local partagé | PASS | `get_json` -> `record_rate_limited` |
| aucun SDK provider | PASS | manifest + boundary tests |
| Config, registry global et refresh multiple non avancés | PASS | scopes réservés à `pre.007+` / `pre.009` |
| audit Rust sandbox | PASS | clean, 0 candidate export |
| audit Markdown sandbox | PASS | clean, 118 tables / 109 files |
| gate Cargo complet `pre.004` | FAIL | audit Rust : 1 x RUST-FMT-104 ; Clippy : 2 warnings ; 1 fixture CMC KO |
Les tests déterministes de `pre.004` couvrent les modes d'auth, les headers sans fuite de credential, les URLs/query fixes, le parsing des réponses SOL/USD, le rejet d'identités ou schémas incohérents et le parsing exact des nombres JSON. Aucun smoke réseau n'est revendiqué dans cette tranche.
## 3.7 Correctif `0.2.11-pre.004-fix.001`
Le gate opérateur du `2026-08-25` sur `pre.004` a confirmé `cargo check --workspace` PASS, mais a détecté trois écarts avant `pre.005` : une violation `RUST-FMT-104` dans le bloc de constantes de `src/error.rs`, deux warnings `clippy::collapsible_if` dans les builders CoinGecko/CoinMarketCap et un échec du test `coinmarketcap_v2_fixture_normalizes_string_status_and_exact_price`.
L'échec CoinMarketCap provenait uniquement de la fixture : la concaténation produisait `...,151.987654321012345678,last_updated...` au lieu de `...,151.987654321012345678,"last_updated"...`. Le parser runtime n'est pas modifié. Le correctif remet le bloc de constantes dans l'ordre alphabétique complet, utilise des `let`-chains pour les deux insertions de headers optionnels et corrige le JSON déterministe de la fixture.
La version Cargo du correctif est `0.2.11-pre.4.fix.1`. Aucun endpoint, mode d'auth, descriptor, limite provider ou contrat public n'est modifié.
## 4. Matrice provider prévue
| Provider | SOL/USD V1 | Gratuit V1 | Mode auth prévu | Test déterministe | Smoke live | Statut courant |