v0.3.15-pre.007
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 605
|
# version: 606
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.15-pre.6.fix.3"
|
version = "0.3.15-pre.7"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: crates/ksp-app-raw-transaction-ingest-desk/Cargo.toml
|
# file: crates/ksp-app-raw-transaction-ingest-desk/Cargo.toml
|
||||||
# version: 2
|
# version: 3
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "ksp-app-raw-transaction-ingest-desk"
|
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-core-lib = { path = "../ksp-core-lib" }
|
||||||
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
||||||
ksp-onchain-transport-lib = { path = "../ksp-onchain-transport-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"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
tauri.workspace = true
|
tauri.workspace = true
|
||||||
tauri-plugin-tracing.workspace = true
|
tauri-plugin-tracing.workspace = true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/README.md -->
|
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/README.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# `ksp-app-raw-transaction-ingest-desk`
|
# `ksp-app-raw-transaction-ingest-desk`
|
||||||
|
|
||||||
@@ -20,13 +20,15 @@ bin : ksp-app-raw-transaction-ingest-desk
|
|||||||
```text
|
```text
|
||||||
Config -> composite, profils, secrets et résolution effective
|
Config -> composite, profils, secrets et résolution effective
|
||||||
Transport -> capabilities HTTP/WS/gRPC et ressources physiques
|
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
|
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.
|
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.
|
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.
|
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
|
// 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.
|
//! 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);
|
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.
|
/// Builds the safe scaffold status exposed by the shell.
|
||||||
pub(crate) fn shell_status(&self) -> ksp_core_lib::Result<crate::ShellStatusDto> {
|
pub(crate) fn shell_status(&self) -> ksp_core_lib::Result<crate::ShellStatusDto> {
|
||||||
let document_count = self.config_management.engine().registry().descriptors().count();
|
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(),
|
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||||
config_document_count: document_count,
|
config_document_count: document_count,
|
||||||
fallback_logging_active: runtime.fallback_active,
|
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(),
|
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/constants.rs
|
// file: crates/ksp-app-raw-transaction-ingest-desk/src/constants.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Application-owned tracing targets and Config component identifiers.
|
//! 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";
|
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||||
/// Structured domain used while rebuilding Config-only route inventory.
|
/// Structured domain used while rebuilding Config-only route inventory.
|
||||||
pub(crate) const TRACING_DOMAIN_INVENTORY: &str = "raw_transaction_ingest.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.
|
/// Structured domain used by the Raw Transaction Ingest Desk shell.
|
||||||
pub(crate) const TRACING_DOMAIN_SHELL: &str = "raw_transaction_ingest.shell";
|
pub(crate) const TRACING_DOMAIN_SHELL: &str = "raw_transaction_ingest.shell";
|
||||||
/// Structured domain used by Tauri window lifecycle operations.
|
/// Structured domain used by Tauri window lifecycle operations.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/dto_route.rs
|
// 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.
|
//! Safe route-identity, Config-composability and lifecycle DTOs for Raw Transaction Ingest Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: trait-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Stable logical V1 route identifiers owned by Raw Transaction Ingest Desk.
|
/// 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")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteId.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteId.ts")]
|
||||||
pub(crate) enum RawIngestRouteId {
|
pub(crate) enum RawIngestRouteId {
|
||||||
@@ -26,7 +26,13 @@ impl crate::RawIngestRouteId {
|
|||||||
/// Returns all V1 route identifiers in stable UI order.
|
/// Returns all V1 route identifiers in stable UI order.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn all() -> std::vec::Vec<Self> {
|
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.
|
/// Returns the application-owned route family used by safe UI projections.
|
||||||
@@ -52,6 +58,81 @@ impl crate::RawIngestRouteId {
|
|||||||
Self::HttpBlockPolling => "HTTP Block Polling",
|
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.
|
/// 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.
|
/// Builds one route that is not composable from the current Config profile.
|
||||||
#[must_use]
|
#[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 {
|
return Self {
|
||||||
family: route_id.family(),
|
family: route_id.family(),
|
||||||
label: route_id.label().to_owned(),
|
label: route_id.label().to_owned(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/errors.rs
|
// file: crates/ksp-app-raw-transaction-ingest-desk/src/errors.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Stable Raw Transaction Ingest Desk application error codes.
|
//! 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.
|
/// Config-only route inventory cannot be projected safely from the validated composite.
|
||||||
pub(crate) const ERROR_CODE_ROUTE_INVENTORY_INVALID: ksp_core_lib::ErrorCode =
|
pub(crate) const ERROR_CODE_ROUTE_INVENTORY_INVALID: ksp_core_lib::ErrorCode =
|
||||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_inventory_invalid");
|
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.
|
/// Splash readiness was invoked from a window other than the splash window.
|
||||||
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode =
|
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode =
|
||||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "splash_origin_invalid");
|
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 =
|
pub(crate) const ERROR_CODE_SPLASH_SETTING_INVALID: ksp_core_lib::ErrorCode =
|
||||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "splash_setting_invalid");
|
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "splash_setting_invalid");
|
||||||
/// Tauri runtime assembly or execution failed.
|
/// 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.
|
/// 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.
|
/// A Tauri window show/focus/destroy/event operation failed.
|
||||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED: ksp_core_lib::ErrorCode =
|
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");
|
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
|
// 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.
|
//! Tauri desktop application scaffold for composing and supervising KSP live RAW transaction ingest routes.
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ mod errors;
|
|||||||
mod frontend_logging;
|
mod frontend_logging;
|
||||||
mod logging_runtime;
|
mod logging_runtime;
|
||||||
mod route_inventory;
|
mod route_inventory;
|
||||||
|
mod route_start;
|
||||||
mod splash;
|
mod splash;
|
||||||
mod tauri;
|
mod tauri;
|
||||||
mod tw_main;
|
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;
|
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_STORE;
|
||||||
/// Composite-local identifier for the standard Transport component.
|
/// Composite-local identifier for the standard Transport component.
|
||||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT;
|
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.
|
/// Prefix used by optional same-network Transport capability sources.
|
||||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_PREFIX;
|
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.
|
/// Composite-local optional Transport source for Yellowstone gRPC capabilities.
|
||||||
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE;
|
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE;
|
||||||
/// Structured domain used while bootstrapping Config and Logging.
|
/// 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;
|
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||||
/// Structured domain used while rebuilding Config-only route inventory.
|
/// Structured domain used while rebuilding Config-only route inventory.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_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.
|
/// Structured domain used by the Raw Transaction Ingest Desk shell.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
||||||
/// Structured domain used by Tauri window lifecycle operations.
|
/// 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;
|
pub(crate) use self::dto_common::CommandErrorDto;
|
||||||
/// Safe scaffold/runtime snapshot exposed to the shell.
|
/// Safe scaffold/runtime snapshot exposed to the shell.
|
||||||
pub(crate) use self::dto_common::ShellStatusDto;
|
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.
|
/// Safe route inventory for one logical composite profile.
|
||||||
pub(crate) use self::dto_route::RawIngestProfileInventoryDto;
|
pub(crate) use self::dto_route::RawIngestProfileInventoryDto;
|
||||||
/// Safe Config-only composability projection for one logical route.
|
/// 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;
|
pub(crate) use self::dto_route::RawIngestRouteId;
|
||||||
/// Complete deterministic Config-only route inventory.
|
/// Complete deterministic Config-only route inventory.
|
||||||
pub(crate) use self::dto_route::RawIngestRouteInventoryDto;
|
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.
|
/// Stable route lifecycle states reserved for runtime projections.
|
||||||
pub(crate) use self::dto_route::RawIngestRouteState;
|
pub(crate) use self::dto_route::RawIngestRouteState;
|
||||||
/// Safe reason explaining why one logical route is not composable from Config.
|
/// 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;
|
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
|
||||||
/// Config-only route inventory cannot be projected safely.
|
/// Config-only route inventory cannot be projected safely.
|
||||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_INVENTORY_INVALID;
|
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.
|
/// Splash readiness originated from an invalid window.
|
||||||
pub(crate) use self::errors::ERROR_CODE_SPLASH_ORIGIN_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_SPLASH_ORIGIN_INVALID;
|
||||||
/// Splash timing configuration is 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;
|
pub(crate) use self::logging_runtime::launch_identity;
|
||||||
/// Rebuilds the complete safe route inventory from validated Config without runtime I/O.
|
/// Rebuilds the complete safe route inventory from validated Config without runtime I/O.
|
||||||
pub(crate) use self::route_inventory::build_route_inventory;
|
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.
|
/// Command emitted by Rust to the splash frontend.
|
||||||
pub(crate) use self::splash::SplashOrderDto;
|
pub(crate) use self::splash::SplashOrderDto;
|
||||||
/// Runtime timings used by the common desk splash lifecycle.
|
/// 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
|
// 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.
|
//! 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.
|
/// 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 = ksp_config_lib::ConfigEnvironment::load();
|
||||||
let environment = match environment {
|
let environment = match environment {
|
||||||
std::result::Result::Ok(value) => value,
|
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);
|
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,
|
management: &ksp_config_lib::ConfigManagement,
|
||||||
environment: &ksp_config_lib::ConfigEnvironment,
|
environment: &ksp_config_lib::ConfigEnvironment,
|
||||||
generation: u32,
|
generation: u32,
|
||||||
@@ -69,7 +73,12 @@ fn build_route_inventory_with_environment(
|
|||||||
};
|
};
|
||||||
let mut profiles = std::vec::Vec::with_capacity(catalog.profile_ids.len());
|
let mut profiles = std::vec::Vec::with_capacity(catalog.profile_ids.len());
|
||||||
for profile_id in &catalog.profile_ids {
|
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!(
|
ksp_logging_lib::debug!(
|
||||||
target: crate::TRACING_TARGET,
|
target: crate::TRACING_TARGET,
|
||||||
@@ -155,8 +164,11 @@ fn build_profile_inventory(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let network = store.settings().network().as_str().to_owned();
|
let network = store.settings().network().as_str().to_owned();
|
||||||
let base_transport_profile =
|
let base_transport_profile = crate::required_composite_component_profile(
|
||||||
crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT);
|
&composite,
|
||||||
|
crate::COMPOSITE_COMPONENT_ID_TRANSPORT,
|
||||||
|
ksp_config_lib::FILE_ID_STD_TRANSPORT,
|
||||||
|
);
|
||||||
let base_transport_profile = match base_transport_profile {
|
let base_transport_profile = match base_transport_profile {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(_) => {
|
std::result::Result::Err(_) => {
|
||||||
@@ -172,7 +184,12 @@ fn build_profile_inventory(
|
|||||||
let base_transport = match base_transport {
|
let base_transport = match base_transport {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
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()) {
|
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
|
// 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.
|
//! 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_foundation,
|
||||||
get_route_inventory,
|
get_route_inventory,
|
||||||
get_runtime_status,
|
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)),
|
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
|
// 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)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -21,51 +21,56 @@ fn read_text(path: &std::path::Path) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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());
|
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"] {
|
for required in [
|
||||||
assert!(manifest.contains(required), "missing pre.006 dependency {required}");
|
"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 [
|
for forbidden in [
|
||||||
"ksp-store-lib",
|
"ksp-store-lib",
|
||||||
"ksp-store-api",
|
"ksp-store-api",
|
||||||
"ksp-store-postgres-lib",
|
"ksp-store-postgres-lib",
|
||||||
"ksp-worker-api",
|
"ksp-worker-api",
|
||||||
"ksp-worker-raw-transaction-ingest-lib",
|
|
||||||
"ksp-job-backfill-lib",
|
"ksp-job-backfill-lib",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"tonic",
|
"tonic",
|
||||||
"yellowstone-grpc",
|
"yellowstone-grpc",
|
||||||
] {
|
] {
|
||||||
assert!(!manifest.contains(forbidden), "pre.006 opens forbidden dependency {forbidden}");
|
assert!(!manifest.contains(forbidden), "pre.007 opens forbidden direct dependency {forbidden}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_006_production_sources_use_transport_settings_only_without_runtime_io_or_store_open() {
|
fn pre_007_route_start_reconstructs_exact_worker_sources_without_network_store_or_worker_launch() {
|
||||||
let root = app_root().join("src");
|
let source = read_text(app_root().join("src/route_start.rs").as_path());
|
||||||
let entries = std::fs::read_dir(root.as_path());
|
for required in [
|
||||||
assert!(entries.is_ok());
|
"HttpTransportPool::new",
|
||||||
if let std::result::Result::Ok(entries) = entries {
|
"YellowstoneGrpcChannel::prepare",
|
||||||
for entry in entries.flatten() {
|
"RawTransactionIngestYellowstoneSource::new",
|
||||||
let path = entry.path();
|
"RawTransactionIngestStandardLogsSource::new",
|
||||||
if path.extension().and_then(std::ffi::OsStr::to_str) != std::option::Option::Some("rs") {
|
"RawTransactionIngestStandardBlockSource::new",
|
||||||
continue;
|
"RawTransactionIngestHeliusTransactionSource::new",
|
||||||
|
"RawTransactionIngestHttpBlockPollingSource::new",
|
||||||
|
] {
|
||||||
|
assert!(source.contains(required), "missing exact resource constructor {required}");
|
||||||
}
|
}
|
||||||
let source = read_text(path.as_path());
|
|
||||||
for forbidden in [
|
for forbidden in [
|
||||||
"HttpTransportPool",
|
"YellowstoneGrpcChannel::connect",
|
||||||
"WsProtocolSession",
|
"SolanaStandardWsSession::connect",
|
||||||
"YellowstoneGrpcChannel",
|
"HeliusWsSession::connect",
|
||||||
"Store::open",
|
"Store::open",
|
||||||
"RawTransactionIngestWorker",
|
"RawTransactionIngestWorker::",
|
||||||
"RawTransactionIngestRuntimeResources",
|
|
||||||
"start_with_runtime_resources",
|
"start_with_runtime_resources",
|
||||||
"request_stop",
|
"request_stop",
|
||||||
] {
|
] {
|
||||||
assert!(!source.contains(forbidden), "{} contains premature runtime marker {forbidden}", path.display());
|
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
|
// 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.
|
//! 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}");
|
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
|
// 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.
|
//! 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}");
|
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
|
// 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)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -21,7 +21,7 @@ fn read_text(path: &std::path::Path) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 lib = read_text(app_root().join("src/lib.rs").as_path());
|
||||||
let expected = [
|
let expected = [
|
||||||
"mod app_state;",
|
"mod app_state;",
|
||||||
@@ -33,6 +33,7 @@ fn pre_006_production_module_inventory_adds_only_route_inventory_to_the_scaffold
|
|||||||
"mod frontend_logging;",
|
"mod frontend_logging;",
|
||||||
"mod logging_runtime;",
|
"mod logging_runtime;",
|
||||||
"mod route_inventory;",
|
"mod route_inventory;",
|
||||||
|
"mod route_start;",
|
||||||
"mod splash;",
|
"mod splash;",
|
||||||
"mod tauri;",
|
"mod tauri;",
|
||||||
"mod tw_main;",
|
"mod tw_main;",
|
||||||
@@ -45,46 +46,44 @@ fn pre_006_production_module_inventory_adds_only_route_inventory_to_the_scaffold
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 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<_>>();
|
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;"]);
|
assert_eq!(public_reexports, vec!["pub use self::tauri::run;"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_006_config_inventory_keeps_start_stop_and_runtime_resources_for_later_tranches() {
|
fn pre_007_reconstructs_all_five_worker_source_contracts_without_advancing_store_or_worker_lifecycle() {
|
||||||
let app = read_text(app_root().join("src/app_state.rs").as_path());
|
let route_start = read_text(app_root().join("src/route_start.rs").as_path());
|
||||||
let tauri = read_text(app_root().join("src/tauri.rs").as_path());
|
for required in [
|
||||||
for forbidden in [
|
"RawTransactionIngestYellowstoneSource::new",
|
||||||
"RawTransactionIngestWorker",
|
"RawTransactionIngestStandardLogsSource::new",
|
||||||
"RawTransactionIngestRuntimeResources",
|
"RawTransactionIngestStandardBlockSource::new",
|
||||||
"request_stop",
|
"RawTransactionIngestHeliusTransactionSource::new",
|
||||||
"start_with_runtime_resources",
|
"RawTransactionIngestHttpBlockPollingSource::new",
|
||||||
"Store::open",
|
"SolanaLogsSubscribeFilter::AllWithVotes",
|
||||||
"HttpTransportPool",
|
"SolanaBlockSubscribeFilter::All",
|
||||||
"WsProtocolSession",
|
"YellowstoneSubscribeTransactionFilter::new",
|
||||||
"YellowstoneGrpcChannel",
|
|
||||||
] {
|
] {
|
||||||
assert!(!app.contains(forbidden));
|
assert!(route_start.contains(required), "missing pre.007 exact Worker resource marker {required}");
|
||||||
assert!(!tauri.contains(forbidden));
|
}
|
||||||
|
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]
|
#[test]
|
||||||
fn pre_006_inventory_source_is_config_only_and_contains_all_five_capability_requirements() {
|
fn pre_007_start_revalidation_reuses_inventory_and_requires_profile_network_identity() {
|
||||||
let inventory = read_text(app_root().join("src/route_inventory.rs").as_path());
|
let route_start = read_text(app_root().join("src/route_start.rs").as_path());
|
||||||
for required in [
|
for required in [
|
||||||
"WsSubscriptionKind::Logs",
|
"current_generation == 0",
|
||||||
"WsSubscriptionKind::Block",
|
"request.inventory_generation != current_generation",
|
||||||
"WsSubscriptionKind::HeliusTransaction",
|
"build_route_inventory_with_environment",
|
||||||
"getTransaction",
|
"request.profile_id",
|
||||||
"getBlocksWithLimit",
|
"resolve_store_config_profile",
|
||||||
"MissingYellowstoneGrpc",
|
"store.settings().network().as_str() != network",
|
||||||
"MissingRequiredSecret",
|
"transport_matches_network",
|
||||||
] {
|
] {
|
||||||
assert!(inventory.contains(required), "missing pre.006 inventory requirement {required}");
|
assert!(route_start.contains(required), "missing pre.007 revalidation marker {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}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_inventory.rs
|
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_inventory.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
|
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]
|
#[test]
|
||||||
fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_material() {
|
fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_material() {
|
||||||
let profile =
|
let profile = super::unavailable_profile(
|
||||||
super::unavailable_profile("mainnet", false, std::option::Option::Some("mainnet"), crate::RawIngestRouteUnavailableReason::MissingRequiredSecret);
|
"mainnet",
|
||||||
|
false,
|
||||||
|
std::option::Option::Some("mainnet"),
|
||||||
|
crate::RawIngestRouteUnavailableReason::MissingRequiredSecret,
|
||||||
|
);
|
||||||
assert_eq!(profile.routes.len(), 5);
|
assert_eq!(profile.routes.len(), 5);
|
||||||
assert_eq!(profile.network.as_deref(), std::option::Option::Some("mainnet"));
|
assert_eq!(profile.network.as_deref(), std::option::Option::Some("mainnet"));
|
||||||
for route in profile.routes {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(_) => return,
|
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());
|
assert!(inventory.is_ok());
|
||||||
let inventory = match inventory {
|
let inventory = match inventory {
|
||||||
std::result::Result::Ok(value) => value,
|
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.generation, 7);
|
||||||
assert_eq!(inventory.default_profile, "devnet");
|
assert_eq!(inventory.default_profile, "devnet");
|
||||||
assert_eq!(
|
assert_eq!(inventory.profiles.iter().map(|profile| return profile.profile_id.as_str()).collect::<std::vec::Vec<_>>(), vec!["devnet", "mainnet", "testnet"]);
|
||||||
inventory.profiles.iter().map(|profile| return profile.profile_id.as_str()).collect::<std::vec::Vec<_>>(),
|
|
||||||
vec!["devnet", "mainnet", "testnet"]
|
|
||||||
);
|
|
||||||
let devnet = profile(&inventory, "devnet");
|
let devnet = profile(&inventory, "devnet");
|
||||||
assert!(devnet.is_some());
|
assert!(devnet.is_some());
|
||||||
if let std::option::Option::Some(devnet) = devnet {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(_) => return,
|
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());
|
assert!(inventory.is_ok());
|
||||||
let inventory = match inventory {
|
let inventory = match inventory {
|
||||||
std::result::Result::Ok(value) => value,
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
deltas/0.3.15/pre.007.md
Normal file
157
deltas/0.3.15/pre.007.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<!-- file: deltas/0.3.15/pre.007.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta `0.3.15-pre.007`
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Revalider au Start la sélection logique issue de l'inventaire `pre.006`, reconstruire les ressources Transport exactes attendues par les cinq familles Worker V1, puis les valider via les constructeurs publics du Worker sans ouvrir Store ni lancer de Worker.
|
||||||
|
|
||||||
|
## Requête Start sûre
|
||||||
|
|
||||||
|
Le préflight backend reçoit uniquement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
profile_id
|
||||||
|
route_id
|
||||||
|
inventory_generation
|
||||||
|
commitment = confirmed | finalized
|
||||||
|
```
|
||||||
|
|
||||||
|
`profile_id` est ajouté au contrat candidat de `pre.001`. Depuis le passage réseau-centrique de `pre.006-fix.001`, un même `route_id` peut exister sur plusieurs réseaux ; le backend doit donc recevoir l'identité logique du profil réseau sélectionné pour reconstruire sans état frontend implicite.
|
||||||
|
|
||||||
|
Aucun endpoint, URL, provider choisi par le frontend, credential, token, metadata secrète, URI Store, source key ou handle runtime ne traverse IPC.
|
||||||
|
|
||||||
|
## Préflight `validate_route_start`
|
||||||
|
|
||||||
|
Le backend :
|
||||||
|
|
||||||
|
1. refuse une génération nulle ou différente de la génération courante avec `stale_inventory` ;
|
||||||
|
2. capture `ConfigEnvironment` ;
|
||||||
|
3. reconstruit l'inventaire avec le même snapshot d'environnement et la même génération ;
|
||||||
|
4. vérifie que le profil et la route existent encore et restent `Configured` ;
|
||||||
|
5. recharge le composite réseau explicite ;
|
||||||
|
6. résout Store et reproouve le réseau logique ;
|
||||||
|
7. résout le Transport de base et les sources provider-specific nécessaires ;
|
||||||
|
8. reconstruit la ressource exacte de la famille Worker ;
|
||||||
|
9. détruit immédiatement la ressource après validation ;
|
||||||
|
10. renvoie uniquement une projection sûre profil/réseau/route/génération/commitment.
|
||||||
|
|
||||||
|
Les erreurs lower-layer sont encapsulées derrière les codes/messages bornés du Desk ; les contextes physiques ne sont pas projetés au frontend.
|
||||||
|
|
||||||
|
## Reconstruction exacte des cinq routes
|
||||||
|
|
||||||
|
```text
|
||||||
|
yellowstone-hydrated
|
||||||
|
YellowstoneGrpcChannel::prepare
|
||||||
|
YellowstoneSubscribeRequest transaction-bearing
|
||||||
|
HttpTransportPool same-network / getTransaction
|
||||||
|
RawTransactionIngestYellowstoneSource::new
|
||||||
|
|
||||||
|
standard-logs-hydrated
|
||||||
|
endpoint SolanaStandard + capability Logs
|
||||||
|
SolanaLogsSubscribeFilter::AllWithVotes
|
||||||
|
HttpTransportPool same-network / getTransaction
|
||||||
|
RawTransactionIngestStandardLogsSource::new
|
||||||
|
|
||||||
|
standard-block-direct
|
||||||
|
endpoint SolanaStandard + capability Block
|
||||||
|
SolanaBlockSubscribeFilter::All
|
||||||
|
RawTransactionIngestStandardBlockSource::new
|
||||||
|
|
||||||
|
helius-transaction-hydrated
|
||||||
|
source transport.helius same-network
|
||||||
|
endpoint HeliusLaserStream + capability HeliusTransaction
|
||||||
|
filtre transaction général borné
|
||||||
|
HttpTransportPool de base same-network / getTransaction
|
||||||
|
RawTransactionIngestHeliusTransactionSource::new
|
||||||
|
|
||||||
|
http-block-polling
|
||||||
|
HttpTransportPool same-network
|
||||||
|
rôle unique composant getSlot + getBlocksWithLimit + getBlock
|
||||||
|
RawTransactionIngestHttpBlockPollingSource::new
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP conserve la sélection physique de ses endpoints dans `HttpTransportPool`. Pour WS et Yellowstone gRPC, `pre.007` exige exactement un endpoint activé matching dans le profil source résolu ; une ambiguïté est refusée au lieu d'être arbitrée silencieusement par le Desk.
|
||||||
|
|
||||||
|
## Frontières maintenues
|
||||||
|
|
||||||
|
Cette tranche n'introduit pas :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Store::open
|
||||||
|
connexion WebSocket active
|
||||||
|
YellowstoneGrpcChannel::connect
|
||||||
|
RawTransactionIngestRuntimeResources
|
||||||
|
RawTransactionIngestWorker::start_with_runtime_resources
|
||||||
|
Worker handle
|
||||||
|
request_stop
|
||||||
|
état Running
|
||||||
|
multi-route runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
Le Store lifecycle et le Start/Stop mono-route réel restent la responsabilité de `pre.008`.
|
||||||
|
|
||||||
|
## Dépendances
|
||||||
|
|
||||||
|
Le Desk ajoute uniquement la dépendance directe :
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-worker-raw-transaction-ingest-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune dépendance directe vers `ksp-store-lib`, `ksp-store-api`, un backend Store physique, `ksp-worker-api`, Job Backfill, `reqwest`, `tonic` ou Yellowstone provider crate n'est ajoutée.
|
||||||
|
|
||||||
|
## Tests et canaris
|
||||||
|
|
||||||
|
Ajouts/corrections :
|
||||||
|
|
||||||
|
- canari génération stale avant toute reconstruction ;
|
||||||
|
- commitments strictement bornés à `confirmed` / `finalized` ;
|
||||||
|
- reconstruction sans I/O des routes Standard Block et HTTP Polling sur les profils réseau committés ;
|
||||||
|
- inventaire de modules mis à jour avec `route_start` ;
|
||||||
|
- surface publique toujours limitée à `run` ;
|
||||||
|
- vérification statique des cinq constructeurs Worker exacts ;
|
||||||
|
- vérification de l'absence de Store/Worker launch ;
|
||||||
|
- sécurité DTO Start/ack sans matériel physique ;
|
||||||
|
- accès aux helpers crate-shared via `crate::` conformément aux règles Rust KSP.
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
```text
|
||||||
|
header racine : 605 -> 606
|
||||||
|
workspace : 0.3.15-pre.6.fix.3 -> 0.3.15-pre.7
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validations disponibles dans l'environnement d'assemblage
|
||||||
|
|
||||||
|
```text
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
General Rust rule audit: clean
|
||||||
|
Rust export completeness audit: 0 candidate(s)
|
||||||
|
KSP workspace Rust rule audit: clean
|
||||||
|
|
||||||
|
python3 scripts/audit_markdown_tables.py ...
|
||||||
|
clean
|
||||||
|
|
||||||
|
JSON/TOML parse
|
||||||
|
clean
|
||||||
|
```
|
||||||
|
|
||||||
|
La toolchain Cargo/rustfmt n'est pas disponible dans l'environnement d'assemblage ; aucune commande Cargo locale n'est donc déclarée PASS.
|
||||||
|
|
||||||
|
## Gate opérateur attendu
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
python3 scripts/audit_rust_workspace_rules.py
|
||||||
|
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.15
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
(cd crates/ksp-app-raw-transaction-ingest-desk && cargo tauri dev)
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun `npm run build` direct n'est exécuté ; Tauri possède le cycle frontend.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
|
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
|
||||||
<!-- version: 9 -->
|
<!-- version: 10 -->
|
||||||
|
|
||||||
# Plan v0.3.15 — Raw Transaction Ingest Desk
|
# Plan v0.3.15 — Raw Transaction Ingest Desk
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ route_id -> Worker handle + snapshot source + terminal task ownership
|
|||||||
Start d'une route :
|
Start d'une route :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
1. recevoir route_id + inventory_generation + commitment
|
1. recevoir profile_id + route_id + inventory_generation + commitment
|
||||||
2. re-résoudre la Config côté Rust
|
2. re-résoudre la Config côté Rust
|
||||||
3. vérifier que la génération/route existe toujours
|
3. vérifier que la génération/route existe toujours
|
||||||
4. revalider réseau Transport/Store
|
4. revalider réseau Transport/Store
|
||||||
@@ -523,7 +523,7 @@ Le texte détaillé des erreurs provider n'est jamais projeté tel quel.
|
|||||||
|---------------------------------|---------------------|------------------------------------------------------------------------------|------------------------------------------|
|
|---------------------------------|---------------------|------------------------------------------------------------------------------|------------------------------------------|
|
||||||
| `RawIngestDeskOptionsDto` | backend -> frontend | profils logiques sûrs, réseaux, commitments supportés, génération inventaire | URL, secret, Store URI |
|
| `RawIngestDeskOptionsDto` | backend -> frontend | profils logiques sûrs, réseaux, commitments supportés, génération inventaire | URL, secret, Store URI |
|
||||||
| `RawIngestRouteDto` | backend -> frontend | route_id, family, label, network, state, selectable, reason code | endpoint name/URL/token/source key |
|
| `RawIngestRouteDto` | backend -> frontend | route_id, family, label, network, state, selectable, reason code | endpoint name/URL/token/source key |
|
||||||
| `RawIngestRouteStartRequestDto` | frontend -> backend | route_id, inventory_generation, commitment | endpoint/credential/client |
|
| `RawIngestRouteStartRequestDto` | frontend -> backend | profile_id, route_id, inventory_generation, commitment | endpoint/credential/client |
|
||||||
| `RawIngestRouteStopRequestDto` | frontend -> backend | route_id | Worker handle |
|
| `RawIngestRouteStopRequestDto` | frontend -> backend | route_id | Worker handle |
|
||||||
| `RawIngestRouteRuntimeDto` | backend -> frontend | route_id, lifecycle/health/activity, compteurs source-neutral | payload brut, erreur provider arbitraire |
|
| `RawIngestRouteRuntimeDto` | backend -> frontend | route_id, lifecycle/health/activity, compteurs source-neutral | payload brut, erreur provider arbitraire |
|
||||||
| `CommandErrorDto` | backend -> frontend | code stable + contexte whiteliste | texte remote arbitraire |
|
| `CommandErrorDto` | backend -> frontend | code stable + contexte whiteliste | texte remote arbitraire |
|
||||||
@@ -746,9 +746,23 @@ Une route liée à un provider n'est projetée que si le composite réseau décl
|
|||||||
|
|
||||||
### `pre.007` — reconstruction Start des ressources Transport
|
### `pre.007` — reconstruction Start des ressources Transport
|
||||||
|
|
||||||
Revalider Config au Start et reconstruire les ressources Transport exactes nécessaires aux routes applicables du réseau sélectionné, dans le catalogue global de cinq IDs V1, sans lancer encore le Worker. Le choix des endpoints physiques reste Transport-owned.
|
**État : implémenté.** Le backend introduit un préflight `validate_route_start` qui reçoit une sélection logique sûre (`profile_id`, `route_id`, `inventory_generation`, `commitment`), refuse une génération stale, recharge le même snapshot d'environnement Config, reconstruit l'inventaire au même numéro puis revalide Store et Transport sur le réseau sélectionné.
|
||||||
|
|
||||||
Budget cible : une tranche intégration Transport/Desk.
|
Le `profile_id` est ajouté au DTO de Start par rapport au candidat de `pre.001` : après le passage réseau-centrique de `pre.006-fix.001`, un `route_id` existe sur plusieurs réseaux et ne peut donc pas identifier seul la composition à reconstruire. Ce champ est un identifiant logique sûr (`devnet`, `mainnet`, `testnet`) et ne révèle aucun provider ni endpoint.
|
||||||
|
|
||||||
|
La reconstruction exacte réutilise les contrats lower-layer existants :
|
||||||
|
|
||||||
|
```text
|
||||||
|
yellowstone-hydrated -> YellowstoneGrpcChannel::prepare + RawTransactionIngestYellowstoneSource::new
|
||||||
|
standard-logs-hydrated -> HttpTransportPool + RawTransactionIngestStandardLogsSource::new
|
||||||
|
standard-block-direct -> RawTransactionIngestStandardBlockSource::new
|
||||||
|
helius-transaction-hydrated -> HttpTransportPool + RawTransactionIngestHeliusTransactionSource::new
|
||||||
|
http-block-polling -> HttpTransportPool + RawTransactionIngestHttpBlockPollingSource::new
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP conserve son pool Transport-owned. Pour WS/gRPC, le profil source doit produire exactement un endpoint activé correspondant au protocole/capability/réseau attendu ; le Desk refuse l'ambiguïté au lieu d'inventer un choix physique. Les constructeurs Worker reçoivent des presets backend bornés (`AllWithVotes`, `All`, filtre transaction général, commitment confirmed/finalized).
|
||||||
|
|
||||||
|
Le préflight instancie puis détruit immédiatement le contrat source. Il ne fait aucun `connect`, aucun `Store::open`, aucun `start_with_runtime_resources`, ne conserve aucun handle et ne publie jamais `Running`. Le Start réel, l'ouverture Store et le lifecycle mono-route restent `pre.008`.
|
||||||
|
|
||||||
### `pre.008` — Store lifecycle et Start/Stop mono-route
|
### `pre.008` — Store lifecycle et Start/Stop mono-route
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
|
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
|
||||||
<!-- version: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
# Validation v0.3.15 — Raw Transaction Ingest Desk
|
# Validation v0.3.15 — Raw Transaction Ingest Desk
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ frontend sans endpoints/secrets/handles/source keys/payloads bruts
|
|||||||
|---------------------------------|---------------------|------------------------------------------------------------------------------|------------------------------------------|
|
|---------------------------------|---------------------|------------------------------------------------------------------------------|------------------------------------------|
|
||||||
| `RawIngestDeskOptionsDto` | backend -> frontend | profils logiques sûrs, réseaux, commitments supportés, génération inventaire | URL, secret, Store URI |
|
| `RawIngestDeskOptionsDto` | backend -> frontend | profils logiques sûrs, réseaux, commitments supportés, génération inventaire | URL, secret, Store URI |
|
||||||
| `RawIngestRouteDto` | backend -> frontend | route_id, family, label, network, state, selectable, reason code | endpoint name/URL/token/source key |
|
| `RawIngestRouteDto` | backend -> frontend | route_id, family, label, network, state, selectable, reason code | endpoint name/URL/token/source key |
|
||||||
| `RawIngestRouteStartRequestDto` | frontend -> backend | route_id, inventory_generation, commitment | endpoint/credential/client |
|
| `RawIngestRouteStartRequestDto` | frontend -> backend | profile_id, route_id, inventory_generation, commitment | endpoint/credential/client |
|
||||||
| `RawIngestRouteStopRequestDto` | frontend -> backend | route_id | Worker handle |
|
| `RawIngestRouteStopRequestDto` | frontend -> backend | route_id | Worker handle |
|
||||||
| `RawIngestRouteRuntimeDto` | backend -> frontend | route_id, lifecycle/health/activity, compteurs source-neutral | payload brut, erreur provider arbitraire |
|
| `RawIngestRouteRuntimeDto` | backend -> frontend | route_id, lifecycle/health/activity, compteurs source-neutral | payload brut, erreur provider arbitraire |
|
||||||
| `CommandErrorDto` | backend -> frontend | code stable + contexte whiteliste | texte remote arbitraire |
|
| `CommandErrorDto` | backend -> frontend | code stable + contexte whiteliste | texte remote arbitraire |
|
||||||
@@ -384,7 +384,22 @@ Le catalogue global reste exactement cinq IDs V1, mais l'inventaire d'un profil
|
|||||||
|
|
||||||
### `pre.007` — ressources Start
|
### `pre.007` — ressources Start
|
||||||
|
|
||||||
Reconstruction/revalidation Start des ressources Transport pour les routes applicables du réseau sélectionné, dans le catalogue global de cinq IDs V1, sans launch Worker.
|
**Statut : implémenté, gate opérateur à exécuter.** Le préflight backend `validate_route_start` est generation-bound et revalide la sélection contre un inventaire Config reconstruit avant toute ressource. La requête contient désormais `profile_id + route_id + inventory_generation + commitment` : `profile_id` est nécessaire depuis que les profils applicatifs sont réseau-centriques et qu'un même route ID existe sur plusieurs réseaux.
|
||||||
|
|
||||||
|
La preuve Start-time exige ensuite :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Store Config résolu et réseau identique
|
||||||
|
Transport de base résolu et same-network
|
||||||
|
source provider-specific résolue seulement pour Helius/Yellowstone
|
||||||
|
HTTP role unique satisfaisant l'ensemble des méthodes requises
|
||||||
|
WS/gRPC endpoint unique correspondant au protocole/capability/réseau
|
||||||
|
constructeur public Worker de la famille exacte accepté
|
||||||
|
```
|
||||||
|
|
||||||
|
Les cinq constructeurs source Worker sont effectivement réutilisés. `HttpTransportPool::new` et `YellowstoneGrpcChannel::prepare` restent sans requête réseau ; la source reconstruite est détruite immédiatement après validation. Sont encore interdits dans cette tranche : `Store::open`, connexion WS/gRPC active, `RawTransactionIngestRuntimeResources`, `RawTransactionIngestWorker::start_with_runtime_resources`, handle Worker, Start/Stop réel et état `Running`.
|
||||||
|
|
||||||
|
Attendus de sécurité : la réponse de préflight ne contient que profil logique, réseau, route, génération et commitment ; les erreurs lower-layer sont remappées derrière des codes/messages app bornés. Aucun endpoint, URL, credential, metadata secrète, URI Store, source key ou payload RAW ne traverse IPC.
|
||||||
|
|
||||||
### `pre.008` — runtime mono-route
|
### `pre.008` — runtime mono-route
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user