180 lines
7.9 KiB
Rust
180 lines
7.9 KiB
Rust
// 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<String> {
|
|
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<crate::BackfillDeskOptionsDto> {
|
|
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<TransportRuntime> {
|
|
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<std::vec::Vec<crate::BackfillHttpRouteOptionDto>> {
|
|
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::<String, std::collections::BTreeSet<String>>::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::<std::vec::Vec<_>>();
|
|
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<String> {
|
|
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::<std::vec::Vec<_>>();
|
|
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;
|