v0.2.11-pre.009
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/src/lib.rs
|
||||
// version: 18
|
||||
// version: 19
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -21,6 +21,7 @@ mod environment;
|
||||
mod error;
|
||||
mod logging;
|
||||
mod management;
|
||||
mod offchain_transport;
|
||||
mod packaging;
|
||||
mod persistence;
|
||||
mod profile;
|
||||
@@ -127,6 +128,8 @@ pub use self::management::LoggingOutputFilterConfig;
|
||||
pub use self::management::LoggingProfileConfig;
|
||||
/// Typed source contract for one global Logging target override.
|
||||
pub use self::management::LoggingTargetFilterConfig;
|
||||
/// Effective standard Off-chain Transport configuration resolved from Config.
|
||||
pub use self::offchain_transport::ResolvedOffchainTransportConfig;
|
||||
/// Writable KSP runtime roots prepared from packaged Config resources.
|
||||
pub use self::packaging::PackagedRuntimeLayout;
|
||||
/// Prepares the shared writable KSP desktop runtime from immutable packaged resources.
|
||||
@@ -155,6 +158,10 @@ pub use self::registry::DEFAULT_COMPOSITE_SCHEMA_FILENAME;
|
||||
pub use self::registry::DEFAULT_STD_LOGGING_FILENAME;
|
||||
/// Default physical filename for the standard Logging JSON Schema document.
|
||||
pub use self::registry::DEFAULT_STD_LOGGING_SCHEMA_FILENAME;
|
||||
/// Default physical filename for the standard Off-chain Transport configuration document.
|
||||
pub use self::registry::DEFAULT_STD_OFFCHAIN_TRANSPORT_FILENAME;
|
||||
/// Default physical filename for the standard Off-chain Transport JSON Schema document.
|
||||
pub use self::registry::DEFAULT_STD_OFFCHAIN_TRANSPORT_SCHEMA_FILENAME;
|
||||
/// Default physical filename for the standard HTTP + WebSocket + Yellowstone gRPC Transport configuration document.
|
||||
pub use self::registry::DEFAULT_STD_TRANSPORT_FILENAME;
|
||||
/// Default physical filename for the standard HTTP + WebSocket + Yellowstone gRPC Transport JSON Schema document.
|
||||
@@ -169,12 +176,16 @@ pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK;
|
||||
pub use self::registry::FILE_ID_SCHEMA_COMPOSITE;
|
||||
/// Logical file identifier for the standard Logging JSON Schema document.
|
||||
pub use self::registry::FILE_ID_SCHEMA_STD_LOGGING;
|
||||
/// Logical file identifier for the standard Off-chain Transport JSON Schema document.
|
||||
pub use self::registry::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT;
|
||||
/// Logical file identifier for the standard HTTP + WebSocket + Yellowstone gRPC Transport JSON Schema document.
|
||||
pub use self::registry::FILE_ID_SCHEMA_STD_TRANSPORT;
|
||||
/// Logical file identifier for the standard Wallet JSON Schema document.
|
||||
pub use self::registry::FILE_ID_SCHEMA_STD_WALLET;
|
||||
/// Logical file identifier for the standard Logging configuration document.
|
||||
pub use self::registry::FILE_ID_STD_LOGGING;
|
||||
/// Logical file identifier for the standard Off-chain Transport configuration document.
|
||||
pub use self::registry::FILE_ID_STD_OFFCHAIN_TRANSPORT;
|
||||
/// Logical file identifier for the standard HTTP + WebSocket + Yellowstone gRPC Transport configuration document.
|
||||
pub use self::registry::FILE_ID_STD_TRANSPORT;
|
||||
/// Logical file identifier for the standard Wallet configuration document.
|
||||
|
||||
499
crates/ksp-config-lib/src/offchain_transport.rs
Normal file
499
crates/ksp-config-lib/src/offchain_transport.rs
Normal file
@@ -0,0 +1,499 @@
|
||||
// file: crates/ksp-config-lib/src/offchain_transport.rs
|
||||
// version: 1
|
||||
|
||||
//! Adapter from Config-owned Off-chain Transport documents to the provider-agnostic market-price runtime service.
|
||||
|
||||
/// Effective standard Off-chain Transport configuration resolved from Config.
|
||||
pub struct ResolvedOffchainTransportConfig {
|
||||
effective: crate::ResolvedConfigJson,
|
||||
file_id: crate::ConfigFileId,
|
||||
profile_id: String,
|
||||
selection_source: crate::ConfigProfileSelectionSource,
|
||||
service: ksp_offchain_transport_lib::MarketPriceService,
|
||||
source_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl crate::ResolvedOffchainTransportConfig {
|
||||
/// Returns the detailed environment-resolved effective Config view with secret-safe diagnostics.
|
||||
#[must_use]
|
||||
pub const fn effective(&self) -> &crate::ResolvedConfigJson {
|
||||
return &self.effective;
|
||||
}
|
||||
|
||||
/// Returns the logical Config document identifier used by this runtime configuration.
|
||||
#[must_use]
|
||||
pub const fn file_id(&self) -> &crate::ConfigFileId {
|
||||
return &self.file_id;
|
||||
}
|
||||
|
||||
/// Returns the selected standard Off-chain Transport profile identifier.
|
||||
#[must_use]
|
||||
pub fn profile_id(&self) -> &str {
|
||||
return self.profile_id.as_str();
|
||||
}
|
||||
|
||||
/// Returns the source that selected the standard Off-chain Transport profile.
|
||||
#[must_use]
|
||||
pub const fn selection_source(&self) -> crate::ConfigProfileSelectionSource {
|
||||
return self.selection_source;
|
||||
}
|
||||
|
||||
/// Returns the provider-agnostic market-price runtime service.
|
||||
#[must_use]
|
||||
pub const fn service(&self) -> &ksp_offchain_transport_lib::MarketPriceService {
|
||||
return &self.service;
|
||||
}
|
||||
|
||||
/// Returns the physical source Config document path.
|
||||
#[must_use]
|
||||
pub fn source_path(&self) -> &std::path::Path {
|
||||
return self.source_path.as_path();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for crate::ResolvedOffchainTransportConfig {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let registry = self.service.registry();
|
||||
return formatter
|
||||
.debug_struct("ResolvedOffchainTransportConfig")
|
||||
.field("effective", &self.effective)
|
||||
.field("file_id", &self.file_id)
|
||||
.field("profile_id", &self.profile_id)
|
||||
.field("provider_registry", ®istry)
|
||||
.field("selection_source", &self.selection_source)
|
||||
.field("source_path", &self.source_path)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ConfigDocumentEngine {
|
||||
/// Loads the standard Off-chain Transport document and maps one profile to the generic market-price runtime service.
|
||||
pub fn load_resolved_offchain_transport_config(
|
||||
&self,
|
||||
requested_profile: std::option::Option<&str>,
|
||||
environment: &crate::ConfigEnvironment,
|
||||
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
||||
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_OFFCHAIN_TRANSPORT);
|
||||
let file_id = match file_id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let profile = self.load_resolved_profile(&file_id, requested_profile);
|
||||
let profile = match profile {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return resolve_offchain_transport_profile(&profile, environment);
|
||||
}
|
||||
|
||||
/// Maps an already resolved standard Off-chain Transport profile to the runtime service while preserving composite selection provenance.
|
||||
pub fn resolve_offchain_transport_config_profile(
|
||||
&self,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
environment: &crate::ConfigEnvironment,
|
||||
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
||||
if profile.file_id().as_str() != crate::FILE_ID_STD_OFFCHAIN_TRANSPORT {
|
||||
return std::result::Result::Err(effective_error(profile, "resolved Config profile does not reference the standard Off-chain Transport document"));
|
||||
}
|
||||
let descriptor = self.registry().descriptor(profile.file_id());
|
||||
if let std::result::Result::Err(error) = descriptor {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return resolve_offchain_transport_profile(profile, environment);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveOffchainTransportSource {
|
||||
format_version: u32,
|
||||
market_price: EffectiveMarketPriceSource,
|
||||
profile_id: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveMarketPriceSource {
|
||||
birdeye: EffectiveBirdeyeSource,
|
||||
coinbase_exchange: EffectiveEnabledSource,
|
||||
coingecko: EffectiveCoinGeckoSource,
|
||||
coinmarketcap: EffectiveCoinMarketCapSource,
|
||||
coinpaprika: EffectiveEnabledSource,
|
||||
dexscreener: EffectiveDexScreenerSource,
|
||||
jupiter: EffectiveJupiterSource,
|
||||
kraken: EffectiveEnabledSource,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveEnabledSource {
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveBirdeyeSource {
|
||||
api_key: std::option::Option<String>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveCoinGeckoSource {
|
||||
access_mode: ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode,
|
||||
api_key: std::option::Option<String>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveCoinMarketCapSource {
|
||||
access_mode: ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode,
|
||||
api_key: std::option::Option<String>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveDexScreenerSource {
|
||||
enabled: bool,
|
||||
sol_usd_pair_address: std::option::Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct EffectiveJupiterSource {
|
||||
access_mode: ksp_offchain_transport_lib::MarketPriceJupiterAccessMode,
|
||||
api_key: std::option::Option<String>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
fn resolve_offchain_transport_profile(
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
environment: &crate::ConfigEnvironment,
|
||||
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
||||
ksp_logging_lib::trace!(
|
||||
target: crate::TRACING_TARGET,
|
||||
profile_id = profile.profile_id(),
|
||||
"mapping standard Off-chain Transport Config profile"
|
||||
);
|
||||
let effective = profile.resolve_effective_environment_detailed(environment);
|
||||
let effective = match effective {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source = serde_json::from_value::<EffectiveOffchainTransportSource>(effective.value().clone());
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "effective Off-chain Transport Config cannot be decoded into the runtime adapter contract").with_source(error),
|
||||
);
|
||||
},
|
||||
};
|
||||
if source.format_version != 1 {
|
||||
return std::result::Result::Err(effective_error(profile, "effective Off-chain Transport format_version is unsupported"));
|
||||
}
|
||||
if source.profile_id != profile.profile_id() {
|
||||
return std::result::Result::Err(effective_error(profile, "effective Off-chain Transport profile_id does not match the selected profile"));
|
||||
}
|
||||
let setups = map_market_price_setups(source.market_price, &effective, profile);
|
||||
let setups = match setups {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let service = ksp_offchain_transport_lib::MarketPriceService::new(setups);
|
||||
let service = match service {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(offchain_contract_error(
|
||||
profile,
|
||||
"effective market-price providers fail the Off-chain Transport runtime contract",
|
||||
&error,
|
||||
));
|
||||
},
|
||||
};
|
||||
let provider_count = service.registry().entries().len();
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
profile_id = profile.profile_id(),
|
||||
format_version = source.format_version,
|
||||
provider_count,
|
||||
"mapped standard Off-chain Transport Config to market-price service"
|
||||
);
|
||||
return std::result::Result::Ok(crate::ResolvedOffchainTransportConfig {
|
||||
effective,
|
||||
file_id: profile.file_id().clone(),
|
||||
profile_id: profile.profile_id().to_owned(),
|
||||
selection_source: profile.selection_source(),
|
||||
service,
|
||||
source_path: profile.path().to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
fn map_market_price_setups(
|
||||
source: EffectiveMarketPriceSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<ksp_offchain_transport_lib::MarketPriceProviderSetup>> {
|
||||
let birdeye = map_birdeye(source.birdeye, effective, profile);
|
||||
let birdeye = match birdeye {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let coinbase_exchange = ksp_offchain_transport_lib::MarketPriceCoinbaseExchangeSettings::new(source.coinbase_exchange.enabled);
|
||||
let coinbase_exchange = match coinbase_exchange {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "coinbase_exchange", &error)),
|
||||
};
|
||||
let coingecko = map_coingecko(source.coingecko, effective, profile);
|
||||
let coingecko = match coingecko {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let coinmarketcap = map_coinmarketcap(source.coinmarketcap, effective, profile);
|
||||
let coinmarketcap = match coinmarketcap {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let coinpaprika = ksp_offchain_transport_lib::MarketPriceCoinPaprikaSettings::new(source.coinpaprika.enabled);
|
||||
let coinpaprika = match coinpaprika {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "coinpaprika", &error)),
|
||||
};
|
||||
let dexscreener = map_dexscreener(source.dexscreener, effective, profile);
|
||||
let dexscreener = match dexscreener {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let jupiter = map_jupiter(source.jupiter, effective, profile);
|
||||
let jupiter = match jupiter {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let kraken = ksp_offchain_transport_lib::MarketPriceKrakenSettings::new(source.kraken.enabled);
|
||||
let kraken = match kraken {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "kraken", &error)),
|
||||
};
|
||||
return std::result::Result::Ok(std::vec![
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::Birdeye(birdeye),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinbaseExchange(coinbase_exchange),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinGecko(coingecko),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinMarketCap(coinmarketcap),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinPaprika(coinpaprika),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::DexScreener(dexscreener),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::Jupiter(jupiter),
|
||||
ksp_offchain_transport_lib::MarketPriceProviderSetup::Kraken(kraken),
|
||||
]);
|
||||
}
|
||||
|
||||
fn map_birdeye(
|
||||
source: EffectiveBirdeyeSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceBirdeyeSettings> {
|
||||
if source.api_key.is_some()
|
||||
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/birdeye/api_key", profile, "birdeye")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let settings = ksp_offchain_transport_lib::MarketPriceBirdeyeSettings::new(source.enabled, source.api_key);
|
||||
return match settings {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "birdeye", &error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn map_coingecko(
|
||||
source: EffectiveCoinGeckoSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings> {
|
||||
if source.api_key.is_some()
|
||||
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/coingecko/api_key", profile, "coingecko")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let settings = match source.access_mode {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode::Demo => {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::demo(source.enabled, source.api_key)
|
||||
},
|
||||
ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode::Keyless => {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::keyless(source.enabled)
|
||||
},
|
||||
};
|
||||
return match settings {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "coingecko", &error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn map_coinmarketcap(
|
||||
source: EffectiveCoinMarketCapSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings> {
|
||||
if source.api_key.is_some()
|
||||
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/coinmarketcap/api_key", profile, "coinmarketcap")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let settings = match source.access_mode {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode::Basic => {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::basic(source.enabled, source.api_key)
|
||||
},
|
||||
ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode::Keyless => {
|
||||
ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::keyless(source.enabled)
|
||||
},
|
||||
};
|
||||
return match settings {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "coinmarketcap", &error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn map_dexscreener(
|
||||
source: EffectiveDexScreenerSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceDexScreenerSettings> {
|
||||
if source.sol_usd_pair_address.is_some()
|
||||
&& let std::result::Result::Err(error) =
|
||||
validate_public_field_provenance(effective, "/market_price/dexscreener/sol_usd_pair_address", profile, "dexscreener")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let settings = ksp_offchain_transport_lib::MarketPriceDexScreenerSettings::new(source.enabled, source.sol_usd_pair_address);
|
||||
return match settings {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "dexscreener", &error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn map_jupiter(
|
||||
source: EffectiveJupiterSource,
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceJupiterSettings> {
|
||||
if source.api_key.is_some()
|
||||
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/jupiter/api_key", profile, "jupiter")
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let settings = match source.access_mode {
|
||||
ksp_offchain_transport_lib::MarketPriceJupiterAccessMode::Free => {
|
||||
ksp_offchain_transport_lib::MarketPriceJupiterSettings::free(source.enabled, source.api_key)
|
||||
},
|
||||
ksp_offchain_transport_lib::MarketPriceJupiterAccessMode::Keyless => ksp_offchain_transport_lib::MarketPriceJupiterSettings::keyless(source.enabled),
|
||||
};
|
||||
return match settings {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "jupiter", &error)),
|
||||
};
|
||||
}
|
||||
|
||||
fn validate_secret_field_provenance(
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
pointer: &str,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
provider: &'static str,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let provenance = match effective.provenance_at(pointer) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "Off-chain Transport secret provenance is unavailable")
|
||||
.with_context("provider", provider)
|
||||
.with_context("field", pointer),
|
||||
);
|
||||
},
|
||||
};
|
||||
let mut has_secret_environment = false;
|
||||
for item in provenance {
|
||||
let variable_name = match item.variable_name() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name);
|
||||
let sensitivity = match sensitivity {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if !sensitivity.is_secret() {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "Off-chain Transport credentials may reference only secret environment variables")
|
||||
.with_context("provider", provider)
|
||||
.with_context("field", pointer),
|
||||
);
|
||||
}
|
||||
has_secret_environment = true;
|
||||
}
|
||||
if !has_secret_environment {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "Off-chain Transport credentials require secret environment provenance")
|
||||
.with_context("provider", provider)
|
||||
.with_context("field", pointer),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn validate_public_field_provenance(
|
||||
effective: &crate::ResolvedConfigJson,
|
||||
pointer: &str,
|
||||
profile: &crate::ResolvedConfigProfile,
|
||||
provider: &'static str,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let provenance = match effective.provenance_at(pointer) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "Off-chain Transport public provenance is unavailable")
|
||||
.with_context("provider", provider)
|
||||
.with_context("field", pointer),
|
||||
);
|
||||
},
|
||||
};
|
||||
for item in provenance {
|
||||
let variable_name = match item.variable_name() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name);
|
||||
let sensitivity = match sensitivity {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if sensitivity != crate::ConfigSensitivity::Public {
|
||||
return std::result::Result::Err(
|
||||
effective_error(profile, "public Off-chain Transport fields may reference only public environment variables")
|
||||
.with_context("provider", provider)
|
||||
.with_context("field", pointer),
|
||||
);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn provider_contract_error(profile: &crate::ResolvedConfigProfile, provider: &'static str, error: &ksp_core_lib::Error) -> ksp_core_lib::Error {
|
||||
return offchain_contract_error(profile, "effective market-price provider settings fail the Off-chain Transport runtime contract", error)
|
||||
.with_context("provider", provider);
|
||||
}
|
||||
|
||||
fn offchain_contract_error(profile: &crate::ResolvedConfigProfile, reason: &'static str, error: &ksp_core_lib::Error) -> ksp_core_lib::Error {
|
||||
return effective_error(profile, reason)
|
||||
.with_context("offchain_transport_error_domain", error.code().domain())
|
||||
.with_context("offchain_transport_error_code", error.code().code());
|
||||
}
|
||||
|
||||
fn effective_error(profile: &crate::ResolvedConfigProfile, reason: &'static str) -> ksp_core_lib::Error {
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective Config cannot be mapped to the requested runtime contract")
|
||||
.with_context("file_id", profile.file_id().as_str())
|
||||
.with_context("profile_id", profile.profile_id())
|
||||
.with_context("reason", reason);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/offchain_transport.rs"]
|
||||
mod tests;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/src/registry.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
/// Bootstrap argument used to replace a known Config filename mapping.
|
||||
pub const ARG_FILE_MAP: &str = "--filemap";
|
||||
@@ -11,6 +11,10 @@ pub const DEFAULT_COMPOSITE_SCHEMA_FILENAME: &str = "composite.schema.json";
|
||||
pub const DEFAULT_STD_LOGGING_FILENAME: &str = "std.logging.json";
|
||||
/// Default physical filename for the standard Logging JSON Schema document.
|
||||
pub const DEFAULT_STD_LOGGING_SCHEMA_FILENAME: &str = "std.logging.schema.json";
|
||||
/// Default physical filename for the standard Off-chain Transport configuration document.
|
||||
pub const DEFAULT_STD_OFFCHAIN_TRANSPORT_FILENAME: &str = "std.offchain_transport.json";
|
||||
/// Default physical filename for the standard Off-chain Transport JSON Schema document.
|
||||
pub const DEFAULT_STD_OFFCHAIN_TRANSPORT_SCHEMA_FILENAME: &str = "std.offchain_transport.schema.json";
|
||||
/// Default physical filename for the standard HTTP + WebSocket + Yellowstone gRPC Transport configuration document.
|
||||
pub const DEFAULT_STD_TRANSPORT_FILENAME: &str = "std.transport.json";
|
||||
/// Default physical filename for the standard HTTP + WebSocket + Yellowstone gRPC Transport JSON Schema document.
|
||||
@@ -25,12 +29,16 @@ pub const FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK: &str = "cfg.composite.ksp-app-w
|
||||
pub const FILE_ID_SCHEMA_COMPOSITE: &str = "schema.composite";
|
||||
/// Logical file identifier for the standard Logging JSON Schema document.
|
||||
pub const FILE_ID_SCHEMA_STD_LOGGING: &str = "schema.std.logging";
|
||||
/// Logical file identifier for the standard Off-chain Transport JSON Schema document.
|
||||
pub const FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT: &str = "schema.std.offchain_transport";
|
||||
/// Logical file identifier for the standard HTTP + WebSocket + Yellowstone gRPC Transport JSON Schema document.
|
||||
pub const FILE_ID_SCHEMA_STD_TRANSPORT: &str = "schema.std.transport";
|
||||
/// Logical file identifier for the standard Wallet JSON Schema document.
|
||||
pub const FILE_ID_SCHEMA_STD_WALLET: &str = "schema.std.wallet";
|
||||
/// Logical file identifier for the standard Logging configuration document.
|
||||
pub const FILE_ID_STD_LOGGING: &str = "cfg.std.logging";
|
||||
/// Logical file identifier for the standard Off-chain Transport configuration document.
|
||||
pub const FILE_ID_STD_OFFCHAIN_TRANSPORT: &str = "cfg.std.offchain_transport";
|
||||
/// Logical file identifier for the standard HTTP + WebSocket + Yellowstone gRPC Transport configuration document.
|
||||
pub const FILE_ID_STD_TRANSPORT: &str = "cfg.std.transport";
|
||||
/// Logical file identifier for the standard Wallet configuration document.
|
||||
@@ -172,6 +180,26 @@ impl ConfigFileRegistry {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let offchain_transport = ConfigFileDescriptor::new(
|
||||
FILE_ID_STD_OFFCHAIN_TRANSPORT,
|
||||
ConfigFileKind::Config,
|
||||
DEFAULT_STD_OFFCHAIN_TRANSPORT_FILENAME,
|
||||
std::option::Option::Some(FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT),
|
||||
);
|
||||
let offchain_transport = match offchain_transport {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let offchain_transport_schema = ConfigFileDescriptor::new(
|
||||
FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT,
|
||||
ConfigFileKind::Schema,
|
||||
DEFAULT_STD_OFFCHAIN_TRANSPORT_SCHEMA_FILENAME,
|
||||
std::option::Option::None,
|
||||
);
|
||||
let offchain_transport_schema = match offchain_transport_schema {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transport = ConfigFileDescriptor::new(
|
||||
FILE_ID_STD_TRANSPORT,
|
||||
ConfigFileKind::Config,
|
||||
@@ -204,7 +232,18 @@ impl ConfigFileRegistry {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return crate::build_registry([composite, composite_schema, logging, logging_schema, transport, transport_schema, wallet, wallet_schema]);
|
||||
return crate::build_registry([
|
||||
composite,
|
||||
composite_schema,
|
||||
logging,
|
||||
logging_schema,
|
||||
offchain_transport,
|
||||
offchain_transport_schema,
|
||||
transport,
|
||||
transport_schema,
|
||||
wallet,
|
||||
wallet_schema,
|
||||
]);
|
||||
}
|
||||
|
||||
/// Creates the default registry and applies repeatable `--filemap=<file_id>=<filename>` overrides from raw process arguments.
|
||||
|
||||
Reference in New Issue
Block a user