v0.3.15-pre.007
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# file: crates/ksp-app-raw-transaction-ingest-desk/Cargo.toml
|
||||
# version: 2
|
||||
# version: 3
|
||||
|
||||
[package]
|
||||
name = "ksp-app-raw-transaction-ingest-desk"
|
||||
@@ -28,6 +28,7 @@ ksp-config-lib = { path = "../ksp-config-lib" }
|
||||
ksp-core-lib = { path = "../ksp-core-lib" }
|
||||
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
||||
ksp-onchain-transport-lib = { path = "../ksp-onchain-transport-lib" }
|
||||
ksp-worker-raw-transaction-ingest-lib = { path = "../ksp-worker-raw-transaction-ingest-lib" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tauri.workspace = true
|
||||
tauri-plugin-tracing.workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/README.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# `ksp-app-raw-transaction-ingest-desk`
|
||||
|
||||
@@ -20,13 +20,15 @@ bin : ksp-app-raw-transaction-ingest-desk
|
||||
```text
|
||||
Config -> composite, profils, secrets et résolution effective
|
||||
Transport -> capabilities HTTP/WS/gRPC et ressources physiques
|
||||
Desk -> catalogue logique, inventaire Config-only et projections sûres
|
||||
Desk -> catalogue logique, inventaire Config-only et reconstruction Start sans I/O
|
||||
Store -> persistance via la façade KSP
|
||||
Worker -> acquisition continue et lifecycle d'exécution
|
||||
Worker -> validation des contrats source, acquisition continue et lifecycle d'exécution
|
||||
```
|
||||
|
||||
L'inventaire de routes est calculé côté Rust à partir du composite Config validé et des settings Transport/Store résolus. Il ne lance aucun probe réseau et n'ouvre ni Store ni Worker.
|
||||
|
||||
Avant le Start réel, le backend peut revalider une sélection logique par `profile_id + route_id + inventory_generation + commitment`. Cette prévalidation recharge Config, reproouve le réseau Store/Transport, reconstruit le pool HTTP ou l'endpoint WS/gRPC exact requis et passe par le constructeur public de la source `ksp-worker-raw-transaction-ingest-lib`. La ressource ainsi reconstruite est immédiatement détruite : aucun socket n'est ouvert, aucun Store n'est ouvert et aucun Worker n'est lancé dans cette tranche.
|
||||
|
||||
Les profils applicatifs sont réseau-centriques (`devnet`, `mainnet`, `testnet`). Un profil peut agréger plusieurs profils Transport du même réseau : le provider reste une source de capability et ne devient jamais une identité réseau ni un choix de profil applicatif.
|
||||
Une route explicitement liée à un provider n’est projetée pour un réseau que si le composite déclare une source Transport de ce provider. Ainsi, la route Helius Transaction existe sur Devnet/Mainnet mais n’est pas présentée sur Testnet tant qu’Helius ne fournit pas de source Testnet configurée.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/app_state.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Shared backend state owned by the Raw Transaction Ingest Desk Tauri application.
|
||||
|
||||
@@ -98,6 +98,24 @@ impl crate::AppState {
|
||||
return std::result::Result::Ok(inventory);
|
||||
}
|
||||
|
||||
/// Revalidates one future Start request and reconstructs its exact Transport/Worker source contract without launching a Worker.
|
||||
pub(crate) fn validate_route_start(
|
||||
&self,
|
||||
request: &crate::RawIngestRouteStartRequestDto,
|
||||
) -> ksp_core_lib::Result<crate::RawIngestRouteStartValidationDto> {
|
||||
let generation = self.inventory_generation.lock();
|
||||
let generation = match generation {
|
||||
std::result::Result::Ok(value) => *value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
||||
"Raw Transaction Ingest Desk route inventory generation lock is poisoned",
|
||||
));
|
||||
},
|
||||
};
|
||||
return crate::validate_route_start(&self.config_management, generation, request);
|
||||
}
|
||||
|
||||
/// Builds the safe scaffold status exposed by the shell.
|
||||
pub(crate) fn shell_status(&self) -> ksp_core_lib::Result<crate::ShellStatusDto> {
|
||||
let document_count = self.config_management.engine().registry().descriptors().count();
|
||||
@@ -131,7 +149,7 @@ impl crate::AppState {
|
||||
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
config_document_count: document_count,
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
shell_phase: "pre.006-route-inventory".to_owned(),
|
||||
shell_phase: "pre.007-start-resource-revalidation".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/constants.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Application-owned tracing targets and Config component identifiers.
|
||||
|
||||
@@ -21,6 +21,8 @@ pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "raw_transaction_ingest.bootst
|
||||
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||
/// Structured domain used while rebuilding Config-only route inventory.
|
||||
pub(crate) const TRACING_DOMAIN_INVENTORY: &str = "raw_transaction_ingest.inventory";
|
||||
/// Structured domain used while revalidating future Start requests and reconstructing route resources.
|
||||
pub(crate) const TRACING_DOMAIN_ROUTE_START: &str = "raw_transaction_ingest.route_start";
|
||||
/// Structured domain used by the Raw Transaction Ingest Desk shell.
|
||||
pub(crate) const TRACING_DOMAIN_SHELL: &str = "raw_transaction_ingest.shell";
|
||||
/// Structured domain used by Tauri window lifecycle operations.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/dto_route.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Safe route-identity, Config-composability and lifecycle DTOs for Raw Transaction Ingest Desk.
|
||||
|
||||
use ts_rs::TS; // rust-rules: trait-import
|
||||
|
||||
/// Stable logical V1 route identifiers owned by Raw Transaction Ingest Desk.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, TS)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteId.ts")]
|
||||
pub(crate) enum RawIngestRouteId {
|
||||
@@ -26,7 +26,13 @@ impl crate::RawIngestRouteId {
|
||||
/// Returns all V1 route identifiers in stable UI order.
|
||||
#[must_use]
|
||||
pub(crate) fn all() -> std::vec::Vec<Self> {
|
||||
return vec![Self::YellowstoneHydrated, Self::StandardLogsHydrated, Self::StandardBlockDirect, Self::HeliusTransactionHydrated, Self::HttpBlockPolling];
|
||||
return vec![
|
||||
Self::YellowstoneHydrated,
|
||||
Self::StandardLogsHydrated,
|
||||
Self::StandardBlockDirect,
|
||||
Self::HeliusTransactionHydrated,
|
||||
Self::HttpBlockPolling,
|
||||
];
|
||||
}
|
||||
|
||||
/// Returns the application-owned route family used by safe UI projections.
|
||||
@@ -52,6 +58,81 @@ impl crate::RawIngestRouteId {
|
||||
Self::HttpBlockPolling => "HTTP Block Polling",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the stable kebab-case route identifier used by safe logs and IPC.
|
||||
#[must_use]
|
||||
pub(crate) const fn as_str(self) -> &'static str {
|
||||
return match self {
|
||||
Self::YellowstoneHydrated => "yellowstone-hydrated",
|
||||
Self::StandardLogsHydrated => "standard-logs-hydrated",
|
||||
Self::StandardBlockDirect => "standard-block-direct",
|
||||
Self::HeliusTransactionHydrated => "helius-transaction-hydrated",
|
||||
Self::HttpBlockPolling => "http-block-polling",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Start-time commitments accepted by Raw Transaction Ingest Desk.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestCommitment.ts")]
|
||||
pub(crate) enum RawIngestCommitment {
|
||||
/// Confirmed commitment.
|
||||
Confirmed,
|
||||
/// Finalized commitment.
|
||||
Finalized,
|
||||
}
|
||||
|
||||
impl crate::RawIngestCommitment {
|
||||
/// Returns the stable wire label.
|
||||
#[must_use]
|
||||
pub(crate) const fn as_str(self) -> &'static str {
|
||||
return match self {
|
||||
Self::Confirmed => "confirmed",
|
||||
Self::Finalized => "finalized",
|
||||
};
|
||||
}
|
||||
|
||||
/// Maps the application-owned bounded commitment into the Transport contract.
|
||||
#[must_use]
|
||||
pub(crate) const fn into_transport(self) -> ksp_onchain_transport_lib::SolanaCommitment {
|
||||
return match self {
|
||||
Self::Confirmed => ksp_onchain_transport_lib::SolanaCommitment::Confirmed,
|
||||
Self::Finalized => ksp_onchain_transport_lib::SolanaCommitment::Finalized,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe request used to revalidate and reconstruct one future route Start without accepting physical resources from the frontend.
|
||||
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteStartRequestDto.ts")]
|
||||
pub(crate) struct RawIngestRouteStartRequestDto {
|
||||
/// Confirmed/finalized commitment selected for the future Worker.
|
||||
pub(crate) commitment: crate::RawIngestCommitment,
|
||||
/// Inventory generation observed by the caller.
|
||||
pub(crate) inventory_generation: u32,
|
||||
/// Safe logical network-profile identifier selected from the current inventory.
|
||||
pub(crate) profile_id: String,
|
||||
/// Stable logical route identifier.
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
}
|
||||
|
||||
/// Safe acknowledgement that exact Start-time Transport resources were reconstructed and validated without Worker launch.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteStartValidationDto.ts")]
|
||||
pub(crate) struct RawIngestRouteStartValidationDto {
|
||||
/// Commitment validated by the exact Worker source constructor.
|
||||
pub(crate) commitment: crate::RawIngestCommitment,
|
||||
/// Inventory generation validated against backend state.
|
||||
pub(crate) inventory_generation: u32,
|
||||
/// Logical network re-proven from Store and Transport Config.
|
||||
pub(crate) network: String,
|
||||
/// Safe logical network-profile identifier.
|
||||
pub(crate) profile_id: String,
|
||||
/// Exact logical route whose Transport resources were reconstructed.
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
}
|
||||
|
||||
/// Stable application-owned route families used only for safe presentation and orchestration.
|
||||
@@ -182,7 +263,11 @@ impl crate::RawIngestRouteDto {
|
||||
|
||||
/// Builds one route that is not composable from the current Config profile.
|
||||
#[must_use]
|
||||
pub(crate) fn unavailable(route_id: crate::RawIngestRouteId, network: std::option::Option<&str>, reason: crate::RawIngestRouteUnavailableReason) -> Self {
|
||||
pub(crate) fn unavailable(
|
||||
route_id: crate::RawIngestRouteId,
|
||||
network: std::option::Option<&str>,
|
||||
reason: crate::RawIngestRouteUnavailableReason,
|
||||
) -> Self {
|
||||
return Self {
|
||||
family: route_id.family(),
|
||||
label: route_id.label().to_owned(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/errors.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Stable Raw Transaction Ingest Desk application error codes.
|
||||
|
||||
@@ -23,6 +23,15 @@ pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode =
|
||||
/// Config-only route inventory cannot be projected safely from the validated composite.
|
||||
pub(crate) const ERROR_CODE_ROUTE_INVENTORY_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_inventory_invalid");
|
||||
/// Exact Start-time Transport resources cannot be reconstructed safely.
|
||||
pub(crate) const ERROR_CODE_ROUTE_START_PREPARATION_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_start_preparation_failed");
|
||||
/// Start request references an inventory generation that is no longer current.
|
||||
pub(crate) const ERROR_CODE_ROUTE_START_STALE_INVENTORY: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "stale_inventory");
|
||||
/// Start request references a route that is no longer Configured/applicable.
|
||||
pub(crate) const ERROR_CODE_ROUTE_START_UNAVAILABLE: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_start_unavailable");
|
||||
/// Splash readiness was invoked from a window other than the splash window.
|
||||
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "splash_origin_invalid");
|
||||
@@ -30,9 +39,11 @@ pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode =
|
||||
pub(crate) const ERROR_CODE_SPLASH_SETTING_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "splash_setting_invalid");
|
||||
/// Tauri runtime assembly or execution failed.
|
||||
pub(crate) const ERROR_CODE_TAURI_RUNTIME_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "tauri_runtime_failed");
|
||||
pub(crate) const ERROR_CODE_TAURI_RUNTIME_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "tauri_runtime_failed");
|
||||
/// A required Tauri window is missing from the configured application runtime.
|
||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "tauri_window_missing");
|
||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_MISSING: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "tauri_window_missing");
|
||||
/// A Tauri window show/focus/destroy/event operation failed.
|
||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "tauri_window_operation_failed");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/lib.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Tauri desktop application scaffold for composing and supervising KSP live RAW transaction ingest routes.
|
||||
|
||||
@@ -16,6 +16,7 @@ mod errors;
|
||||
mod frontend_logging;
|
||||
mod logging_runtime;
|
||||
mod route_inventory;
|
||||
mod route_start;
|
||||
mod splash;
|
||||
mod tauri;
|
||||
mod tw_main;
|
||||
@@ -44,10 +45,10 @@ pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_LOGGING;
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_STORE;
|
||||
/// Composite-local identifier for the standard Transport component.
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT;
|
||||
/// Composite-local optional Transport source for Helius WebSocket capabilities.
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS;
|
||||
/// Prefix used by optional same-network Transport capability sources.
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_PREFIX;
|
||||
/// Composite-local optional Transport source for Helius WebSocket capabilities.
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS;
|
||||
/// Composite-local optional Transport source for Yellowstone gRPC capabilities.
|
||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE;
|
||||
/// Structured domain used while bootstrapping Config and Logging.
|
||||
@@ -56,6 +57,8 @@ pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||
/// Structured domain used while rebuilding Config-only route inventory.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_INVENTORY;
|
||||
/// Structured domain used while revalidating future Start requests.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_ROUTE_START;
|
||||
/// Structured domain used by the Raw Transaction Ingest Desk shell.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
||||
/// Structured domain used by Tauri window lifecycle operations.
|
||||
@@ -72,6 +75,8 @@ pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH;
|
||||
pub(crate) use self::dto_common::CommandErrorDto;
|
||||
/// Safe scaffold/runtime snapshot exposed to the shell.
|
||||
pub(crate) use self::dto_common::ShellStatusDto;
|
||||
/// Bounded confirmed/finalized commitment accepted by future route Start requests.
|
||||
pub(crate) use self::dto_route::RawIngestCommitment;
|
||||
/// Safe route inventory for one logical composite profile.
|
||||
pub(crate) use self::dto_route::RawIngestProfileInventoryDto;
|
||||
/// Safe Config-only composability projection for one logical route.
|
||||
@@ -84,6 +89,10 @@ pub(crate) use self::dto_route::RawIngestRouteFoundationDto;
|
||||
pub(crate) use self::dto_route::RawIngestRouteId;
|
||||
/// Complete deterministic Config-only route inventory.
|
||||
pub(crate) use self::dto_route::RawIngestRouteInventoryDto;
|
||||
/// Safe future Start request containing only logical route selection and inventory generation.
|
||||
pub(crate) use self::dto_route::RawIngestRouteStartRequestDto;
|
||||
/// Safe acknowledgement that exact Start-time resources were reconstructed without Worker launch.
|
||||
pub(crate) use self::dto_route::RawIngestRouteStartValidationDto;
|
||||
/// Stable route lifecycle states reserved for runtime projections.
|
||||
pub(crate) use self::dto_route::RawIngestRouteState;
|
||||
/// Safe reason explaining why one logical route is not composable from Config.
|
||||
@@ -102,6 +111,12 @@ pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_TARGET_INVALID;
|
||||
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
|
||||
/// Config-only route inventory cannot be projected safely.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_INVENTORY_INVALID;
|
||||
/// Exact Start-time Transport resources cannot be reconstructed safely.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_START_PREPARATION_FAILED;
|
||||
/// Start request references a stale inventory generation.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_START_STALE_INVENTORY;
|
||||
/// Start request references a route that is no longer Configured/applicable.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_START_UNAVAILABLE;
|
||||
/// Splash readiness originated from an invalid window.
|
||||
pub(crate) use self::errors::ERROR_CODE_SPLASH_ORIGIN_INVALID;
|
||||
/// Splash timing configuration is invalid.
|
||||
@@ -120,6 +135,10 @@ pub(crate) use self::frontend_logging::emit_frontend_log_event;
|
||||
pub(crate) use self::logging_runtime::launch_identity;
|
||||
/// Rebuilds the complete safe route inventory from validated Config without runtime I/O.
|
||||
pub(crate) use self::route_inventory::build_route_inventory;
|
||||
/// Rebuilds the route inventory from one already captured Config environment snapshot.
|
||||
pub(crate) use self::route_inventory::build_route_inventory_with_environment;
|
||||
/// Revalidates one future Start request and reconstructs its exact Transport-owned Worker source contract without launch.
|
||||
pub(crate) use self::route_start::validate_route_start;
|
||||
/// Command emitted by Rust to the splash frontend.
|
||||
pub(crate) use self::splash::SplashOrderDto;
|
||||
/// Runtime timings used by the common desk splash lifecycle.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_inventory.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Config-only logical route inventory for Raw Transaction Ingest Desk.
|
||||
|
||||
@@ -48,7 +48,10 @@ impl RouteCapabilities {
|
||||
}
|
||||
|
||||
/// Rebuilds the complete safe route inventory from validated Config without opening any network, Store or Worker resource.
|
||||
pub(crate) fn build_route_inventory(management: &ksp_config_lib::ConfigManagement, generation: u32) -> ksp_core_lib::Result<crate::RawIngestRouteInventoryDto> {
|
||||
pub(crate) fn build_route_inventory(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
generation: u32,
|
||||
) -> ksp_core_lib::Result<crate::RawIngestRouteInventoryDto> {
|
||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||
let environment = match environment {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -57,7 +60,8 @@ pub(crate) fn build_route_inventory(management: &ksp_config_lib::ConfigManagemen
|
||||
return build_route_inventory_with_environment(management, &environment, generation);
|
||||
}
|
||||
|
||||
fn build_route_inventory_with_environment(
|
||||
/// Rebuilds the complete safe route inventory from one already captured Config environment snapshot.
|
||||
pub(crate) pub(crate) fn build_route_inventory_with_environment(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
generation: u32,
|
||||
@@ -69,7 +73,12 @@ fn build_route_inventory_with_environment(
|
||||
};
|
||||
let mut profiles = std::vec::Vec::with_capacity(catalog.profile_ids.len());
|
||||
for profile_id in &catalog.profile_ids {
|
||||
profiles.push(build_profile_inventory(management, environment, profile_id.as_str(), profile_id == &catalog.default_profile));
|
||||
profiles.push(build_profile_inventory(
|
||||
management,
|
||||
environment,
|
||||
profile_id.as_str(),
|
||||
profile_id == &catalog.default_profile,
|
||||
));
|
||||
}
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
@@ -155,8 +164,11 @@ fn build_profile_inventory(
|
||||
},
|
||||
};
|
||||
let network = store.settings().network().as_str().to_owned();
|
||||
let base_transport_profile =
|
||||
crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT);
|
||||
let base_transport_profile = crate::required_composite_component_profile(
|
||||
&composite,
|
||||
crate::COMPOSITE_COMPONENT_ID_TRANSPORT,
|
||||
ksp_config_lib::FILE_ID_STD_TRANSPORT,
|
||||
);
|
||||
let base_transport_profile = match base_transport_profile {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
@@ -172,7 +184,12 @@ fn build_profile_inventory(
|
||||
let base_transport = match base_transport {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return unavailable_profile(profile_id, is_default, std::option::Option::Some(network.as_str()), resolution_error_reason(&error));
|
||||
return unavailable_profile(
|
||||
profile_id,
|
||||
is_default,
|
||||
std::option::Option::Some(network.as_str()),
|
||||
resolution_error_reason(&error),
|
||||
);
|
||||
},
|
||||
};
|
||||
if !transport_matches_network(&base_transport, network.as_str()) {
|
||||
|
||||
493
crates/ksp-app-raw-transaction-ingest-desk/src/route_start.rs
Normal file
493
crates/ksp-app-raw-transaction-ingest-desk/src/route_start.rs
Normal file
@@ -0,0 +1,493 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_start.rs
|
||||
// version: 1
|
||||
|
||||
//! Start-time Config revalidation and Transport-resource reconstruction without Worker launch.
|
||||
|
||||
/// Revalidates one future Start request against the current inventory generation and reconstructs the exact Worker source contract without starting it.
|
||||
pub(crate) fn validate_route_start(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
current_generation: u32,
|
||||
request: &crate::RawIngestRouteStartRequestDto,
|
||||
) -> ksp_core_lib::Result<crate::RawIngestRouteStartValidationDto> {
|
||||
if current_generation == 0 || request.inventory_generation != current_generation {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_STALE_INVENTORY,
|
||||
"Raw Transaction Ingest Desk Start request uses a stale route inventory generation",
|
||||
));
|
||||
}
|
||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||
let environment = match environment {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time Config environment", error),
|
||||
};
|
||||
let inventory = crate::build_route_inventory_with_environment(management, &environment, current_generation);
|
||||
let inventory = match inventory {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot rebuild Start-time route inventory", error),
|
||||
};
|
||||
let profile = inventory.profiles.iter().find(|profile| return profile.profile_id == request.profile_id);
|
||||
let profile = match profile {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return route_unavailable_error(),
|
||||
};
|
||||
let route = profile.routes.iter().find(|route| return route.route_id == request.route_id);
|
||||
let route = match route {
|
||||
std::option::Option::Some(value) if value.selectable && value.state == crate::RawIngestRouteState::Configured => value,
|
||||
std::option::Option::Some(_) | std::option::Option::None => return route_unavailable_error(),
|
||||
};
|
||||
let network = match route.network.as_deref() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return route_unavailable_error(),
|
||||
};
|
||||
let composite = crate::load_raw_transaction_ingest_desk_composite_profile(management, std::option::Option::Some(request.profile_id.as_str()));
|
||||
let composite = match composite {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time composite profile", error),
|
||||
};
|
||||
let store_profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_STORE, ksp_config_lib::FILE_ID_STD_STORE);
|
||||
let store_profile = match store_profile {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time Store profile", error),
|
||||
};
|
||||
let store = management.engine().resolve_store_config_profile(&store_profile, &environment);
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time Store settings", error),
|
||||
};
|
||||
if store.settings().network().as_str() != network {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED,
|
||||
"Raw Transaction Ingest Desk Start-time Store network no longer matches the selected route",
|
||||
));
|
||||
}
|
||||
let transport_profile = crate::required_composite_component_profile(
|
||||
&composite,
|
||||
crate::COMPOSITE_COMPONENT_ID_TRANSPORT,
|
||||
ksp_config_lib::FILE_ID_STD_TRANSPORT,
|
||||
);
|
||||
let transport_profile = match transport_profile {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time base Transport profile", error),
|
||||
};
|
||||
let transport = management.engine().resolve_transport_config_profile(&transport_profile, &environment);
|
||||
let transport = match transport {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return route_start_error("Cannot resolve Start-time base Transport settings", error),
|
||||
};
|
||||
if !transport_matches_network(&transport, network) {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED,
|
||||
"Raw Transaction Ingest Desk Start-time Transport network no longer matches the selected route",
|
||||
));
|
||||
}
|
||||
let commitment = request.commitment.into_transport();
|
||||
let prepared = match request.route_id {
|
||||
crate::RawIngestRouteId::YellowstoneHydrated => prepare_yellowstone(management, &environment, &composite, &transport, network, commitment),
|
||||
crate::RawIngestRouteId::StandardLogsHydrated => prepare_standard_logs(&transport, network, commitment),
|
||||
crate::RawIngestRouteId::StandardBlockDirect => prepare_standard_block(&transport, network, commitment),
|
||||
crate::RawIngestRouteId::HeliusTransactionHydrated => prepare_helius(management, &environment, &composite, &transport, network, commitment),
|
||||
crate::RawIngestRouteId::HttpBlockPolling => prepare_http_polling(&transport, network, commitment),
|
||||
};
|
||||
if let std::result::Result::Err(error) = prepared {
|
||||
return route_start_error("Cannot reconstruct Start-time Transport resources", error);
|
||||
}
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_START,
|
||||
profile_id = request.profile_id.as_str(),
|
||||
network,
|
||||
route_id = request.route_id.as_str(),
|
||||
inventory_generation = request.inventory_generation,
|
||||
commitment = request.commitment.as_str(),
|
||||
"revalidated Start request and reconstructed exact Transport-owned Worker source contract without runtime I/O"
|
||||
);
|
||||
return std::result::Result::Ok(crate::RawIngestRouteStartValidationDto {
|
||||
commitment: request.commitment,
|
||||
inventory_generation: request.inventory_generation,
|
||||
network: network.to_owned(),
|
||||
profile_id: request.profile_id.clone(),
|
||||
route_id: request.route_id,
|
||||
});
|
||||
}
|
||||
|
||||
fn prepare_standard_logs(
|
||||
transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let endpoint = single_ws_endpoint(
|
||||
transport,
|
||||
network,
|
||||
ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard,
|
||||
ksp_onchain_transport_lib::WsSubscriptionKind::Logs,
|
||||
);
|
||||
let endpoint = match endpoint {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let role = single_http_role(transport.http_settings(), network, &["getTransaction"]);
|
||||
let role = match role {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(transport.http_settings().clone());
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestStandardLogsSource::new(
|
||||
endpoint,
|
||||
ksp_onchain_transport_lib::SolanaLogsSubscribeFilter::AllWithVotes,
|
||||
commitment,
|
||||
pool,
|
||||
role,
|
||||
);
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::mem::drop(source);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn prepare_standard_block(
|
||||
transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let endpoint = single_ws_endpoint(
|
||||
transport,
|
||||
network,
|
||||
ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard,
|
||||
ksp_onchain_transport_lib::WsSubscriptionKind::Block,
|
||||
);
|
||||
let endpoint = match endpoint {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestStandardBlockSource::new(
|
||||
endpoint,
|
||||
ksp_onchain_transport_lib::SolanaBlockSubscribeFilter::All,
|
||||
commitment,
|
||||
);
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::mem::drop(source);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn prepare_helius(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
composite: &ksp_config_lib::ResolvedConfigComposite,
|
||||
base_transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let transport = resolve_optional_transport(management, environment, composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS, network);
|
||||
let transport = match transport {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let endpoint = single_ws_endpoint(
|
||||
&transport,
|
||||
network,
|
||||
ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream,
|
||||
ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction,
|
||||
);
|
||||
let endpoint = match endpoint {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let role = single_http_role(base_transport.http_settings(), network, &["getTransaction"]);
|
||||
let role = match role {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(base_transport.http_settings().clone());
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter = ksp_onchain_transport_lib::HeliusTransactionSubscribeFilter::new(
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
let source = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestHeliusTransactionSource::new(endpoint, filter, commitment, pool, role);
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::mem::drop(source);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn prepare_yellowstone(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
composite: &ksp_config_lib::ResolvedConfigComposite,
|
||||
base_transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let transport = resolve_optional_transport(management, environment, composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE, network);
|
||||
let transport = match transport {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let endpoint = single_grpc_endpoint(&transport, network);
|
||||
let endpoint = match endpoint {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let channel = ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint);
|
||||
let channel = match channel {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_name = ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("raw-ingest-all-transactions");
|
||||
let filter_name = match filter_name {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
|
||||
let inserted = request.insert_transaction_filter(filter_name, ksp_onchain_transport_lib::YellowstoneSubscribeTransactionFilter::new());
|
||||
if let std::result::Result::Err(error) = inserted {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
request.set_commitment(std::option::Option::Some(commitment));
|
||||
let role = single_http_role(base_transport.http_settings(), network, &["getTransaction"]);
|
||||
let role = match role {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(base_transport.http_settings().clone());
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestYellowstoneSource::new(channel, request, pool, role);
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::mem::drop(source);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn prepare_http_polling(
|
||||
transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let role = single_http_role(transport.http_settings(), network, &["getSlot", "getBlocksWithLimit", "getBlock"]);
|
||||
let role = match role {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(transport.http_settings().clone());
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let source = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestHttpBlockPollingSource::new(pool, role, commitment);
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
std::mem::drop(source);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn resolve_optional_transport(
|
||||
management: &ksp_config_lib::ConfigManagement,
|
||||
environment: &ksp_config_lib::ConfigEnvironment,
|
||||
composite: &ksp_config_lib::ResolvedConfigComposite,
|
||||
component_id: &str,
|
||||
network: &str,
|
||||
) -> ksp_core_lib::Result<ksp_config_lib::ResolvedTransportConfig> {
|
||||
let component = composite.component(component_id);
|
||||
let component = match component {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED,
|
||||
"Raw Transaction Ingest Desk selected route has no declared Transport source",
|
||||
));
|
||||
},
|
||||
};
|
||||
if component.resolved().file_id().as_str() != ksp_config_lib::FILE_ID_STD_TRANSPORT {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED,
|
||||
"Raw Transaction Ingest Desk selected route references an unexpected Transport document",
|
||||
));
|
||||
}
|
||||
let resolved = management.engine().resolve_transport_config_profile(component.resolved(), environment);
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if !transport_matches_network(&resolved, network) {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED,
|
||||
"Raw Transaction Ingest Desk provider Transport network no longer matches the selected route",
|
||||
));
|
||||
}
|
||||
return std::result::Result::Ok(resolved);
|
||||
}
|
||||
|
||||
fn single_ws_endpoint(
|
||||
transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
protocol: ksp_onchain_transport_lib::WsProtocolKind,
|
||||
capability: ksp_onchain_transport_lib::WsSubscriptionKind,
|
||||
) -> ksp_core_lib::Result<ksp_onchain_transport_lib::WsEndpointSettings> {
|
||||
let settings = match transport.ws_settings() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return ambiguous_resource_error("Start-time route requires one WebSocket endpoint but none is configured"),
|
||||
};
|
||||
let matching = settings
|
||||
.endpoints()
|
||||
.iter()
|
||||
.filter(|endpoint| {
|
||||
return endpoint.enabled()
|
||||
&& endpoint.cluster().as_str() == network
|
||||
&& endpoint.protocol() == protocol
|
||||
&& endpoint.supports_subscription(capability);
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
if matching.len() != 1 {
|
||||
return ambiguous_resource_error("Start-time route requires exactly one matching WebSocket endpoint in its selected Transport profile");
|
||||
}
|
||||
return std::result::Result::Ok(matching[0].clone());
|
||||
}
|
||||
|
||||
fn single_grpc_endpoint(
|
||||
transport: &ksp_config_lib::ResolvedTransportConfig,
|
||||
network: &str,
|
||||
) -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
let settings = match transport.grpc_settings() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return ambiguous_resource_error("Start-time route requires one Yellowstone gRPC endpoint but none is configured"),
|
||||
};
|
||||
let matching = settings
|
||||
.endpoints()
|
||||
.iter()
|
||||
.filter(|endpoint| return endpoint.enabled() && endpoint.cluster().as_str() == network)
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
if matching.len() != 1 {
|
||||
return ambiguous_resource_error("Start-time route requires exactly one matching Yellowstone gRPC endpoint in its selected Transport profile");
|
||||
}
|
||||
return std::result::Result::Ok(matching[0].clone());
|
||||
}
|
||||
|
||||
fn single_http_role(
|
||||
settings: &ksp_onchain_transport_lib::HttpTransportSettings,
|
||||
network: &str,
|
||||
methods: &[&'static str],
|
||||
) -> ksp_core_lib::Result<ksp_onchain_transport_lib::HttpRoleName> {
|
||||
let mut request_kinds = std::vec::Vec::<&'static str>::with_capacity(methods.len());
|
||||
for method_name in methods {
|
||||
let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method_name);
|
||||
let descriptor = match descriptor {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return ambiguous_resource_error("Start-time route references an unknown HTTP method requirement"),
|
||||
};
|
||||
request_kinds.push(descriptor.request_kind());
|
||||
}
|
||||
let mut roles = std::collections::BTreeSet::<String>::new();
|
||||
for endpoint in settings.endpoints() {
|
||||
if !endpoint.enabled() || endpoint.cluster().as_str() != network {
|
||||
continue;
|
||||
}
|
||||
for role in endpoint.roles() {
|
||||
if role.enabled() {
|
||||
roles.insert(role.role().as_str().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
let matching = roles
|
||||
.into_iter()
|
||||
.filter(|role_name| {
|
||||
return request_kinds
|
||||
.iter()
|
||||
.all(|request_kind| return http_role_supports_request_kind(settings, network, role_name.as_str(), request_kind));
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
if matching.len() != 1 {
|
||||
return ambiguous_resource_error("Start-time route requires exactly one logical HTTP role satisfying its complete method set");
|
||||
}
|
||||
return std::result::Result::Ok(ksp_onchain_transport_lib::HttpRoleName::new(matching[0].clone()));
|
||||
}
|
||||
|
||||
fn http_role_supports_request_kind(
|
||||
settings: &ksp_onchain_transport_lib::HttpTransportSettings,
|
||||
network: &str,
|
||||
role_name: &str,
|
||||
request_kind: &str,
|
||||
) -> bool {
|
||||
for endpoint in settings.endpoints() {
|
||||
if !endpoint.enabled() || endpoint.cluster().as_str() != network {
|
||||
continue;
|
||||
}
|
||||
for role in endpoint.roles() {
|
||||
if !role.enabled() || role.role().as_str() != role_name {
|
||||
continue;
|
||||
}
|
||||
if role.request_kinds().iter().any(|kind| return kind.is_wildcard() || kind.as_str() == request_kind) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn transport_matches_network(transport: &ksp_config_lib::ResolvedTransportConfig, network: &str) -> bool {
|
||||
let mut networks = std::collections::BTreeSet::<String>::new();
|
||||
for endpoint in transport.http_settings().endpoints() {
|
||||
if endpoint.enabled() {
|
||||
networks.insert(endpoint.cluster().as_str().to_owned());
|
||||
}
|
||||
}
|
||||
if let std::option::Option::Some(settings) = transport.ws_settings() {
|
||||
for endpoint in settings.endpoints() {
|
||||
if endpoint.enabled() {
|
||||
networks.insert(endpoint.cluster().as_str().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let std::option::Option::Some(settings) = transport.grpc_settings() {
|
||||
for endpoint in settings.endpoints() {
|
||||
if endpoint.enabled() {
|
||||
networks.insert(endpoint.cluster().as_str().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
return networks.len() == 1 && networks.contains(network);
|
||||
}
|
||||
|
||||
fn route_unavailable_error<T>() -> ksp_core_lib::Result<T> {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_START_UNAVAILABLE,
|
||||
"Raw Transaction Ingest Desk selected route is no longer Configured in the current Start-time inventory",
|
||||
));
|
||||
}
|
||||
|
||||
fn ambiguous_resource_error<T>(message: &'static str) -> ksp_core_lib::Result<T> {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED, message));
|
||||
}
|
||||
|
||||
fn route_start_error<T>(message: &'static str, source: ksp_core_lib::Error) -> ksp_core_lib::Result<T> {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_START_PREPARATION_FAILED, message).with_source(source));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/route_start.rs"]
|
||||
mod tests;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Tauri runtime assembly for the KSP Raw Transaction Ingest desktop application.
|
||||
|
||||
@@ -79,7 +79,8 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
|
||||
get_route_foundation,
|
||||
get_route_inventory,
|
||||
get_runtime_status,
|
||||
splash_frontend_ready
|
||||
splash_frontend_ready,
|
||||
validate_route_start
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -139,3 +140,17 @@ async fn splash_frontend_ready(
|
||||
std::result::Result::Err(error) => std::result::Result::Err(project_command_error("splash_frontend_ready", crate::TRACING_DOMAIN_WINDOWS, &error)),
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn validate_route_start(
|
||||
request: crate::RawIngestRouteStartRequestDto,
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::RawIngestRouteStartValidationDto, crate::CommandErrorDto> {
|
||||
let result = state.validate_route_start(&request);
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => {
|
||||
std::result::Result::Err(project_command_error("validate_route_start", crate::TRACING_DOMAIN_ROUTE_START, &error))
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/tests/dependency_boundary.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Dependency-boundary canaries for Raw Transaction Ingest Desk Config-only route inventory.
|
||||
//! Dependency-boundary canaries for Raw Transaction Ingest Desk Start-resource reconstruction.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -21,51 +21,56 @@ fn read_text(path: &std::path::Path) -> String {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_manifest_opens_only_transport_for_typed_config_composability() {
|
||||
fn pre_007_manifest_adds_only_worker_vertical_to_existing_config_transport_boundary() {
|
||||
let manifest = read_text(app_root().join("Cargo.toml").as_path());
|
||||
for required in ["ksp-config-lib", "ksp-core-lib", "ksp-logging-lib", "ksp-onchain-transport-lib", "tauri-plugin-tracing"] {
|
||||
assert!(manifest.contains(required), "missing pre.006 dependency {required}");
|
||||
for required in [
|
||||
"ksp-config-lib",
|
||||
"ksp-core-lib",
|
||||
"ksp-logging-lib",
|
||||
"ksp-onchain-transport-lib",
|
||||
"ksp-worker-raw-transaction-ingest-lib",
|
||||
"tauri-plugin-tracing",
|
||||
] {
|
||||
assert!(manifest.contains(required), "missing pre.007 dependency {required}");
|
||||
}
|
||||
for forbidden in [
|
||||
"ksp-store-lib",
|
||||
"ksp-store-api",
|
||||
"ksp-store-postgres-lib",
|
||||
"ksp-worker-api",
|
||||
"ksp-worker-raw-transaction-ingest-lib",
|
||||
"ksp-job-backfill-lib",
|
||||
"reqwest",
|
||||
"tokio-postgres",
|
||||
"tonic",
|
||||
"yellowstone-grpc",
|
||||
] {
|
||||
assert!(!manifest.contains(forbidden), "pre.006 opens forbidden dependency {forbidden}");
|
||||
assert!(!manifest.contains(forbidden), "pre.007 opens forbidden direct dependency {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_production_sources_use_transport_settings_only_without_runtime_io_or_store_open() {
|
||||
let root = app_root().join("src");
|
||||
let entries = std::fs::read_dir(root.as_path());
|
||||
assert!(entries.is_ok());
|
||||
if let std::result::Result::Ok(entries) = entries {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.extension().and_then(std::ffi::OsStr::to_str) != std::option::Option::Some("rs") {
|
||||
continue;
|
||||
}
|
||||
let source = read_text(path.as_path());
|
||||
for forbidden in [
|
||||
"HttpTransportPool",
|
||||
"WsProtocolSession",
|
||||
"YellowstoneGrpcChannel",
|
||||
"Store::open",
|
||||
"RawTransactionIngestWorker",
|
||||
"RawTransactionIngestRuntimeResources",
|
||||
"start_with_runtime_resources",
|
||||
"request_stop",
|
||||
] {
|
||||
assert!(!source.contains(forbidden), "{} contains premature runtime marker {forbidden}", path.display());
|
||||
}
|
||||
}
|
||||
fn pre_007_route_start_reconstructs_exact_worker_sources_without_network_store_or_worker_launch() {
|
||||
let source = read_text(app_root().join("src/route_start.rs").as_path());
|
||||
for required in [
|
||||
"HttpTransportPool::new",
|
||||
"YellowstoneGrpcChannel::prepare",
|
||||
"RawTransactionIngestYellowstoneSource::new",
|
||||
"RawTransactionIngestStandardLogsSource::new",
|
||||
"RawTransactionIngestStandardBlockSource::new",
|
||||
"RawTransactionIngestHeliusTransactionSource::new",
|
||||
"RawTransactionIngestHttpBlockPollingSource::new",
|
||||
] {
|
||||
assert!(source.contains(required), "missing exact resource constructor {required}");
|
||||
}
|
||||
for forbidden in [
|
||||
"YellowstoneGrpcChannel::connect",
|
||||
"SolanaStandardWsSession::connect",
|
||||
"HeliusWsSession::connect",
|
||||
"Store::open",
|
||||
"RawTransactionIngestWorker::",
|
||||
"start_with_runtime_resources",
|
||||
"request_stop",
|
||||
] {
|
||||
assert!(!source.contains(forbidden), "pre.007 performs premature runtime operation {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Desktop scaffold contract canaries for Raw Transaction Ingest Desk.
|
||||
|
||||
@@ -169,3 +169,18 @@ fn pre_006_frontend_projects_profile_network_generation_and_safe_route_reasons()
|
||||
assert!(!main.contains(forbidden), "frontend exposes forbidden physical marker: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_007_start_preflight_is_backend_owned_generation_bound_and_does_not_launch_worker() {
|
||||
let dto = read_text(app_root().join("src/dto_route.rs").as_path());
|
||||
for required in
|
||||
["RawIngestCommitment", "RawIngestRouteStartRequestDto", "inventory_generation", "profile_id", "route_id", "RawIngestRouteStartValidationDto"]
|
||||
{
|
||||
assert!(dto.contains(required), "missing pre.007 Start DTO marker: {required}");
|
||||
}
|
||||
let tauri = read_text(app_root().join("src/tauri.rs").as_path());
|
||||
assert!(tauri.contains("validate_route_start"));
|
||||
for forbidden in ["start_worker", "stop_worker", "start_with_runtime_resources", "Store::open"] {
|
||||
assert!(!tauri.contains(forbidden), "pre.007 Tauri surface contains premature runtime marker {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_security.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Desktop security canaries for the Raw Transaction Ingest Desk scaffold.
|
||||
|
||||
@@ -82,3 +82,31 @@ fn pre_006_route_inventory_dto_surface_excludes_endpoint_secret_and_physical_sto
|
||||
assert!(!dto.contains(forbidden), "route DTO contains forbidden field marker {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_007_start_request_and_acknowledgement_are_logical_only_and_resource_free() {
|
||||
let dto = read_text(app_root().join("src/dto_route.rs").as_path());
|
||||
for required in ["profile_id", "route_id", "inventory_generation", "commitment", "network"] {
|
||||
assert!(dto.contains(required), "missing safe Start field marker {required}");
|
||||
}
|
||||
for forbidden in [
|
||||
"endpoint_name",
|
||||
"endpoint_url",
|
||||
"api_key",
|
||||
"connection_uri",
|
||||
"source_key",
|
||||
"secret_metadata",
|
||||
"worker_handle",
|
||||
"http_pool",
|
||||
"ws_endpoint",
|
||||
"yellowstone_channel",
|
||||
] {
|
||||
assert!(!dto.contains(forbidden), "Start DTO exposes forbidden physical marker {forbidden}");
|
||||
}
|
||||
let route_start = read_text(app_root().join("src/route_start.rs").as_path());
|
||||
assert!(route_start.contains("ERROR_CODE_ROUTE_START_STALE_INVENTORY"));
|
||||
assert!(route_start.contains("build_route_inventory_with_environment"));
|
||||
assert!(route_start.contains("resolve_store_config_profile"));
|
||||
assert!(!route_start.contains("with_context("endpoint"));
|
||||
assert!(!route_start.contains("with_context("provider"));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/tests/release_completeness.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Release-completeness canaries for Raw Transaction Ingest Desk Config-only route inventory.
|
||||
//! Release-completeness canaries for Raw Transaction Ingest Desk Start-resource reconstruction.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -21,7 +21,7 @@ fn read_text(path: &std::path::Path) -> String {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_production_module_inventory_adds_only_route_inventory_to_the_scaffold() {
|
||||
fn pre_007_production_module_inventory_adds_only_route_start_to_pre_006_surface() {
|
||||
let lib = read_text(app_root().join("src/lib.rs").as_path());
|
||||
let expected = [
|
||||
"mod app_state;",
|
||||
@@ -33,6 +33,7 @@ fn pre_006_production_module_inventory_adds_only_route_inventory_to_the_scaffold
|
||||
"mod frontend_logging;",
|
||||
"mod logging_runtime;",
|
||||
"mod route_inventory;",
|
||||
"mod route_start;",
|
||||
"mod splash;",
|
||||
"mod tauri;",
|
||||
"mod tw_main;",
|
||||
@@ -45,46 +46,44 @@ fn pre_006_production_module_inventory_adds_only_route_inventory_to_the_scaffold
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_public_surface_still_exposes_only_application_run_entry_point() {
|
||||
fn pre_007_public_surface_still_exposes_only_application_run_entry_point() {
|
||||
let lib = read_text(app_root().join("src/lib.rs").as_path());
|
||||
let public_reexports = lib.lines().filter(|line| return line.starts_with("pub use ")).collect::<std::vec::Vec<_>>();
|
||||
assert_eq!(public_reexports, vec!["pub use self::tauri::run;"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_config_inventory_keeps_start_stop_and_runtime_resources_for_later_tranches() {
|
||||
let app = read_text(app_root().join("src/app_state.rs").as_path());
|
||||
let tauri = read_text(app_root().join("src/tauri.rs").as_path());
|
||||
for forbidden in [
|
||||
"RawTransactionIngestWorker",
|
||||
"RawTransactionIngestRuntimeResources",
|
||||
"request_stop",
|
||||
"start_with_runtime_resources",
|
||||
"Store::open",
|
||||
"HttpTransportPool",
|
||||
"WsProtocolSession",
|
||||
"YellowstoneGrpcChannel",
|
||||
fn pre_007_reconstructs_all_five_worker_source_contracts_without_advancing_store_or_worker_lifecycle() {
|
||||
let route_start = read_text(app_root().join("src/route_start.rs").as_path());
|
||||
for required in [
|
||||
"RawTransactionIngestYellowstoneSource::new",
|
||||
"RawTransactionIngestStandardLogsSource::new",
|
||||
"RawTransactionIngestStandardBlockSource::new",
|
||||
"RawTransactionIngestHeliusTransactionSource::new",
|
||||
"RawTransactionIngestHttpBlockPollingSource::new",
|
||||
"SolanaLogsSubscribeFilter::AllWithVotes",
|
||||
"SolanaBlockSubscribeFilter::All",
|
||||
"YellowstoneSubscribeTransactionFilter::new",
|
||||
] {
|
||||
assert!(!app.contains(forbidden));
|
||||
assert!(!tauri.contains(forbidden));
|
||||
assert!(route_start.contains(required), "missing pre.007 exact Worker resource marker {required}");
|
||||
}
|
||||
for forbidden in ["Store::open", "start_with_runtime_resources", "request_stop", "RawTransactionIngestRuntimeResources::new"] {
|
||||
assert!(!route_start.contains(forbidden), "pre.007 advanced premature lifecycle marker {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_inventory_source_is_config_only_and_contains_all_five_capability_requirements() {
|
||||
let inventory = read_text(app_root().join("src/route_inventory.rs").as_path());
|
||||
fn pre_007_start_revalidation_reuses_inventory_and_requires_profile_network_identity() {
|
||||
let route_start = read_text(app_root().join("src/route_start.rs").as_path());
|
||||
for required in [
|
||||
"WsSubscriptionKind::Logs",
|
||||
"WsSubscriptionKind::Block",
|
||||
"WsSubscriptionKind::HeliusTransaction",
|
||||
"getTransaction",
|
||||
"getBlocksWithLimit",
|
||||
"MissingYellowstoneGrpc",
|
||||
"MissingRequiredSecret",
|
||||
"current_generation == 0",
|
||||
"request.inventory_generation != current_generation",
|
||||
"build_route_inventory_with_environment",
|
||||
"request.profile_id",
|
||||
"resolve_store_config_profile",
|
||||
"store.settings().network().as_str() != network",
|
||||
"transport_matches_network",
|
||||
] {
|
||||
assert!(inventory.contains(required), "missing pre.006 inventory requirement {required}");
|
||||
}
|
||||
for forbidden in ["connect(", "Store::open", "start_with_runtime_resources", "request_stop"] {
|
||||
assert!(!inventory.contains(forbidden), "pre.006 inventory performs premature runtime operation {forbidden}");
|
||||
assert!(route_start.contains(required), "missing pre.007 revalidation marker {required}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_inventory.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
#[test]
|
||||
fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
|
||||
@@ -27,8 +27,12 @@ fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
|
||||
|
||||
#[test]
|
||||
fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_material() {
|
||||
let profile =
|
||||
super::unavailable_profile("mainnet", false, std::option::Option::Some("mainnet"), crate::RawIngestRouteUnavailableReason::MissingRequiredSecret);
|
||||
let profile = super::unavailable_profile(
|
||||
"mainnet",
|
||||
false,
|
||||
std::option::Option::Some("mainnet"),
|
||||
crate::RawIngestRouteUnavailableReason::MissingRequiredSecret,
|
||||
);
|
||||
assert_eq!(profile.routes.len(), 5);
|
||||
assert_eq!(profile.network.as_deref(), std::option::Option::Some("mainnet"));
|
||||
for route in profile.routes {
|
||||
@@ -51,7 +55,7 @@ fn pre_006_fix_003_network_profiles_aggregate_same_network_capabilities_and_omit
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let inventory = super::build_route_inventory_with_environment(&management, &environment, 7);
|
||||
let inventory = crate::build_route_inventory_with_environment(&management, &environment, 7);
|
||||
assert!(inventory.is_ok());
|
||||
let inventory = match inventory {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -59,10 +63,7 @@ fn pre_006_fix_003_network_profiles_aggregate_same_network_capabilities_and_omit
|
||||
};
|
||||
assert_eq!(inventory.generation, 7);
|
||||
assert_eq!(inventory.default_profile, "devnet");
|
||||
assert_eq!(
|
||||
inventory.profiles.iter().map(|profile| return profile.profile_id.as_str()).collect::<std::vec::Vec<_>>(),
|
||||
vec!["devnet", "mainnet", "testnet"]
|
||||
);
|
||||
assert_eq!(inventory.profiles.iter().map(|profile| return profile.profile_id.as_str()).collect::<std::vec::Vec<_>>(), vec!["devnet", "mainnet", "testnet"]);
|
||||
let devnet = profile(&inventory, "devnet");
|
||||
assert!(devnet.is_some());
|
||||
if let std::option::Option::Some(devnet) = devnet {
|
||||
@@ -130,7 +131,7 @@ fn pre_006_fix_003_base_network_routes_remain_configured_independently_of_option
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let inventory = super::build_route_inventory_with_environment(&management, &environment, 11);
|
||||
let inventory = crate::build_route_inventory_with_environment(&management, &environment, 11);
|
||||
assert!(inventory.is_ok());
|
||||
let inventory = match inventory {
|
||||
std::result::Result::Ok(value) => value,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_start.rs
|
||||
// version: 1
|
||||
|
||||
|
||||
#[test]
|
||||
fn pre_007_stale_generation_is_rejected_before_config_or_transport_reconstruction() {
|
||||
let management = crate::config_management(&[]);
|
||||
assert!(management.is_ok());
|
||||
let management = match management {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let request = crate::RawIngestRouteStartRequestDto {
|
||||
commitment: crate::RawIngestCommitment::Confirmed,
|
||||
inventory_generation: 3,
|
||||
profile_id: "devnet".to_owned(),
|
||||
route_id: crate::RawIngestRouteId::StandardBlockDirect,
|
||||
};
|
||||
let result = crate::validate_route_start(&management, 2, &request);
|
||||
assert!(result.is_err());
|
||||
if let std::result::Result::Err(error) = result {
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_ROUTE_START_STALE_INVENTORY);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_007_commitments_are_bounded_to_confirmed_and_finalized() {
|
||||
assert_eq!(crate::RawIngestCommitment::Confirmed.into_transport(), ksp_onchain_transport_lib::SolanaCommitment::Confirmed);
|
||||
assert_eq!(crate::RawIngestCommitment::Finalized.into_transport(), ksp_onchain_transport_lib::SolanaCommitment::Finalized);
|
||||
assert_eq!(crate::RawIngestCommitment::Confirmed.as_str(), "confirmed");
|
||||
assert_eq!(crate::RawIngestCommitment::Finalized.as_str(), "finalized");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_007_public_standard_profiles_reconstruct_block_and_http_sources_without_network_io() {
|
||||
let management = crate::config_management(&[]);
|
||||
assert!(management.is_ok());
|
||||
let management = match management {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let inventory = crate::build_route_inventory(&management, 1);
|
||||
assert!(inventory.is_ok());
|
||||
let inventory = match inventory {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
for profile in inventory.profiles {
|
||||
for route_id in [crate::RawIngestRouteId::StandardLogsHydrated, crate::RawIngestRouteId::StandardBlockDirect, crate::RawIngestRouteId::HttpBlockPolling] {
|
||||
let route = profile.routes.iter().find(|route| return route.route_id == route_id);
|
||||
assert!(route.is_some(), "missing base route for {}", profile.profile_id);
|
||||
if let std::option::Option::Some(route) = route {
|
||||
assert_eq!(route.state, crate::RawIngestRouteState::Configured);
|
||||
let request = crate::RawIngestRouteStartRequestDto {
|
||||
commitment: crate::RawIngestCommitment::Confirmed,
|
||||
inventory_generation: 1,
|
||||
profile_id: profile.profile_id.clone(),
|
||||
route_id,
|
||||
};
|
||||
let prepared = crate::validate_route_start(&management, 1, &request);
|
||||
assert!(prepared.is_ok(), "failed Start-time reconstruction for {} / {}", profile.profile_id, route_id.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user