// file: crates/ksp-app-backfill-desk/src/transport_runtime.rs // version: 6 //! Composite-selected HTTP Transport readiness and route inventory owned by Backfill Desk. /// Safe and executable Transport runtime retained by the application state. pub(crate) struct TransportRuntime { pool: ksp_onchain_transport_lib::HttpTransportPool, profile_id: String, } impl TransportRuntime { /// Returns a shareable clone of the Transport-owned HTTP pool for one admitted Backfill execution. #[must_use] pub(crate) fn pool(&self) -> ksp_onchain_transport_lib::HttpTransportPool { return self.pool.clone(); } /// Returns the composite-selected Transport profile identifier. #[must_use] pub(crate) fn profile_id(&self) -> &str { return self.profile_id.as_str(); } /// Returns the one configured Transport network only when the active HTTP pool is logically coherent. #[must_use] pub(crate) fn coherent_network(&self) -> std::option::Option { let networks = configured_networks(&self.pool.snapshot()); if networks.len() != 1 { return std::option::Option::None; } return networks.into_iter().next(); } /// Builds the current safe Transport-only projection for the Backfill options surface. pub(crate) fn options(&self) -> ksp_core_lib::Result { let snapshot = self.pool.snapshot(); let configured_networks = configured_networks(&snapshot); let http_routes = compatible_backfill_http_routes(&self.pool, &snapshot); let http_routes = match http_routes { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let limits = crate::backfill_request_limits(); let limits = match limits { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let transport_ready = configured_networks.len() == 1 && !http_routes.is_empty() && snapshot.available_endpoint_count() > 0; return std::result::Result::Ok(crate::BackfillDeskOptionsDto { commitments: crate::backfill_commitment_codes(), composition_ready: false, configured_networks, http_routes, limits, network_coherent: false, program_id_options: crate::program_id_autocomplete_options(), scope_kinds: crate::backfill_scope_kind_codes(), store_diagnostic: std::option::Option::None, store_network: std::option::Option::None, store_ready: false, transport_diagnostic: std::option::Option::None, transport_ready, }); } } /// Resolves the composite-selected Transport profile and builds the executable HTTP pool. pub(crate) fn initialize_transport(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result { let environment = ksp_config_lib::ConfigEnvironment::load(); let environment = match environment { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let composite = crate::load_backfill_desk_composite(management); let composite = match composite { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT); let profile = match profile { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let resolved = management.engine().resolve_transport_config_profile(&profile, &environment); let resolved = match resolved { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let profile_id = resolved.profile_id().to_owned(); let pool = ksp_onchain_transport_lib::HttpTransportPool::new(resolved.into_settings()); let pool = match pool { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let runtime = TransportRuntime { pool, profile_id }; let options = runtime.options(); let options = match options { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; ksp_logging_lib::debug!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_TRANSPORT, transport_profile = runtime.profile_id(), network_count = options.configured_networks.len(), http_route_count = options.http_routes.len(), transport_ready = options.transport_ready, "initialized Backfill Desk HTTP Transport readiness from composite-managed configuration" ); return std::result::Result::Ok(runtime); } fn compatible_backfill_http_routes( pool: &ksp_onchain_transport_lib::HttpTransportPool, snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot, ) -> ksp_core_lib::Result> { let signatures = required_http_rpc_method("getSignaturesForAddress"); let signatures = match signatures { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let transaction = required_http_rpc_method("getTransaction"); let transaction = match transaction { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mut candidates = std::collections::BTreeMap::>::new(); for endpoint in snapshot.endpoints() { if !endpoint.enabled() { continue; } for role in endpoint.roles() { if role.enabled() { candidates.entry(role.role().to_owned()).or_default().insert(endpoint.provider().to_owned()); } } } let mut compatible = std::vec::Vec::new(); for (candidate, providers) in candidates { let role = ksp_onchain_transport_lib::HttpRoleName::new(candidate.clone()); if pool.select_for_method(&role, signatures).is_ok() && pool.select_for_method(&role, transaction).is_ok() { let providers = providers.into_iter().collect::>(); compatible.push(crate::BackfillHttpRouteOptionDto { pooled: providers.len() > 1, providers, role: candidate }); } } return std::result::Result::Ok(compatible); } fn configured_networks(snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot) -> std::vec::Vec { let mut values = snapshot .endpoints() .iter() .filter_map(|endpoint| { if endpoint.enabled() { return std::option::Option::Some(endpoint.cluster().to_owned()); } return std::option::Option::None; }) .collect::>(); values.sort(); values.dedup(); return values; } fn required_http_rpc_method(method: &'static str) -> ksp_core_lib::Result<&'static ksp_onchain_transport_lib::HttpRpcMethodDescriptor> { let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method); return match descriptor { std::option::Option::Some(value) => std::result::Result::Ok(value), std::option::Option::None => std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_TRANSPORT_READINESS_INVALID, "Backfill Desk Transport registry is missing a required HTTP RPC method") .with_context("rpc_method", method), ), }; } #[cfg(test)] #[path = "../unit_tests/transport_runtime.rs"] mod tests;