v0.1.0-pre.076

This commit is contained in:
2026-08-01 16:23:15 +02:00
parent f4331cf48a
commit d7284ab71e
27 changed files with 2273 additions and 2020 deletions

View File

@@ -1,8 +1,22 @@
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
<!-- version: 3 -->
<!-- version: 6 -->
# CHANGELOG — kb-app-demo-desktop
## 0.1.0-pre.076
- correction de larchitecture Tauri : `tauri.rs` reste lunique propriétaire des `#[tauri::command]` et délègue via la façade `crate::*` ;
- suppression des modules intermédiaires `tauri_*` ajoutés à tort ;
- suppression de 43 exports `pub(crate)` qui ne franchissaient aucune frontière de module ;
- renommage des fonctions `*_inner` en noms fonctionnels directs ;
- suppression du helper IPC utilisé une seule fois et simplification de linitialisation de létat ;
- complétion des rustdoc manquantes dans `lib.rs` ;
- correction de laudit statique pour contrôler la minceur sémantique de `tauri.rs` plutôt quun seuil arbitraire de lignes ;
- réduction de `tauri.rs` à lassemblage runtime et aux wrappers strictement nécessaires ;
- répartition des commandes Tauri par domaine dans des modules dédiés ;
- validation Mainnet de la persistance de session WebSocket après fermeture et réouverture de `demo_ws` ;
- ajout du scénario borné de backfill `mainnet_research`.
## 0.1.0-pre.075
- ajout de laudit statique Tauri/frontend et du scénario de validation du cycle de vie WebSocket ;

View File

@@ -1,17 +1,12 @@
<!-- file: kb-app-demo-desktop/TODO.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# TODO — kb-app-demo-desktop
## Bloquants avant alignement `0.4.6`
- [ ] Architecture - déplacer hors de `tauri.rs` lorchestration pipeline, stockage et exécution encore substantielle.
- [ ] Architecture - conserver dans `tauri.rs` uniquement les wrappers Tauri, lassemblage runtime et les adaptations minimales.
- [ ] Validation - vérifier intégralement la synchronisation des adaptateurs Tauri et des payloads TS-RS avec les APIs réutilisables.
- [ ] Validation - couvrir explicitement les cycles douverture, fermeture et réouverture des fenêtres concernées.
- [ ] WebSocket - valider que la fermeture de `demo_ws` ne ferme pas une session WebSocket active.
- [ ] WebSocket - valider que la réouverture de `demo_ws` restaure létat courant de la session WebSocket.
## Entre `0.4.6` et `0.4.7`

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_backfill.rs
// version: 12
// version: 15
//! Tauri commands and UI payloads for bounded HTTP transaction backfills.
@@ -227,7 +227,103 @@ impl std::ops::Drop for crate::DemoBackfillRunGuard<'_> {
}
}
pub(crate) fn build_role_options(
pub(crate) fn demo_backfill_cancel(state: tauri::State<'_, crate::AppState>) -> bool {
let running = state.demo_backfill_running().load(std::sync::atomic::Ordering::Acquire);
state
.demo_backfill_cancel_requested()
.store(true, std::sync::atomic::Ordering::Release);
return running;
}
pub(crate) fn demo_backfill_options(
state: tauri::State<'_, crate::AppState>,
) -> crate::DemoBackfillOptionsPayload {
let roles = build_role_options(state.http_pool().snapshot());
let default_role = if roles.iter().any(|item| return item.role == "history_backfill") {
std::option::Option::Some("history_backfill".to_string())
} else {
roles.first().map(|item| return item.role.clone())
};
let programs = kb_program_ids::registered_program_ids()
.iter()
.map(|entry| {
return crate::DemoBackfillProgramOption {
code: entry.code().to_string(),
program_id: entry.program_id().to_string(),
};
})
.collect();
return crate::DemoBackfillOptionsPayload {
roles,
programs,
default_role,
default_commitment: "confirmed".to_string(),
default_page_size: 100,
default_max_pages: 20,
default_max_concurrent_requests: 4,
default_max_retries: 2,
running: state.demo_backfill_running().load(std::sync::atomic::Ordering::Acquire),
};
}
pub(crate) async fn demo_backfill_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoBackfillRequest,
) -> std::result::Result<crate::DemoBackfillSummaryPayload, std::string::String> {
let acquire_result = state.demo_backfill_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err("a backfill campaign is already running".to_string());
}
let _run_guard = crate::DemoBackfillRunGuard { running: state.demo_backfill_running() };
state
.demo_backfill_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let pipeline_request = match build_pipeline_request(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let profile = state.active_profile();
let store_options = match kb_store::PostgresStoreOptions::new(
profile.database.postgres.url.clone(),
profile.database.postgres.max_connections,
profile.database.postgres.connect_timeout_ms,
false,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match kb_store::PostgresStore::connect(store_options).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let observer = crate::DemoBackfillObserver {
app_handle,
cancel_requested: state.demo_backfill_cancel_requested(),
};
let summary = match kb_pipeline::execute_http_backfill(
state.http_pool(),
&store,
&pipeline_request,
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(summary_payload(summary));
}
fn build_role_options(
snapshots: std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoBackfillRoleOption> {
let mut role_methods = std::collections::BTreeMap::<
@@ -266,7 +362,7 @@ pub(crate) fn build_role_options(
return output;
}
pub(crate) fn build_demo_backfill_pipeline_request(
fn build_pipeline_request(
request: DemoBackfillRequest,
) -> std::result::Result<kb_pipeline::BackfillRequest, std::string::String> {
let source_result = match request.mode.trim() {
@@ -385,9 +481,7 @@ fn address_source(
});
}
pub(crate) fn demo_backfill_summary_payload(
summary: kb_pipeline::BackfillSummary,
) -> DemoBackfillSummaryPayload {
fn summary_payload(summary: kb_pipeline::BackfillSummary) -> DemoBackfillSummaryPayload {
return crate::DemoBackfillSummaryPayload {
capture_session_id: summary.capture_session_id,
filter_code: summary.filter_code,

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_core_extraction.rs
// version: 11
// version: 13
//! Tauri commands and UI payloads for canonical transaction to core extraction.
@@ -194,7 +194,7 @@ pub(crate) fn demo_core_extraction_options_payload(
};
}
pub(crate) async fn execute_demo_core_extraction(
pub(crate) async fn demo_core_extraction_execute(
app_handle: tauri::AppHandle,
state: &crate::AppState,
request: crate::DemoCoreExtractionRequest,
@@ -216,7 +216,7 @@ pub(crate) async fn execute_demo_core_extraction(
state
.demo_core_extraction_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let pipeline_request_result = crate::build_demo_core_extraction_pipeline_request(request);
let pipeline_request_result = build_pipeline_request(request);
let pipeline_request = match pipeline_request_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -236,10 +236,10 @@ pub(crate) async fn execute_demo_core_extraction(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::demo_core_extraction_summary_payload(summary));
return std::result::Result::Ok(summary_payload(summary));
}
pub(crate) fn cancel_demo_core_extraction(state: &crate::AppState) -> bool {
pub(crate) fn demo_core_extraction_cancel(state: &crate::AppState) -> bool {
let running = state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire);
state
.demo_core_extraction_cancel_requested()
@@ -247,7 +247,7 @@ pub(crate) fn cancel_demo_core_extraction(state: &crate::AppState) -> bool {
return running;
}
pub(crate) fn build_demo_core_extraction_pipeline_request(
fn build_pipeline_request(
request: crate::DemoCoreExtractionRequest,
) -> std::result::Result<kb_pipeline::CoreExtractionRequest, std::string::String> {
let source_result = match request.mode.trim() {
@@ -308,6 +308,25 @@ pub(crate) fn build_demo_core_extraction_pipeline_request(
return std::result::Result::Ok(pipeline_request);
}
fn summary_payload(
summary: kb_pipeline::CoreExtractionSummary,
) -> crate::DemoCoreExtractionSummaryPayload {
return crate::DemoCoreExtractionSummaryPayload {
processor_version: summary.processor_version,
selected: summary.selected,
started: summary.started,
completed: summary.completed,
skipped: summary.skipped,
extracted: summary.extracted,
failed: summary.failed,
cancelled_candidates: summary.cancelled_candidates,
not_started: summary.not_started,
cancelled: summary.cancelled,
started_at: summary.started_at,
finished_at: summary.finished_at,
};
}
fn split_signatures(text: std::option::Option<&str>) -> std::vec::Vec<std::string::String> {
let source = match text {
std::option::Option::Some(value) => value,
@@ -327,25 +346,6 @@ fn split_signatures(text: std::option::Option<&str>) -> std::vec::Vec<std::strin
return output;
}
pub(crate) fn demo_core_extraction_summary_payload(
summary: kb_pipeline::CoreExtractionSummary,
) -> crate::DemoCoreExtractionSummaryPayload {
return crate::DemoCoreExtractionSummaryPayload {
processor_version: summary.processor_version,
selected: summary.selected,
started: summary.started,
completed: summary.completed,
skipped: summary.skipped,
extracted: summary.extracted,
failed: summary.failed,
cancelled_candidates: summary.cancelled_candidates,
not_started: summary.not_started,
cancelled: summary.cancelled,
started_at: summary.started_at,
finished_at: summary.finished_at,
};
}
#[cfg(test)]
mod tests {
#[test]

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
// version: 33
// version: 37
//! Tauri commands and UI payloads for contextual instruction decode replay.
@@ -357,7 +357,289 @@ impl std::ops::Drop for crate::DemoDecodeReplayRunGuard<'_> {
}
}
pub(crate) fn register_active_campaign(
/// Returns available decoders and default replay bounds.
pub(crate) fn demo_decode_replay_options(
state: tauri::State<'_, crate::AppState>,
) -> crate::DemoDecodeReplayOptionsPayload {
let decoders = available_decoders();
let decoder_options = decoders
.iter()
.map(|decoder| {
let identity = decoder.identity();
return crate::DemoDecodeReplayDecoderOption {
name: identity.name,
version: identity.version,
program_ids: decoder
.surfaces()
.iter()
.map(|surface| return surface.program_id.to_string())
.collect(),
};
})
.collect::<std::vec::Vec<_>>();
let materializers = available_materializers();
let materializer_names = materializers
.iter()
.map(|materializer| {
let identity = materializer.identity();
return format!("{}@{}", identity.name, identity.version);
})
.collect::<std::vec::Vec<_>>();
let programs = kb_program_ids::registered_program_ids()
.iter()
.map(|entry| {
return crate::DemoDecodeReplayProgramOption {
code: entry.code().to_string(),
program_id: entry.program_id().to_string(),
};
})
.collect();
let running = state.demo_decode_replay_running().load(std::sync::atomic::Ordering::Acquire);
tracing::debug!(
target: crate::TRACING_TARGET,
action = "load_options",
pipeline_version = kb_pipeline::DECODE_PIPELINE_VERSION,
decoder_count = decoder_options.len(),
materializer_names = ?materializer_names,
default_limit = 100_u32,
default_max_concurrent_inputs = 4_u32,
running,
"return contextual decode replay options"
);
return crate::DemoDecodeReplayOptionsPayload {
pipeline_version: kb_pipeline::DECODE_PIPELINE_VERSION.to_string(),
decoders: decoder_options,
materializer_names,
programs,
default_limit: 100,
default_max_concurrent_inputs: 4,
running,
};
}
/// Executes one bounded contextual decode and optional materialization campaign.
pub(crate) async fn demo_decode_replay_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoDecodeReplayRequest,
) -> std::result::Result<crate::DemoDecodeReplaySummaryPayload, std::string::String> {
let campaign_id = kb_pipeline::new_decode_campaign_id();
tracing::debug!(
target: crate::TRACING_TARGET,
action = "execute",
campaign_id = %campaign_id,
instruction_state = %request.instruction_state,
signature_line_count = optional_line_count(&request.signatures_text),
program_id = ?request.program_id,
instruction_paths_text = ?request.instruction_paths_text,
decoder_names = ?request.decoder_names,
limit = request.limit,
max_concurrent_inputs = request.max_concurrent_inputs,
all_compatible = request.all_compatible,
force_replay = request.force_replay,
force_replay_all_matching = request.force_replay_all_matching,
materialize_after_decode = request.materialize_after_decode,
"received contextual decode replay command"
);
let acquire_result = state.demo_decode_replay_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
tracing::warn!(target: crate::TRACING_TARGET, action = "execute", campaign_id = %campaign_id, accepted = false, reason = "already_running", "reject contextual decode replay command");
return std::result::Result::Err(
"a contextual decode replay campaign is already running".to_string(),
);
}
let _run_guard = crate::DemoDecodeReplayRunGuard {
running: state.demo_decode_replay_running(),
campaign_id: state.demo_decode_replay_campaign_id(),
};
let register_result =
register_active_campaign(state.demo_decode_replay_campaign_id(), campaign_id.as_str());
if let std::result::Result::Err(error) = register_result {
tracing::error!(target: crate::TRACING_TARGET, action = "register_campaign", campaign_id = %campaign_id, error = %error, "cannot lock active contextual decode campaign slot");
return std::result::Result::Err(error);
}
state
.demo_decode_replay_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let pipeline_request_result = build_pipeline_request(request, campaign_id.clone());
let pipeline_request = match pipeline_request_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "build_request", campaign_id = %campaign_id, error = %error, "cannot build contextual decode replay request");
return std::result::Result::Err(error);
},
};
tracing::debug!(
target: crate::TRACING_TARGET,
action = "execute_pipeline",
campaign_id = %pipeline_request.campaign_id,
signature_count = pipeline_request.selection.signatures.len(),
signature_sample = ?text_sample(pipeline_request.selection.signatures.as_slice(), 5),
processing_states = ?pipeline_request.selection.processing_states,
min_slot = ?pipeline_request.selection.min_slot,
max_slot = ?pipeline_request.selection.max_slot,
program_ids = ?pipeline_request.selection.program_ids,
instruction_paths = ?pipeline_request.selection.instruction_paths,
incomplete_signatures = pipeline_request.selection.incomplete_signatures,
limit = pipeline_request.selection.limit,
decoder_names = ?pipeline_request.decoder_names,
dispatch_policy = ?pipeline_request.dispatch_policy,
max_concurrent_inputs = pipeline_request.max_concurrent_inputs,
force_replay = pipeline_request.force_replay,
force_replay_all_matching = pipeline_request.force_replay_all_matching,
materialize_after_decode = pipeline_request.materialize_after_decode,
"start contextual decode replay pipeline"
);
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "connect_store", campaign_id = %pipeline_request.campaign_id, error = %error, "cannot connect contextual decode replay store");
return std::result::Result::Err(error);
},
};
let decoders = available_decoders();
let materializers = available_materializers();
let observer = crate::DemoDecodeReplayObserver {
app_handle,
campaign_id: pipeline_request.campaign_id.clone(),
cancel_requested: state.demo_decode_replay_cancel_requested(),
};
let summary_result = kb_pipeline::execute_decode_replay(
&store,
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await;
let summary = match summary_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_pipeline", campaign_id = %pipeline_request.campaign_id, error = %error, "contextual decode replay pipeline failed");
return std::result::Result::Err(error.to_string());
},
};
tracing::debug!(
target: crate::TRACING_TARGET,
action = "execute_pipeline",
campaign_id = %summary.campaign_id,
selected = summary.selected,
started = summary.started,
completed = summary.completed,
unmatched = summary.unmatched,
not_started = summary.not_started,
failed_inputs = summary.failed_inputs,
processing_error_inputs = summary.processing_error_inputs,
cancelled = summary.cancelled,
processors = ?summary.processors,
"contextual decode replay command completed"
);
return std::result::Result::Ok(summary_payload(summary));
}
/// Requests cooperative cancellation of the current decode replay campaign.
pub(crate) fn demo_decode_replay_cancel(state: tauri::State<'_, crate::AppState>) -> bool {
let running = state.demo_decode_replay_running().load(std::sync::atomic::Ordering::Acquire);
state
.demo_decode_replay_cancel_requested()
.store(true, std::sync::atomic::Ordering::Release);
let campaign_lock_result = state.demo_decode_replay_campaign_id().lock();
let campaign_id = match campaign_lock_result {
std::result::Result::Ok(active_campaign_id) => active_campaign_id.clone(),
std::result::Result::Err(_) => std::option::Option::None,
};
tracing::debug!(target: crate::TRACING_TARGET, action = "cancel", campaign_id = ?campaign_id, running, cancellation_requested = true, "contextual decode replay cancellation command handled");
return running;
}
/// Loads read-only decode, materialization, ledger and coverage diagnostics.
pub(crate) async fn demo_decode_replay_diagnostics(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoDecodeDiagnosticsPayload, std::string::String> {
tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", coverage_limit = 500_u32, "load contextual decode replay diagnostics");
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let table_result = store.known_table_diagnostics().await;
let all_tables = match table_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let selected_tables: std::vec::Vec<crate::DemoSqlTableSnapshot> = all_tables
.iter()
.filter(|table| {
return table.table_name.starts_with("kb_sol_decode_")
|| table.table_name.starts_with("kb_sol_mat_")
|| table.table_name == "kb_sol_ops_processing_ledger";
})
.map(crate::table_snapshot_from_pg)
.collect();
let coverage_result = kb_store::DecodePipelineStore::list_decode_coverage_summary(
&store,
std::option::Option::None,
std::option::Option::None,
500,
)
.await;
let coverage = match coverage_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", table_count = selected_tables.len(), coverage_count = coverage.len(), "contextual decode replay diagnostics loaded");
return std::result::Result::Ok(crate::DemoDecodeDiagnosticsPayload {
tables: selected_tables,
coverage: coverage.into_iter().map(coverage_payload).collect(),
});
}
/// Loads a bounded journal of committed SPL Memo transaction annotations.
pub(crate) async fn demo_decode_replay_annotations(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoTransactionAnnotationRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoTransactionAnnotationRow>, std::string::String> {
let filter_result = kb_store::MaterializedEventFilter::new(
std::option::Option::Some("materializer.transaction.annotations".to_string()),
std::option::Option::Some("transaction_annotation".to_string()),
request.signature_contains,
request.limit,
);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, action = "load_transaction_annotations", signature_contains = ?filter.signature_contains, limit = filter.limit, "load bounded committed transaction annotation journal");
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result =
kb_store::DecodePipelineStore::list_materialized_events(&store, &filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
let mapped = annotation_payload(row);
match mapped {
std::result::Result::Ok(value) => output.push(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
}
tracing::debug!(target: crate::TRACING_TARGET, action = "load_transaction_annotations", row_count = output.len(), "bounded committed transaction annotation journal loaded");
return std::result::Result::Ok(output);
}
fn register_active_campaign(
campaign_slot: &std::sync::Mutex<std::option::Option<std::string::String>>,
campaign_id: &str,
) -> std::result::Result<(), std::string::String> {
@@ -374,8 +656,7 @@ pub(crate) fn register_active_campaign(
return std::result::Result::Ok(());
}
pub(crate) fn available_materializers()
-> std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> {
fn available_materializers() -> std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> {
return std::vec![
std::sync::Arc::new(kb_lib::MtAdminMaterializer),
std::sync::Arc::new(kb_lib::MtComplianceAuditMaterializer),
@@ -388,8 +669,7 @@ pub(crate) fn available_materializers()
];
}
pub(crate) fn available_decoders()
-> std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> {
fn available_decoders() -> std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> {
return std::vec![
std::sync::Arc::new(kb_lib::DcSolanaCoreDecoder),
std::sync::Arc::new(kb_lib::DcMetadataMetaplexTokenMetadataDecoder),
@@ -401,7 +681,7 @@ pub(crate) fn available_decoders()
];
}
pub(crate) fn build_demo_decode_replay_pipeline_request(
fn build_pipeline_request(
request: crate::DemoDecodeReplayRequest,
campaign_id: std::string::String,
) -> std::result::Result<kb_pipeline::DecodeReplayRequest, std::string::String> {
@@ -474,7 +754,7 @@ pub(crate) fn build_demo_decode_replay_pipeline_request(
return std::result::Result::Ok(pipeline_request);
}
pub(crate) fn optional_line_count(value: &std::option::Option<std::string::String>) -> usize {
fn optional_line_count(value: &std::option::Option<std::string::String>) -> usize {
return match value {
std::option::Option::Some(text) => {
text.lines().filter(|line| return !line.trim().is_empty()).count()
@@ -483,7 +763,7 @@ pub(crate) fn optional_line_count(value: &std::option::Option<std::string::Strin
};
}
pub(crate) fn text_sample(values: &[std::string::String], limit: usize) -> std::vec::Vec<&str> {
fn text_sample(values: &[std::string::String], limit: usize) -> std::vec::Vec<&str> {
return values.iter().take(limit).map(std::string::String::as_str).collect();
}
@@ -538,7 +818,7 @@ fn split_lines(text: std::option::Option<&str>) -> std::vec::Vec<std::string::St
return output;
}
pub(crate) fn demo_decode_replay_summary_payload(
fn summary_payload(
summary: kb_pipeline::DecodeReplaySummary,
) -> crate::DemoDecodeReplaySummaryPayload {
return crate::DemoDecodeReplaySummaryPayload {
@@ -576,7 +856,7 @@ pub(crate) fn demo_decode_replay_summary_payload(
};
}
pub(crate) fn coverage_payload(
fn coverage_payload(
value: kb_store::DecodeCoverageSummaryRow,
) -> crate::DemoDecodeCoverageSummaryPayload {
return crate::DemoDecodeCoverageSummaryPayload {
@@ -597,7 +877,7 @@ pub(crate) fn coverage_payload(
};
}
pub(crate) fn annotation_payload(
fn annotation_payload(
row: kb_store::MaterializedEventQueryRow,
) -> std::result::Result<crate::DemoTransactionAnnotationRow, std::string::String> {
if row.processor_name != "materializer.transaction.annotations"
@@ -742,7 +1022,7 @@ mod tests {
#[test]
fn native_memo_token_2022_elgamal_classic_token_and_ata_decoders_are_registered() {
let decoders = crate::available_decoders();
let decoders = super::available_decoders();
assert_eq!(decoders.len(), 7);
let mut names = decoders
.iter()
@@ -764,7 +1044,7 @@ mod tests {
#[test]
fn runtime_materializer_registry_is_complete_for_current_instructional_surfaces() {
let materializers = crate::available_materializers();
let materializers = super::available_materializers();
assert_eq!(materializers.len(), 8);
let names = materializers
.iter()
@@ -798,7 +1078,7 @@ mod tests {
),
] {
let observation = ata_observation(entry);
let owners = crate::available_materializers()
let owners = super::available_materializers()
.iter()
.filter(|materializer| return materializer.accepts_observation(&observation))
.map(|materializer| return materializer.identity().name)
@@ -836,7 +1116,7 @@ mod tests {
#[test]
fn force_replay_requires_signatures_or_all_matching_authorization() {
let result = crate::build_demo_decode_replay_pipeline_request(
let result = super::build_pipeline_request(
request(std::option::Option::None, true, false, false),
"decode-test".to_string(),
);
@@ -845,7 +1125,7 @@ mod tests {
#[test]
fn force_replay_all_matching_is_forwarded() {
let result = crate::build_demo_decode_replay_pipeline_request(
let result = super::build_pipeline_request(
request(std::option::Option::None, true, true, false),
"decode-test".to_string(),
);
@@ -880,7 +1160,7 @@ mod tests {
#[test]
fn register_active_campaign_sets_slot_without_exposing_guard() {
let campaign_id = std::sync::Mutex::new(std::option::Option::None);
let result = crate::register_active_campaign(&campaign_id, "decode-test");
let result = super::register_active_campaign(&campaign_id, "decode-test");
assert!(result.is_ok());
let lock_result = campaign_id.lock();
let active_campaign_id = match lock_result {
@@ -894,10 +1174,7 @@ mod tests {
fn incomplete_signature_state_uses_actionable_states_and_expansion() {
let mut request = request(std::option::Option::None, false, false, true);
request.instruction_state = "incomplete_signatures".to_string();
let result = crate::build_demo_decode_replay_pipeline_request(
request,
"decode-incomplete".to_string(),
);
let result = super::build_pipeline_request(request, "decode-incomplete".to_string());
let pipeline_request = match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("request mapping failed: {error}"),
@@ -944,7 +1221,7 @@ mod tests {
created_at: "created".to_string(),
updated_at: "updated".to_string(),
};
let result = crate::annotation_payload(row);
let result = super::annotation_payload(row);
let mapped = match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("annotation mapping failed: {error}"),
@@ -971,6 +1248,6 @@ mod tests {
created_at: "created".to_string(),
updated_at: "updated".to_string(),
};
assert!(crate::annotation_payload(row).is_err());
assert!(super::annotation_payload(row).is_err());
}
}

View File

@@ -27,14 +27,6 @@ pub(crate) struct DemoExecutionDevnetStoreReadinessPayload {
pub(crate) expected_tables: u32,
}
/// Converts one collection length to the bounded UI counter type.
fn bounded_u32(value: usize) -> u32 {
return match u32::try_from(value) {
std::result::Result::Ok(converted) => converted,
std::result::Result::Err(_) => u32::MAX,
};
}
/// Converts the reusable scenario readiness report to the desktop TS-RS payload.
pub(crate) fn devnet_store_readiness_payload(
readiness: kb_pipeline_demo_scenarios::DevnetProfileStoreReadiness,
@@ -48,3 +40,24 @@ pub(crate) fn devnet_store_readiness_payload(
expected_tables: bounded_u32(readiness.expected_tables),
};
}
/// Serializes a diagnostic value to indented JSON without panicking.
pub(crate) fn pretty_json<T>(value: &T) -> std::string::String
where
T: serde::Serialize,
{
return match serde_json::to_string_pretty(value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
serde_json::json!({"serializationError": error.to_string()}).to_string()
},
};
}
/// Converts one collection length to the bounded UI counter type.
fn bounded_u32(value: usize) -> u32 {
return match u32::try_from(value) {
std::result::Result::Ok(converted) => converted,
std::result::Result::Err(_) => u32::MAX,
};
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_execution_solana_core.rs
// version: 8
// version: 12
//! Tauri adapter for bounded Solana Core execution on Devnet.
@@ -368,7 +368,263 @@ impl std::ops::Drop for crate::DemoExecutionSolanaCoreRunGuard<'_> {
}
}
pub(crate) fn devnet_profile_options(
pub(crate) fn select_devnet_profile(
config: &kb_config::AppConfig,
profile_name: &str,
) -> std::result::Result<kb_config::ProfileConfig, std::string::String> {
return kb_pipeline_demo_scenarios::resolve_demo_devnet_profile(
config,
std::option::Option::Some(profile_name),
)
.map_err(|error| return error.to_string());
}
pub(crate) fn execution_cluster_code(
cluster: kb_lib::ExApiExecutionCluster,
) -> std::string::String {
return match cluster {
kb_lib::ExApiExecutionCluster::Localnet => "localnet".to_string(),
kb_lib::ExApiExecutionCluster::Devnet => "devnet".to_string(),
kb_lib::ExApiExecutionCluster::Testnet => "testnet".to_string(),
kb_lib::ExApiExecutionCluster::Mainnet => "mainnet".to_string(),
};
}
pub(crate) fn confirmation_status_code(
status: kb_lib::ExApiExecutionConfirmationStatus,
) -> std::string::String {
return match status {
kb_lib::ExApiExecutionConfirmationStatus::Processed => "processed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Confirmed => "confirmed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Finalized => "finalized".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Failed => "failed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Expired => "expired".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::TimedOut => "timed_out".to_string(),
};
}
/// Verifies or initializes the PostgreSQL schema selected by one Devnet profile.
pub(crate) async fn demo_execution_devnet_prepare_profile(
state: tauri::State<'_, crate::AppState>,
profile_name: std::string::String,
) -> std::result::Result<crate::DemoExecutionDevnetStoreReadinessPayload, std::string::String> {
let profile = match crate::select_devnet_profile(state.app_config(), profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let readiness =
match kb_pipeline_demo_scenarios::prepare_demo_devnet_profile_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(error.to_string());
},
};
return std::result::Result::Ok(crate::devnet_store_readiness_payload(readiness));
}
/// Returns compatible Devnet profiles and conservative defaults.
pub(crate) fn demo_execution_solana_core_options(
state: tauri::State<'_, crate::AppState>,
) -> crate::DemoExecutionSolanaCoreOptionsPayload {
let profiles = devnet_profile_options(state.app_config());
let default_profile_name = profiles.first().map(|profile| return profile.name.clone());
return crate::DemoExecutionSolanaCoreOptionsPayload {
profiles,
default_profile_name,
default_transfer_lamports: 1_000_000,
running: state
.demo_execution_solana_core_running()
.load(std::sync::atomic::Ordering::Acquire),
};
}
/// Generates a disposable recipient public key without exposing its private key.
pub(crate) fn demo_execution_solana_core_generate_recipient()
-> std::result::Result<crate::DemoExecutionSolanaCoreGeneratedRecipientPayload, std::string::String>
{
let alias = match kb_wallet::WalletAlias::parse("demo-devnet-recipient") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let wallet = kb_wallet::TemporaryWallet::generate(alias);
return std::result::Result::Ok(crate::DemoExecutionSolanaCoreGeneratedRecipientPayload {
public_key: wallet.public_key(),
});
}
/// Executes one bounded System transfer simulation or Devnet submission.
pub(crate) async fn demo_execution_solana_core_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoExecutionSolanaCoreRequest,
) -> std::result::Result<crate::DemoExecutionSolanaCoreSummaryPayload, std::string::String> {
let acquire_result = state.demo_execution_solana_core_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err("a Solana Core execution is already running".to_string());
}
let _run_guard = crate::DemoExecutionSolanaCoreRunGuard {
running: state.demo_execution_solana_core_running(),
};
state
.demo_execution_solana_core_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let initialize_result = store.initialize_store_schema().await;
if let std::result::Result::Err(error) = initialize_result {
return std::result::Result::Err(error.to_string());
}
let mut pipeline_request = kb_pipeline_demo_scenarios::DevnetSystemTransferRequest::new(
format!("demo-execution-{}", chrono::Utc::now().timestamp_micros()),
kb_lib::MdPubkey(request.recipient.trim().to_string()),
request.lamports,
);
pipeline_request.airdrop_lamports = request.airdrop_lamports;
pipeline_request.submit = request.submit;
pipeline_request.operator_confirmed = request.operator_confirmed;
pipeline_request.post_validation_max_retries = 20;
pipeline_request.force_post_validation_replay = request.force_post_validation_replay;
pipeline_request.materialize_after_decode = request.materialize_after_decode;
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> =
std::vec![std::sync::Arc::new(kb_lib::DcSolanaCoreDecoder)];
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> =
if request.materialize_after_decode {
std::vec![
std::sync::Arc::new(kb_lib::MtAdminMaterializer),
std::sync::Arc::new(kb_lib::MtComplianceAuditMaterializer,),
std::sync::Arc::new(kb_lib::MtLifecycleMaterializer),
std::sync::Arc::new(kb_lib::MtStakingMaterializer),
]
} else {
std::vec::Vec::new()
};
let observer = crate::DemoExecutionSolanaCoreObserver {
app_handle,
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
};
let workspace_root = crate::workspace_root_dir();
let summary = match kb_pipeline_demo_scenarios::execute_devnet_system_transfer(
&http_pool,
&store,
&profile,
workspace_root.as_path(),
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(summary_payload(summary));
}
/// Executes one Memo v4 simulation or explicitly authorized Devnet submission.
pub(crate) async fn demo_execution_spl_memo_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoExecutionMemoRequest,
) -> std::result::Result<crate::DemoExecutionMemoSummaryPayload, std::string::String> {
let acquire_result = state.demo_execution_solana_core_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err("a Devnet execution is already running".to_string());
}
let _run_guard = crate::DemoExecutionSolanaCoreRunGuard {
running: state.demo_execution_solana_core_running(),
};
state
.demo_execution_solana_core_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let mut pipeline_request = kb_pipeline_demo_scenarios::DevnetMemoExecutionRequest::new(
format!("demo-memo-execution-{}", chrono::Utc::now().timestamp_micros()),
request.message,
);
pipeline_request.include_wallet_as_memo_signer = request.include_wallet_as_memo_signer;
pipeline_request.submit = request.submit;
pipeline_request.operator_confirmed = request.operator_confirmed;
pipeline_request.post_validation_max_retries = 20;
pipeline_request.force_post_validation_replay = request.force_post_validation_replay;
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> =
std::vec![std::sync::Arc::new(kb_lib::DcSplMemoDecoder)];
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> =
std::vec![std::sync::Arc::new(kb_lib::MtTransactionAnnotationMaterializer,)];
let observer = crate::DemoExecutionSolanaCoreObserver {
app_handle,
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
};
let workspace_root = crate::workspace_root_dir();
let summary = match kb_pipeline_demo_scenarios::execute_devnet_memo(
&http_pool,
&store,
&profile,
workspace_root.as_path(),
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(memo_summary_payload(summary));
}
/// Requests cooperative cancellation before the next execution stage.
pub(crate) fn demo_execution_solana_core_cancel(state: tauri::State<'_, crate::AppState>) -> bool {
let running = state
.demo_execution_solana_core_running()
.load(std::sync::atomic::Ordering::Acquire);
if !running {
return false;
}
state
.demo_execution_solana_core_cancel_requested()
.store(true, std::sync::atomic::Ordering::Release);
return true;
}
fn devnet_profile_options(
config: &kb_config::AppConfig,
) -> std::vec::Vec<crate::DemoExecutionSolanaCoreProfileOption> {
let mut output = std::vec::Vec::new();
@@ -391,18 +647,7 @@ pub(crate) fn devnet_profile_options(
return output;
}
pub(crate) fn select_devnet_profile(
config: &kb_config::AppConfig,
profile_name: &str,
) -> std::result::Result<kb_config::ProfileConfig, std::string::String> {
return kb_pipeline_demo_scenarios::resolve_demo_devnet_profile(
config,
std::option::Option::Some(profile_name),
)
.map_err(|error| return error.to_string());
}
pub(crate) fn demo_execution_solana_core_summary_payload(
fn summary_payload(
summary: kb_pipeline_demo_scenarios::DevnetSystemTransferSummary,
) -> crate::DemoExecutionSolanaCoreSummaryPayload {
let plan_json = crate::pretty_json(&summary.plan);
@@ -483,7 +728,7 @@ pub(crate) fn demo_execution_solana_core_summary_payload(
};
}
pub(crate) fn demo_execution_solana_core_memo_summary_payload(
fn memo_summary_payload(
summary: kb_pipeline_demo_scenarios::DevnetMemoExecutionSummary,
) -> crate::DemoExecutionMemoSummaryPayload {
let plan_json = crate::pretty_json(&summary.plan);
@@ -610,42 +855,6 @@ pub(crate) fn demo_execution_solana_core_memo_summary_payload(
};
}
pub(crate) fn execution_cluster_code(
cluster: kb_lib::ExApiExecutionCluster,
) -> std::string::String {
return match cluster {
kb_lib::ExApiExecutionCluster::Localnet => "localnet".to_string(),
kb_lib::ExApiExecutionCluster::Devnet => "devnet".to_string(),
kb_lib::ExApiExecutionCluster::Testnet => "testnet".to_string(),
kb_lib::ExApiExecutionCluster::Mainnet => "mainnet".to_string(),
};
}
pub(crate) fn confirmation_status_code(
status: kb_lib::ExApiExecutionConfirmationStatus,
) -> std::string::String {
return match status {
kb_lib::ExApiExecutionConfirmationStatus::Processed => "processed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Confirmed => "confirmed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Finalized => "finalized".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Failed => "failed".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::Expired => "expired".to_string(),
kb_lib::ExApiExecutionConfirmationStatus::TimedOut => "timed_out".to_string(),
};
}
pub(crate) fn pretty_json<T>(value: &T) -> std::string::String
where
T: serde::Serialize,
{
return match serde_json::to_string_pretty(value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
serde_json::json!({"serializationError": error.to_string()}).to_string()
},
};
}
#[cfg(test)]
mod tests {
#[test]
@@ -655,7 +864,7 @@ mod tests {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("example config parse failed: {error}"),
};
let profiles = crate::devnet_profile_options(&config);
let profiles = super::devnet_profile_options(&config);
assert_eq!(profiles.len(), 1);
assert_eq!(profiles[0].name, "local_devnet");
assert!(profiles[0].send_enabled);

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_execution_spl.rs
// version: 3
// version: 5
//! Dedicated Tauri window for bounded SPL execution on Devnet.
@@ -27,6 +27,45 @@ pub(crate) struct DevnetSplValidationScenarioPayload {
pub required_fixture_variables: std::vec::Vec<std::string::String>,
}
/// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6.
pub(crate) fn demo_execution_spl_validation_scenarios()
-> std::vec::Vec<crate::DevnetSplValidationScenarioPayload> {
return kb_pipeline_demo_scenarios::devnet_spl_validation_scenarios()
.into_iter()
.map(|scenario| {
return crate::DevnetSplValidationScenarioPayload {
id: scenario.id,
label: scenario.label,
family: match scenario.family {
kb_pipeline_demo_scenarios::DevnetSplValidationFamily::Token2022Public => {
"token_2022_public".to_string()
},
kb_pipeline_demo_scenarios::DevnetSplValidationFamily::ElGamalRegistry => {
"elgamal_registry".to_string()
},
kb_pipeline_demo_scenarios::DevnetSplValidationFamily::Token2022Confidential => {
"token_2022_confidential".to_string()
},
},
operation_code: scenario.operation_code,
implementation_status: match scenario.implementation_status {
kb_pipeline_demo_scenarios::DevnetSplValidationImplementationStatus::Executable => {
"executable".to_string()
},
kb_pipeline_demo_scenarios::DevnetSplValidationImplementationStatus::BackendReady => {
"backend_ready".to_string()
},
kb_pipeline_demo_scenarios::DevnetSplValidationImplementationStatus::ProofFixtureRequired => {
"proof_fixture_required".to_string()
},
},
proof_required: scenario.proof_required,
required_fixture_variables: scenario.required_fixture_variables,
};
})
.collect();
}
#[cfg(test)]
mod tests {

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_http.rs
// version: 9
// version: 13
//! HTTP JSON-RPC demo commands.
@@ -99,7 +99,7 @@ pub(crate) struct DemoHttpExecutionPayload {
pub(crate) response_json: std::string::String,
}
pub(crate) async fn demo_http_execute_request_inner(
pub(crate) async fn demo_http_execute_request(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoHttpRequest,
) -> std::result::Result<crate::DemoHttpExecutionPayload, std::string::String> {
@@ -119,7 +119,7 @@ pub(crate) async fn demo_http_execute_request_inner(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let params = match build_demo_http_params(
let params = match build_http_params(
&method,
request.first_arg.as_deref(),
config_json_value,
@@ -155,7 +155,22 @@ pub(crate) async fn demo_http_execute_request_inner(
});
}
pub(crate) fn build_http_role_options(
pub(crate) fn demo_http_list_pool_clients(
state: tauri::State<'_, crate::AppState>,
) -> std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot> {
return state.http_pool().snapshot();
}
pub(crate) fn demo_http_options(
state: tauri::State<'_, crate::AppState>,
) -> crate::DemoHttpOptionsPayload {
return crate::DemoHttpOptionsPayload {
roles: build_http_role_options(state.http_pool().snapshot()),
methods: build_http_method_options(),
};
}
fn build_http_role_options(
snapshots: std::vec::Vec<kb_onchain_transport::HttpPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoHttpRoleOption> {
let mut roles = std::collections::BTreeMap::<
@@ -183,7 +198,7 @@ pub(crate) fn build_http_role_options(
return options;
}
pub(crate) fn build_http_method_options() -> std::vec::Vec<crate::DemoHttpMethodOption> {
fn build_http_method_options() -> std::vec::Vec<crate::DemoHttpMethodOption> {
let methods = [
("getAccountInfo", "Compte Solana", true, true),
("getBalance", "Balance SOL", true, true),
@@ -250,53 +265,7 @@ pub(crate) fn build_http_method_options() -> std::vec::Vec<crate::DemoHttpMethod
return options;
}
fn parse_optional_json(
config_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<serde_json::Value>, std::string::String> {
let config_text = match config_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if config_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&config_text);
return match parse_result {
std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)),
std::result::Result::Err(error) => {
std::result::Result::Err(format!("invalid configJson: {error}"))
},
};
}
fn parse_optional_params_json(
params_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<std::vec::Vec<serde_json::Value>>, std::string::String>
{
let params_text = match params_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if params_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&params_text);
let value = match parse_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid paramsJson: {error}"));
},
};
let array = match value.as_array() {
std::option::Option::Some(array) => array.clone(),
std::option::Option::None => {
return std::result::Result::Err("paramsJson must be a JSON array".to_string());
},
};
return std::result::Result::Ok(std::option::Option::Some(array));
}
fn build_demo_http_params(
fn build_http_params(
method: &str,
first_arg: std::option::Option<&str>,
config_json: std::option::Option<serde_json::Value>,
@@ -352,6 +321,52 @@ fn build_demo_http_params(
return std::result::Result::Ok(params);
}
fn parse_optional_json(
config_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<serde_json::Value>, std::string::String> {
let config_text = match config_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if config_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&config_text);
return match parse_result {
std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)),
std::result::Result::Err(error) => {
std::result::Result::Err(format!("invalid configJson: {error}"))
},
};
}
fn parse_optional_params_json(
params_json: std::option::Option<std::string::String>,
) -> std::result::Result<std::option::Option<std::vec::Vec<serde_json::Value>>, std::string::String>
{
let params_text = match params_json {
std::option::Option::Some(value) => value.trim().to_string(),
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
};
if params_text.is_empty() {
return std::result::Result::Ok(std::option::Option::None);
}
let parse_result = serde_json::from_str::<serde_json::Value>(&params_text);
let value = match parse_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid paramsJson: {error}"));
},
};
let array = match value.as_array() {
std::option::Option::Some(array) => array.clone(),
std::option::Option::None => {
return std::result::Result::Err("paramsJson must be a JSON array".to_string());
},
};
return std::result::Result::Ok(std::option::Option::Some(array));
}
fn method_class_to_string(method_class: kb_onchain_transport::HttpMethodClass) -> &'static str {
return match method_class {
kb_onchain_transport::HttpMethodClass::GeneralRpc => "GeneralRpc",
@@ -364,7 +379,7 @@ fn method_class_to_string(method_class: kb_onchain_transport::HttpMethodClass) -
mod tests {
#[test]
fn raw_params_override_first_argument_and_config() {
let params = super::build_demo_http_params(
let params = super::build_http_params(
"getBalance",
std::option::Option::Some("ignored"),
std::option::Option::Some(serde_json::json!({"commitment":"confirmed"})),
@@ -375,7 +390,7 @@ mod tests {
#[test]
fn required_first_argument_fails_closed() {
let params = super::build_demo_http_params(
let params = super::build_http_params(
"getBalance",
std::option::Option::None,
std::option::Option::None,
@@ -386,7 +401,7 @@ mod tests {
#[test]
fn method_inventory_is_unique_and_classified() {
let options = crate::build_http_method_options();
let options = super::build_http_method_options();
let mut names = std::collections::BTreeSet::new();
for option in &options {
assert!(names.insert(option.method.as_str()));

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
// version: 7
// version: 11
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
@@ -157,7 +157,193 @@ pub(crate) struct DemoSplAtaJournalRow {
pub(crate) payload_json: std::string::String,
}
pub(crate) async fn load_profile_wallet(
/// Derives the representative profile wallet ATA without exposing private key material.
pub(crate) async fn demo_spl_ata_derive(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplAtaDerivationRequest,
) -> std::result::Result<crate::DemoSplAtaDerivationPayload, std::string::String> {
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet = match load_profile_wallet(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet_owner = wallet.public_key();
let token_program = match parse_token_program(&request.token_program) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let associated_token_account =
match derive_ata(wallet_owner.as_str(), request.mint.trim(), token_program.program_id()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::DemoSplAtaDerivationPayload {
payer: wallet_owner.clone(),
wallet_owner,
token_program_id: token_program.program_id().to_string(),
associated_token_account,
});
}
/// Executes one representative Create or CreateIdempotent ATA flow.
pub(crate) async fn demo_execution_spl_ata_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoExecutionSplAtaRequest,
) -> std::result::Result<crate::DemoExecutionSplAtaSummaryPayload, std::string::String> {
let acquired = state.demo_execution_solana_core_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquired.is_err() {
return std::result::Result::Err("a Devnet execution is already running".to_string());
}
let _run_guard = crate::DemoExecutionSolanaCoreRunGuard {
running: state.demo_execution_solana_core_running(),
};
state
.demo_execution_solana_core_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let profile_wallet = match load_profile_wallet(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if request.wallet_owner.trim() != profile_wallet.public_key() {
return std::result::Result::Err(
"the representative ATA panel requires the profile wallet as wallet owner".to_string(),
);
}
let token_program = match parse_token_program(&request.token_program) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let wallet_owner = kb_lib::MdPubkey(request.wallet_owner.trim().to_string());
let mint = kb_lib::MdPubkey(request.mint.trim().to_string());
let operation = match request.mode.as_str() {
"create" => kb_lib::ExSplAssociatedTokenAccountOperation::Create {
wallet_owner,
mint,
token_program,
},
"create_idempotent" => kb_lib::ExSplAssociatedTokenAccountOperation::CreateIdempotent {
wallet_owner,
mint,
token_program,
},
_ => {
return std::result::Result::Err(
"ATA mode must be create or create_idempotent".to_string(),
);
},
};
let mut pipeline_request =
kb_pipeline_demo_scenarios::DevnetSplAssociatedTokenAccountExecutionRequest::new(
format!("demo-spl-ata-execution-{}", chrono::Utc::now().timestamp_micros()),
operation,
);
pipeline_request.submit = request.submit;
pipeline_request.operator_confirmed = request.operator_confirmed;
pipeline_request.post_validation_max_retries = 20;
pipeline_request.force_post_validation_replay = request.force_post_validation_replay;
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> = std::vec![
std::sync::Arc::new(kb_lib::DcSplAssociatedTokenAccountDecoder),
std::sync::Arc::new(kb_lib::DcSplTokenDecoder),
];
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> = std::vec![
std::sync::Arc::new(kb_lib::MtTokenAccountsMaterializer),
std::sync::Arc::new(kb_lib::MtRiskMaterializer),
];
let observer = crate::DemoExecutionSolanaCoreObserver {
app_handle,
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
};
let summary = match kb_pipeline_demo_scenarios::execute_devnet_spl_associated_token_account(
&http_pool,
&store,
&profile,
crate::workspace_root_dir().as_path(),
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(summary_payload(summary));
}
/// Loads a bounded journal of ATA-owned lifecycle facts.
pub(crate) async fn demo_spl_ata_journal(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplAtaJournalRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSplAtaJournalRow>, std::string::String> {
if request.limit == 0 || request.limit > 500 {
return std::result::Result::Err("ATA journal limit must be between 1 and 500".to_string());
}
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let filter = match kb_store::MaterializedEventFilter::new(
std::option::Option::Some("materializer.token.accounts".to_string()),
std::option::Option::None,
request.signature_contains.clone(),
500,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let rows = match kb_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let mut output = std::vec::Vec::new();
for row in rows {
if row.source_decoder_name != "spl.associated_token_account"
|| !journal_matches(&row.payload_json, &request)
{
continue;
}
output.push(journal_row(row));
if output.len() >= request.limit as usize {
break;
}
}
return std::result::Result::Ok(output);
}
async fn load_profile_wallet(
profile: &kb_config::ProfileConfig,
) -> std::result::Result<kb_wallet::TemporaryWallet, std::string::String> {
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
@@ -180,7 +366,7 @@ pub(crate) async fn load_profile_wallet(
};
}
pub(crate) fn parse_token_program(
fn parse_token_program(
value: &str,
) -> std::result::Result<kb_lib::ExSplAssociatedTokenProgram, std::string::String> {
return match value.trim() {
@@ -190,7 +376,7 @@ pub(crate) fn parse_token_program(
};
}
pub(crate) fn derive_ata(
fn derive_ata(
wallet: &str,
mint: &str,
token_program: &str,
@@ -223,7 +409,7 @@ pub(crate) fn derive_ata(
);
}
pub(crate) fn demo_spl_ata_summary_payload(
fn summary_payload(
summary: kb_pipeline_demo_scenarios::DevnetSplAssociatedTokenAccountExecutionSummary,
) -> crate::DemoExecutionSplAtaSummaryPayload {
let (associated_token_account, token_program_id) = match summary.plan.instructions.first() {
@@ -356,10 +542,7 @@ pub(crate) fn demo_spl_ata_summary_payload(
};
}
pub(crate) fn journal_matches(
payload: &serde_json::Value,
request: &crate::DemoSplAtaJournalRequest,
) -> bool {
fn journal_matches(payload: &serde_json::Value, request: &crate::DemoSplAtaJournalRequest) -> bool {
for (expected, key) in [
(&request.operation, "operation"),
(&request.mint, "mint"),
@@ -375,7 +558,7 @@ pub(crate) fn journal_matches(
return true;
}
pub(crate) fn journal_row(row: kb_store::MaterializedEventQueryRow) -> crate::DemoSplAtaJournalRow {
fn journal_row(row: kb_store::MaterializedEventQueryRow) -> crate::DemoSplAtaJournalRow {
let text = |key: &str| {
return row
.payload_json
@@ -404,9 +587,9 @@ mod tests {
fn classic_and_token_2022_derivations_are_distinct_and_canonical() {
let wallet = kb_program_ids::SYSTEM_PROGRAM_ID;
let mint = "So11111111111111111111111111111111111111112";
let classic = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_PROGRAM_ID)
let classic = super::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_PROGRAM_ID)
.unwrap_or_else(|error| panic!("classic derivation failed: {error}"));
let token_2022 = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
let token_2022 = super::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID)
.unwrap_or_else(|error| panic!("Token-2022 derivation failed: {error}"));
assert_eq!(classic, "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs");
assert_eq!(token_2022, "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS");
@@ -422,7 +605,7 @@ mod tests {
operation: std::option::Option::Some("create_idempotent".to_string()),
limit: 100,
};
assert!(crate::journal_matches(
assert!(super::journal_matches(
&serde_json::json!({
"operation": "create_idempotent",
"mint": "mint111",
@@ -430,7 +613,7 @@ mod tests {
}),
&request,
));
assert!(!crate::journal_matches(
assert!(!super::journal_matches(
&serde_json::json!({
"operation": "create",
"mint": "mint111",

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_token.rs
// version: 4
// version: 8
//! Thin Tauri adapter for classic SPL Token execution and materialized journals.
@@ -153,7 +153,152 @@ pub(crate) struct DemoSplTokenJournalRow {
pub(crate) updated_at: std::string::String,
}
pub(crate) fn demo_spl_token_summary_payload(
/// Executes one checked SPL Token simulation or explicitly authorized Devnet submission.
pub(crate) async fn demo_execution_spl_token_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoExecutionSplTokenRequest,
) -> std::result::Result<crate::DemoExecutionSplTokenSummaryPayload, std::string::String> {
let acquire_result = state.demo_execution_solana_core_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err("a Devnet execution is already running".to_string());
}
let _run_guard = crate::DemoExecutionSolanaCoreRunGuard {
running: state.demo_execution_solana_core_running(),
};
state
.demo_execution_solana_core_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let amount_raw = request.amount_raw.trim().to_string();
let operation = kb_lib::ExSplClassicTokenOperation::Instruction {
value: kb_lib::ExSplClassicTokenSingleOperation::TransferChecked {
source: kb_lib::MdPubkey(request.source.trim().to_string()),
mint: kb_lib::MdPubkey(request.mint.trim().to_string()),
destination: kb_lib::MdPubkey(request.destination.trim().to_string()),
authority: kb_lib::ExSplClassicTokenAuthority {
authority: kb_lib::MdPubkey(request.authority.trim().to_string()),
multisig_signers: std::vec::Vec::new(),
},
amount: kb_lib::ExSplClassicTokenAmount(amount_raw.clone()),
decimals: request.decimals,
},
};
let mut pipeline_request = kb_pipeline_demo_scenarios::DevnetSplTokenExecutionRequest::new(
format!("demo-spl-token-execution-{}", chrono::Utc::now().timestamp_micros()),
operation,
);
pipeline_request.submit = request.submit;
pipeline_request.operator_confirmed = request.operator_confirmed;
pipeline_request.post_validation_max_retries = 20;
pipeline_request.force_post_validation_replay = request.force_post_validation_replay;
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> =
std::vec![std::sync::Arc::new(kb_lib::DcSplTokenDecoder)];
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> = std::vec![
std::sync::Arc::new(kb_lib::MtTokenAccountsMaterializer),
std::sync::Arc::new(kb_lib::MtAdminMaterializer),
std::sync::Arc::new(kb_lib::MtRiskMaterializer),
];
let observer = crate::DemoExecutionSolanaCoreObserver {
app_handle,
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
};
let workspace_root = crate::workspace_root_dir();
let summary = match kb_pipeline_demo_scenarios::execute_devnet_spl_token(
&http_pool,
&store,
&profile,
workspace_root.as_path(),
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(summary_payload(summary, amount_raw, request.decimals));
}
/// Loads a bounded, typed journal of committed classic SPL Token projections.
pub(crate) async fn demo_spl_token_journal(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplTokenJournalRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSplTokenJournalRow>, std::string::String> {
let validated = match validate_journal_request(request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let profile =
match crate::select_devnet_profile(state.app_config(), validated.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let needs_payload_filter =
validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some();
let query_limit = if needs_payload_filter {
kb_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS
} else {
validated.limit
};
let filter = match kb_store::MaterializedEventFilter::new(
std::option::Option::None,
validated.family.clone(),
validated.signature_contains.clone(),
query_limit,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let rows = match kb_store::DecodePipelineStore::list_materialized_events(&store, &filter).await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let mut output = std::vec::Vec::new();
for row in rows {
if row.source_decoder_name != "spl.token" || !payload_matches(&row.payload_json, &validated)
{
continue;
}
output.push(journal_row(row));
if output.len() >= validated.limit as usize {
break;
}
}
return std::result::Result::Ok(output);
}
fn summary_payload(
summary: kb_pipeline_demo_scenarios::DevnetSplTokenExecutionSummary,
amount_raw: std::string::String,
decimals: u8,
@@ -267,7 +412,7 @@ fn replay_diagnostics(summary: &kb_pipeline::DecodeReplaySummary) -> serde_json:
});
}
pub(crate) fn validate_journal_request(
fn validate_journal_request(
request: crate::DemoSplTokenJournalRequest,
) -> std::result::Result<crate::DemoSplTokenJournalRequest, std::string::String> {
if request.limit == 0 || request.limit > kb_store::MAX_MATERIALIZED_EVENT_QUERY_ROWS {
@@ -331,7 +476,7 @@ fn bounded_optional(
return std::result::Result::Ok(trimmed);
}
pub(crate) fn payload_matches(
fn payload_matches(
payload: &serde_json::Value,
request: &crate::DemoSplTokenJournalRequest,
) -> bool {
@@ -354,9 +499,7 @@ pub(crate) fn payload_matches(
return true;
}
pub(crate) fn demo_spl_token_journal_row(
row: kb_store::MaterializedEventQueryRow,
) -> DemoSplTokenJournalRow {
fn journal_row(row: kb_store::MaterializedEventQueryRow) -> DemoSplTokenJournalRow {
let operation = row
.payload_json
.get("operation")
@@ -457,12 +600,12 @@ mod tests {
{"role": "mint", "accountKey": "mint111"}
]
});
assert!(crate::payload_matches(&payload, &request));
assert!(super::payload_matches(&payload, &request));
assert_eq!(
payload.get("amountRaw").and_then(serde_json::Value::as_str),
std::option::Option::Some("18446744073709551615")
);
let row = crate::demo_spl_token_journal_row(kb_store::MaterializedEventQueryRow {
let row = super::journal_row(kb_store::MaterializedEventQueryRow {
processor_name: "materializer.token.accounts".to_string(),
processor_version: "0.4.4".to_string(),
input_key: "input".to_string(),
@@ -492,6 +635,6 @@ mod tests {
operation: std::option::Option::None,
limit: 501,
};
assert!(crate::validate_journal_request(request).is_err());
assert!(super::validate_journal_request(request).is_err());
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_token_2022.rs
// version: 7
// version: 11
//! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios.
@@ -131,7 +131,195 @@ pub(crate) struct DemoExecutionSplToken2022SummaryPayload {
pub(crate) diagnostics_json: std::string::String,
}
pub(crate) fn operation_from_request(
/// Executes one public Token-2022 simulation or explicitly authorized Devnet submission.
pub(crate) async fn demo_execution_spl_token_2022_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoExecutionSplToken2022Request,
) -> std::result::Result<crate::DemoExecutionSplToken2022SummaryPayload, std::string::String> {
let acquire_result = state.demo_execution_solana_core_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err("a Devnet execution is already running".to_string());
}
let _run_guard = crate::DemoExecutionSolanaCoreRunGuard {
running: state.demo_execution_solana_core_running(),
};
state
.demo_execution_solana_core_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let profile =
match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&profile) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store = match crate::connect_postgres_store(&profile).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
return std::result::Result::Err(error.to_string());
}
let operation = match operation_from_request(&request) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let operation_code = operation.operation_code().to_string();
let mut pipeline_request = kb_pipeline_demo_scenarios::DevnetSplToken2022ExecutionRequest::new(
format!("demo-spl-token-2022-execution-{}", chrono::Utc::now().timestamp_micros()),
operation,
);
pipeline_request.submit = request.submit;
pipeline_request.operator_confirmed = request.operator_confirmed;
pipeline_request.post_validation_max_retries = 20;
pipeline_request.force_post_validation_replay = request.force_post_validation_replay;
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> =
std::vec![std::sync::Arc::new(kb_lib::DcSplToken2022Decoder)];
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> = std::vec![
std::sync::Arc::new(kb_lib::MtTokenAccountsMaterializer),
std::sync::Arc::new(kb_lib::MtAdminMaterializer),
std::sync::Arc::new(kb_lib::MtRiskMaterializer),
std::sync::Arc::new(kb_lib::MtFeesMaterializer),
std::sync::Arc::new(kb_lib::MtComplianceAuditMaterializer),
];
let observer = crate::DemoExecutionSolanaCoreObserver {
app_handle,
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
};
let workspace_root = crate::workspace_root_dir();
let summary = match kb_pipeline_demo_scenarios::execute_devnet_spl_token_2022(
&http_pool,
&store,
&profile,
workspace_root.as_path(),
&pipeline_request,
decoders.as_slice(),
materializers.as_slice(),
&observer,
)
.await
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(summary_payload(request.scenario_id, operation_code, summary));
}
/// Loads public Token-2022 fixture values without reading private key bytes.
pub(crate) fn demo_spl_token_2022_fixture(
state: tauri::State<'_, crate::AppState>,
profile_name: std::string::String,
) -> std::result::Result<crate::DemoSplToken2022FixturePayload, std::string::String> {
let profile = match crate::select_devnet_profile(state.app_config(), profile_name.as_str()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
let wallet_dir = if configured.is_absolute() {
configured
} else {
crate::workspace_root_dir().join(configured)
};
let fixture_path = wallet_dir.join("spl_token_2022_validation").join("fixture.env");
let contents = match std::fs::read_to_string(&fixture_path) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"unable to read Token-2022 fixture {}: {error}",
fixture_path.display()
));
},
};
let values = parse_fixture(contents.as_str());
let required = |name: &str| -> std::result::Result<std::string::String, std::string::String> {
return match values.get(name) {
std::option::Option::Some(value) if !value.trim().is_empty() => {
std::result::Result::Ok(value.clone())
},
_ => std::result::Result::Err(format!("Token-2022 fixture is missing {name}")),
};
};
let program_id = match required("TOKEN_2022_PROGRAM") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mint = match required("TOKEN_2022_MINT") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let source = match required("TOKEN_2022_SOURCE") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let destination = match required("TOKEN_2022_DESTINATION") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let close_account = match required("TOKEN_2022_CLOSE_ACCOUNT") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let delegate = match required("TOKEN_2022_DELEGATE") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let authority = match required("TOKEN_2022_AUTHORITY") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let freeze_authority = match required("TOKEN_2022_FREEZE_AUTHORITY") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let decimals_text = match required("TOKEN_2022_DECIMALS") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let decimals = match decimals_text.parse::<u8>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("invalid TOKEN_2022_DECIMALS value: {error}"));
},
};
return std::result::Result::Ok(crate::DemoSplToken2022FixturePayload {
fixture_path: fixture_path.display().to_string(),
program_id,
mint,
source,
destination,
close_account,
delegate,
authority,
freeze_authority,
decimals,
mint_amount_raw: match required("TOKEN_2022_MINT_AMOUNT_RAW") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
},
transfer_amount_raw: match required("TOKEN_2022_TRANSFER_AMOUNT_RAW") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
},
approve_amount_raw: match required("TOKEN_2022_APPROVE_AMOUNT_RAW") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
},
burn_amount_raw: match required("TOKEN_2022_BURN_AMOUNT_RAW") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
},
});
}
fn operation_from_request(
request: &crate::DemoExecutionSplToken2022Request,
) -> std::result::Result<kb_lib::ExSplToken2022Operation, std::string::String> {
let authority = kb_lib::ExSplTokenAuthority {
@@ -203,7 +391,7 @@ pub(crate) fn operation_from_request(
});
}
pub(crate) fn demo_spl_token_2022_summary_payload(
fn summary_payload(
scenario_id: std::string::String,
operation: std::string::String,
summary: kb_pipeline_demo_scenarios::DevnetSplToken2022ExecutionSummary,
@@ -269,7 +457,7 @@ pub(crate) fn demo_spl_token_2022_summary_payload(
};
}
pub(crate) fn parse_fixture(
fn parse_fixture(
contents: &str,
) -> std::collections::BTreeMap<std::string::String, std::string::String> {
let mut values = std::collections::BTreeMap::new();
@@ -403,7 +591,7 @@ mod tests {
"token_2022_thaw_account",
"token_2022_close_destination",
] {
assert!(crate::operation_from_request(&request(scenario)).is_ok());
assert!(super::operation_from_request(&request(scenario)).is_ok());
}
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_sql_common.rs
// version: 11
// version: 12
//! Shared SQL demo helpers and serializable payloads.
@@ -211,6 +211,11 @@ pub(crate) async fn initialize_postgres_schema_for_startup(
emit_sql_startup_table_report(splash_window, before_tables.as_slice(), after_tables.as_slice());
}
/// Formats debug enums for UI display.
pub(crate) fn debug_status<T: std::fmt::Debug>(value: T) -> std::string::String {
return format!("{value:?}");
}
fn emit_sql_startup_table_report(
splash_window: &tauri::WebviewWindow,
before_tables: &[kb_store::PostgresTableDiagnostics],
@@ -305,8 +310,3 @@ fn emit_sql_startup_splash(
std::option::Option::None,
);
}
/// Formats debug enums for UI display.
pub(crate) fn debug_status<T: std::fmt::Debug>(value: T) -> std::string::String {
return format!("{value:?}");
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_sql_diag.rs
// version: 3
// version: 5
//! SQL diagnostic demo commands.
@@ -36,3 +36,38 @@ pub(crate) struct DemoSqlDiagPayload {
/// Known raw/core table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}
/// Loads read-only SQL diagnostics from the active profile.
pub(crate) async fn load_demo_sql_diag(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let diagnostics_result = store.backend_diagnostics().await;
let diagnostics = match diagnostics_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let tables_result = store.known_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlDiagPayload {
config_path: state.config_path().to_string(),
active_profile_name: profile.name.clone(),
backend: profile.database.backend.clone(),
masked_dsn: store.options().masked_dsn(),
current_schema: diagnostics.descriptor.current_schema,
health_status: crate::debug_status(diagnostics.health.status),
health_message: diagnostics.health.message,
migration_status: crate::debug_status(diagnostics.migrations.status),
migration_message: diagnostics.migrations.message,
server_version: diagnostics.server_version,
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_sql_pg_core.rs
// version: 4
// version: 6
//! PostgreSQL core store demo commands.
@@ -20,3 +20,25 @@ pub(crate) struct DemoSqlPgCorePayload {
/// Core table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}
/// Loads read-only core table diagnostics from PostgreSQL.
pub(crate) async fn load_demo_sql_pg_core(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let tables_result = store.core_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlPgCorePayload {
active_profile_name: profile.name.clone(),
masked_dsn: store.options().masked_dsn(),
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_sql_pg_raw.rs
// version: 5
// version: 7
//! PostgreSQL canonical acquisition store demo commands.
@@ -20,3 +20,25 @@ pub(crate) struct DemoSqlPgRawPayload {
/// Raw table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}
/// Loads read-only raw table diagnostics from PostgreSQL.
pub(crate) async fn load_demo_sql_pg_raw(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let tables_result = store.raw_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlPgRawPayload {
active_profile_name: profile.name.clone(),
masked_dsn: store.options().masked_dsn(),
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_sql_replay_candidates.rs
// version: 8
// version: 12
//! Read-only SQL replay candidate browser commands.
@@ -202,7 +202,187 @@ pub(crate) struct DemoSqlReplayEntityRow {
pub(crate) max_slot: i64,
}
pub(crate) fn transaction_row_from_pg(
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
pub(crate) async fn export_demo_sql_replay_csv(
file_name: std::string::String,
content: std::string::String,
) -> std::result::Result<std::string::String, std::string::String> {
if content.is_empty() {
return std::result::Result::Err("CSV export content must not be empty".to_owned());
}
if content.len() > 16 * 1024 * 1024 {
return std::result::Result::Err("CSV export content exceeds 16 MiB".to_owned());
}
let normalized_name_result = validated_csv_file_name(&file_name);
let normalized_name = match normalized_name_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let export_dir = crate::workspace_root_dir().join("data").join("exports_csv");
let create_result = tokio::fs::create_dir_all(&export_dir).await;
if let std::result::Result::Err(error) = create_result {
return std::result::Result::Err(format!("cannot create CSV export directory: {error}"));
}
let export_path_result = available_csv_export_path(&export_dir, normalized_name).await;
let export_path = match export_path_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut bytes = std::vec::Vec::with_capacity(content.len() + 3);
bytes.extend_from_slice(&[0xEF, 0xBB, 0xBF]);
bytes.extend_from_slice(content.as_bytes());
let write_result = tokio::fs::write(&export_path, bytes).await;
if let std::result::Result::Err(error) = write_result {
return std::result::Result::Err(format!("cannot write CSV export: {error}"));
}
let display_path = export_path.to_string_lossy().into_owned();
tracing::info!(target: crate::TRACING_TARGET, path = %display_path, "CSV replay candidate export written");
return std::result::Result::Ok(display_path);
}
/// Loads static browser options from the active PostgreSQL profile.
pub(crate) async fn demo_sql_replay_options(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
let profile = state.active_profile().clone();
let options_result = crate::postgres_store_options_from_profile(&profile, false);
let options = match options_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let mut known_programs =
std::vec::Vec::with_capacity(kb_program_ids::registered_program_ids().len());
for entry in kb_program_ids::registered_program_ids() {
known_programs.push(crate::DemoSqlReplayKnownProgramOption {
code: entry.code().to_owned(),
program_id: entry.program_id().to_owned(),
});
}
return std::result::Result::Ok(crate::DemoSqlReplayOptionsPayload {
active_profile_name: profile.name,
masked_dsn: options.masked_dsn(),
maximum_limit: kb_store::MAX_REPLAY_CANDIDATE_ROWS,
known_programs,
});
}
/// Loads bounded transaction replay candidates from PostgreSQL.
pub(crate) async fn load_demo_sql_replay_transactions(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayTransactionRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayTransactionRow>, std::string::String> {
let program_scope_result = program_scope_from_code(request.program_scope.as_str());
let program_scope = match program_scope_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let entity_kind_result = optional_entity_kind_from_code(request.entity_kind.as_deref());
let entity_kind = match entity_kind_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let filter_result = kb_store::PostgresReplayTransactionFilter::new(
request.signature_contains,
request.min_slot,
request.max_slot,
request.raw_processing_state,
request.ledger_status,
request.program_id,
program_scope,
entity_kind,
request.entity_value,
request.limit,
request.newest_first,
);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_transaction_candidates(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay transaction candidates");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(transaction_row_from_pg(row));
}
return std::result::Result::Ok(output);
}
/// Loads bounded program summaries from PostgreSQL.
pub(crate) async fn load_demo_sql_replay_programs(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayProgramRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayProgramRow>, std::string::String> {
let filter_result =
kb_store::PostgresReplayProgramFilter::new(request.program_id_contains, request.limit);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_program_summaries(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay program summaries");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(program_row_from_pg(row));
}
return std::result::Result::Ok(output);
}
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
pub(crate) async fn load_demo_sql_replay_entities(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayEntityRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayEntityRow>, std::string::String> {
let entity_kind_result = entity_kind_from_code(request.entity_kind.as_str());
let entity_kind = match entity_kind_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let filter_result = kb_store::PostgresReplayEntityFilter::new(
entity_kind,
request.entity_value_contains,
request.limit,
);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_entity_summaries(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay entity summaries");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(entity_row_from_pg(row));
}
return std::result::Result::Ok(output);
}
fn transaction_row_from_pg(
row: kb_store::PostgresReplayTransactionCandidate,
) -> crate::DemoSqlReplayTransactionRow {
return crate::DemoSqlReplayTransactionRow {
@@ -223,7 +403,7 @@ pub(crate) fn transaction_row_from_pg(
};
}
pub(crate) fn program_row_from_pg(
fn program_row_from_pg(
row: kb_store::PostgresReplayProgramSummary,
) -> crate::DemoSqlReplayProgramRow {
let program_code = kb_program_ids::find_registered_program_id(&row.program_id)
@@ -240,9 +420,7 @@ pub(crate) fn program_row_from_pg(
};
}
pub(crate) fn entity_row_from_pg(
row: kb_store::PostgresReplayEntitySummary,
) -> crate::DemoSqlReplayEntityRow {
fn entity_row_from_pg(row: kb_store::PostgresReplayEntitySummary) -> crate::DemoSqlReplayEntityRow {
return crate::DemoSqlReplayEntityRow {
entity_kind: row.entity_kind,
entity_value: row.entity_value,
@@ -253,7 +431,7 @@ pub(crate) fn entity_row_from_pg(
};
}
pub(crate) fn validated_csv_file_name(
fn validated_csv_file_name(
file_name: &str,
) -> std::result::Result<&'static str, std::string::String> {
return match file_name {
@@ -266,7 +444,7 @@ pub(crate) fn validated_csv_file_name(
};
}
pub(crate) async fn available_csv_export_path(
async fn available_csv_export_path(
export_dir: &std::path::Path,
file_name: &str,
) -> std::result::Result<std::path::PathBuf, std::string::String> {
@@ -300,7 +478,7 @@ pub(crate) async fn available_csv_export_path(
return std::result::Result::Err("cannot allocate a unique CSV export path".to_owned());
}
pub(crate) fn program_scope_from_code(
fn program_scope_from_code(
code: &str,
) -> std::result::Result<kb_store::PostgresReplayProgramScope, std::string::String> {
return match code {
@@ -312,7 +490,7 @@ pub(crate) fn program_scope_from_code(
};
}
pub(crate) fn optional_entity_kind_from_code(
fn optional_entity_kind_from_code(
code: std::option::Option<&str>,
) -> std::result::Result<std::option::Option<kb_store::PostgresReplayEntityKind>, std::string::String>
{
@@ -330,7 +508,7 @@ pub(crate) fn optional_entity_kind_from_code(
};
}
pub(crate) fn entity_kind_from_code(
fn entity_kind_from_code(
code: &str,
) -> std::result::Result<kb_store::PostgresReplayEntityKind, std::string::String> {
return match code {
@@ -345,27 +523,27 @@ pub(crate) fn entity_kind_from_code(
mod tests {
#[test]
fn program_scope_parser_accepts_logs() {
let result = crate::program_scope_from_code("logs");
let result = super::program_scope_from_code("logs");
assert_eq!(result, std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Logs));
}
#[test]
fn entity_kind_parser_rejects_unknown_code() {
let result = crate::entity_kind_from_code("unknown");
let result = super::entity_kind_from_code("unknown");
assert!(result.is_err());
}
#[test]
fn csv_file_name_rejects_unknown_name() {
let result = crate::validated_csv_file_name("arbitrary.csv");
let result = super::validated_csv_file_name("arbitrary.csv");
assert!(result.is_err());
}
#[test]
fn csv_file_name_accepts_split_entity_exports() {
let mint_result = crate::validated_csv_file_name("replay_mints.csv");
let owner_result = crate::validated_csv_file_name("replay_owners.csv");
let account_result = crate::validated_csv_file_name("replay_account_keys.csv");
let mint_result = super::validated_csv_file_name("replay_mints.csv");
let owner_result = super::validated_csv_file_name("replay_owners.csv");
let account_result = super::validated_csv_file_name("replay_account_keys.csv");
assert!(mint_result.is_ok());
assert!(owner_result.is_ok());
assert!(account_result.is_ok());
@@ -382,7 +560,7 @@ mod tests {
min_slot: 1,
max_slot: 1,
};
let converted = crate::program_row_from_pg(row);
let converted = super::program_row_from_pg(row);
assert_eq!(converted.program_code.as_deref(), std::option::Option::Some("system"));
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_ws.rs
// version: 17
// version: 19
//! Standard Solana WebSocket demo commands backed by `kb_onchain_transport::WsSession`.
@@ -179,72 +179,8 @@ pub(crate) struct DemoWsMessagePayload {
pub(crate) payload_json: std::string::String,
}
const DEMO_WS_UI_RATE_WINDOW_MS: u64 = 1000;
const DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW: u32 = 12;
const DEMO_WS_UI_MAX_PAYLOAD_CHARS: usize = 8_000;
const DEMO_WS_RECONNECT_ATTEMPTS: u32 = 3;
const DEMO_WS_RECONNECT_INITIAL_DELAY_MS: u64 = 250;
const DEMO_WS_RECONNECT_MAX_DELAY_MS: u64 = 2_000;
struct DemoWsUiRateLimiter {
window_started_at: std::time::Instant,
emitted_in_window: u32,
dropped_in_window: u32,
}
impl DemoWsUiRateLimiter {
fn new() -> Self {
return Self {
window_started_at: std::time::Instant::now(),
emitted_in_window: 0,
dropped_in_window: 0,
};
}
fn should_emit(&mut self, app_handle: &tauri::AppHandle) -> bool {
let now = std::time::Instant::now();
if now.duration_since(self.window_started_at)
>= std::time::Duration::from_millis(DEMO_WS_UI_RATE_WINDOW_MS)
{
if self.dropped_in_window > 0 {
emit_demo_ws_message(
app_handle,
"throttled",
format!(
"{} WebSocket notification(s) masquée(s) pendant la dernière fenêtre UI.",
self.dropped_in_window
),
);
}
self.window_started_at = now;
self.emitted_in_window = 0;
self.dropped_in_window = 0;
}
if self.emitted_in_window >= DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW {
self.dropped_in_window = self.dropped_in_window.saturating_add(1);
return false;
}
self.emitted_in_window = self.emitted_in_window.saturating_add(1);
return true;
}
}
pub(crate) async fn demo_ws_status_inner(
state: &crate::AppState,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let guard = state.demo_ws_session().lock().await;
guard.clone()
};
return match session {
std::option::Option::Some(session) => {
std::result::Result::Ok(status_from_snapshot(session.snapshot().await))
},
std::option::Option::None => std::result::Result::Ok(disconnected_status()),
};
}
pub(crate) async fn demo_ws_connect_inner(
/// Connects if needed, then subscribes through `kb_onchain_transport::WsSession`.
pub(crate) async fn demo_ws_connect(
app_handle: tauri::AppHandle,
state: &crate::AppState,
request: crate::DemoWsRequest,
@@ -274,7 +210,7 @@ pub(crate) async fn demo_ws_connect_inner(
std::result::Result::Ok(client) => client,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let session = match ensure_demo_ws_session(&app_handle, state, selected_client.clone()).await {
let session = match ensure_session(&app_handle, state, selected_client.clone()).await {
std::result::Result::Ok(session) => session,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
@@ -290,7 +226,7 @@ pub(crate) async fn demo_ws_connect_inner(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
emit_demo_ws_status(&app_handle, session.snapshot().await);
emit_status(&app_handle, session.snapshot().await);
return std::result::Result::Ok(crate::DemoWsExecutionPayload {
endpoint_name: selected_client.endpoint_name().to_string(),
provider: selected_client.provider().to_string(),
@@ -304,7 +240,148 @@ pub(crate) async fn demo_ws_connect_inner(
});
}
async fn ensure_demo_ws_session(
/// Returns the current WebSocket demo session status.
pub(crate) async fn demo_ws_status(
state: &crate::AppState,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let guard = state.demo_ws_session().lock().await;
guard.clone()
};
return match session {
std::option::Option::Some(session) => {
std::result::Result::Ok(status_from_snapshot(session.snapshot().await))
},
std::option::Option::None => std::result::Result::Ok(disconnected_status()),
};
}
/// Lists WebSocket endpoints available through the configured pool.
pub(crate) fn demo_ws_list_pool_clients(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<
std::vec::Vec<kb_onchain_transport::WsPoolClientSnapshot>,
std::string::String,
> {
let pool = match state.demo_ws_pool() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(pool.snapshot());
}
/// Lists selectable WebSocket roles and methods for the demo UI.
pub(crate) fn demo_ws_options(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoWsOptionsPayload, std::string::String> {
return std::result::Result::Ok(crate::DemoWsOptionsPayload {
roles: build_ws_role_options(match state.demo_ws_pool() {
std::result::Result::Ok(pool) => pool.snapshot(),
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
}),
methods: build_ws_method_options(),
});
}
/// Unsubscribes one subscription while keeping the WebSocket connection open.
pub(crate) async fn demo_ws_unsubscribe(
app_handle: tauri::AppHandle,
state: &crate::AppState,
remote_subscription_id: u64,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let guard = state.demo_ws_session().lock().await;
guard.clone()
};
let session = match session {
std::option::Option::Some(session) => session,
std::option::Option::None => {
return std::result::Result::Err("demo WebSocket session is not connected".to_string());
},
};
let unsubscribe_result = session.unsubscribe(remote_subscription_id).await;
if let std::result::Result::Err(error) = unsubscribe_result {
return std::result::Result::Err(error.to_string());
}
let snapshot = session.snapshot().await;
emit_status(&app_handle, snapshot.clone());
return std::result::Result::Ok(status_from_snapshot(snapshot));
}
pub(crate) async fn demo_ws_disconnect(
state: &crate::AppState,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let mut guard = state.demo_ws_session().lock().await;
guard.take()
};
if let std::option::Option::Some(session) = session {
if let std::result::Result::Err(error) = session.disconnect().await {
return std::result::Result::Err(error.to_string());
}
}
return std::result::Result::Ok(disconnected_status());
}
/// Disconnects the demo WebSocket session from application lifecycle hooks.
pub(crate) async fn disconnect_demo_ws_app_state(state: &crate::AppState, _wait_for_relay: bool) {
let disconnect_result = demo_ws_disconnect(state).await;
if let std::result::Result::Err(error) = disconnect_result {
tracing::debug!(target: crate::TRACING_TARGET, "demo ws lifecycle disconnect failed: {error}");
}
}
const DEMO_WS_UI_RATE_WINDOW_MS: u64 = 1000;
const DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW: u32 = 12;
const DEMO_WS_UI_MAX_PAYLOAD_CHARS: usize = 8_000;
const DEMO_WS_RECONNECT_ATTEMPTS: u32 = 3;
const DEMO_WS_RECONNECT_INITIAL_DELAY_MS: u64 = 250;
const DEMO_WS_RECONNECT_MAX_DELAY_MS: u64 = 2_000;
struct DemoWsUiRateLimiter {
window_started_at: std::time::Instant,
emitted_in_window: u32,
dropped_in_window: u32,
}
impl DemoWsUiRateLimiter {
fn new() -> Self {
return Self {
window_started_at: std::time::Instant::now(),
emitted_in_window: 0,
dropped_in_window: 0,
};
}
fn should_emit(&mut self, app_handle: &tauri::AppHandle) -> bool {
let now = std::time::Instant::now();
if now.duration_since(self.window_started_at)
>= std::time::Duration::from_millis(DEMO_WS_UI_RATE_WINDOW_MS)
{
if self.dropped_in_window > 0 {
emit_message(
app_handle,
"throttled",
format!(
"{} WebSocket notification(s) masquée(s) pendant la dernière fenêtre UI.",
self.dropped_in_window
),
);
}
self.window_started_at = now;
self.emitted_in_window = 0;
self.dropped_in_window = 0;
}
if self.emitted_in_window >= DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW {
self.dropped_in_window = self.dropped_in_window.saturating_add(1);
return false;
}
self.emitted_in_window = self.emitted_in_window.saturating_add(1);
return true;
}
}
async fn ensure_session(
app_handle: &tauri::AppHandle,
state: &crate::AppState,
selected_client: kb_onchain_transport::WsClient,
@@ -351,7 +428,7 @@ async fn ensure_demo_ws_session(
std::result::Result::Ok(session) => std::sync::Arc::new(session),
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
spawn_demo_ws_event_bridge(app_handle.clone(), session.clone());
spawn_event_bridge(app_handle.clone(), session.clone());
{
let mut guard = state.demo_ws_session().lock().await;
*guard = std::option::Option::Some(session.clone());
@@ -359,54 +436,7 @@ async fn ensure_demo_ws_session(
return std::result::Result::Ok(session);
}
pub(crate) async fn demo_ws_unsubscribe_inner(
app_handle: tauri::AppHandle,
state: &crate::AppState,
remote_subscription_id: u64,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let guard = state.demo_ws_session().lock().await;
guard.clone()
};
let session = match session {
std::option::Option::Some(session) => session,
std::option::Option::None => {
return std::result::Result::Err("demo WebSocket session is not connected".to_string());
},
};
let unsubscribe_result = session.unsubscribe(remote_subscription_id).await;
if let std::result::Result::Err(error) = unsubscribe_result {
return std::result::Result::Err(error.to_string());
}
let snapshot = session.snapshot().await;
emit_demo_ws_status(&app_handle, snapshot.clone());
return std::result::Result::Ok(status_from_snapshot(snapshot));
}
pub(crate) async fn demo_ws_disconnect_inner(
state: &crate::AppState,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
let session = {
let mut guard = state.demo_ws_session().lock().await;
guard.take()
};
if let std::option::Option::Some(session) = session {
if let std::result::Result::Err(error) = session.disconnect().await {
return std::result::Result::Err(error.to_string());
}
}
return std::result::Result::Ok(disconnected_status());
}
/// Disconnects the demo WebSocket session from application lifecycle hooks.
pub(crate) async fn disconnect_demo_ws_app_state(state: &crate::AppState, _wait_for_relay: bool) {
let disconnect_result = demo_ws_disconnect_inner(state).await;
if let std::result::Result::Err(error) = disconnect_result {
tracing::debug!(target: crate::TRACING_TARGET, "demo ws lifecycle disconnect failed: {error}");
}
}
fn spawn_demo_ws_event_bridge(
fn spawn_event_bridge(
app_handle: tauri::AppHandle,
session: std::sync::Arc<kb_onchain_transport::WsSession>,
) {
@@ -420,7 +450,7 @@ fn spawn_demo_ws_event_bridge(
std::result::Result::Err(tokio::sync::broadcast::error::RecvError::Lagged(
count,
)) => {
emit_demo_ws_message(
emit_message(
&app_handle,
"lagged",
format!("{count} événement(s) WebSocket interne(s) perdu(s)"),
@@ -447,43 +477,39 @@ fn spawn_demo_ws_event_bridge(
std::result::Result::Ok(payload) => payload,
std::result::Result::Err(_) => compact_payload,
};
emit_demo_ws_message(
&app_handle,
"notification",
truncate_payload(ui_payload),
);
emit_message(&app_handle, "notification", truncate_payload(ui_payload));
}
},
kb_onchain_transport::WsSessionEvent::Diagnostic { code, message } => {
emit_demo_ws_message(&app_handle, &code, truncate_payload(message));
emit_message(&app_handle, &code, truncate_payload(message));
},
kb_onchain_transport::WsSessionEvent::Reconnecting {
attempt,
maximum_attempts,
} => {
emit_demo_ws_message(
emit_message(
&app_handle,
"reconnecting",
format!("tentative {attempt}/{maximum_attempts}"),
);
emit_demo_ws_status(&app_handle, session.snapshot().await);
emit_status(&app_handle, session.snapshot().await);
},
kb_onchain_transport::WsSessionEvent::Reconnected { reconnect_count } => {
emit_demo_ws_message(
emit_message(
&app_handle,
"reconnected",
format!("reconnexion numéro {reconnect_count}"),
);
emit_demo_ws_status(&app_handle, session.snapshot().await);
emit_status(&app_handle, session.snapshot().await);
},
kb_onchain_transport::WsSessionEvent::Connected
| kb_onchain_transport::WsSessionEvent::SubscriptionAdded(_)
| kb_onchain_transport::WsSessionEvent::SubscriptionRemapped { .. }
| kb_onchain_transport::WsSessionEvent::SubscriptionRemoved(_) => {
emit_demo_ws_status(&app_handle, session.snapshot().await);
emit_status(&app_handle, session.snapshot().await);
},
kb_onchain_transport::WsSessionEvent::Disconnected => {
emit_demo_ws_status(&app_handle, session.snapshot().await);
emit_status(&app_handle, session.snapshot().await);
break;
},
}
@@ -491,10 +517,7 @@ fn spawn_demo_ws_event_bridge(
});
}
fn emit_demo_ws_status(
app_handle: &tauri::AppHandle,
snapshot: kb_onchain_transport::WsSessionSnapshot,
) {
fn emit_status(app_handle: &tauri::AppHandle, snapshot: kb_onchain_transport::WsSessionSnapshot) {
let window = match app_handle.get_webview_window("demo_ws") {
std::option::Option::Some(window) => window,
std::option::Option::None => return,
@@ -505,11 +528,7 @@ fn emit_demo_ws_status(
}
}
fn emit_demo_ws_message(
app_handle: &tauri::AppHandle,
kind: &str,
payload_json: std::string::String,
) {
fn emit_message(app_handle: &tauri::AppHandle, kind: &str, payload_json: std::string::String) {
let window = match app_handle.get_webview_window("demo_ws") {
std::option::Option::Some(window) => window,
std::option::Option::None => return,
@@ -575,7 +594,7 @@ fn disconnected_status() -> crate::DemoWsStatusPayload {
};
}
pub(crate) fn build_ws_role_options(
fn build_ws_role_options(
snapshots: std::vec::Vec<kb_onchain_transport::WsPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoWsRoleOption> {
let mut roles = std::collections::BTreeMap::<
@@ -603,7 +622,7 @@ pub(crate) fn build_ws_role_options(
return options;
}
pub(crate) fn build_ws_method_options() -> std::vec::Vec<crate::DemoWsMethodOption> {
fn build_ws_method_options() -> std::vec::Vec<crate::DemoWsMethodOption> {
let labels = std::collections::BTreeMap::from([
("accountSubscribe", ("Compte", true, false, true)),
("blockSubscribe", ("Bloc", false, true, true)),

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/lib.rs
// version: 15
// version: 25
//! Tauri desktop demo application for `khadhroony-bot3`.
@@ -36,6 +36,8 @@ pub use self::tauri::run;
/// Shared application state managed by Tauri.
pub(crate) use self::app_state::AppState;
/// Canonical tracing target for the desktop demo application.
pub(crate) use self::constants::TRACING_TARGET;
/// Backfill observer forwarding pipeline progress to the Tauri window.
pub(crate) use self::demo_backfill::DemoBackfillObserver;
/// Initial options shown by the backfill window.
@@ -52,12 +54,12 @@ pub(crate) use self::demo_backfill::DemoBackfillRoleOption;
pub(crate) use self::demo_backfill::DemoBackfillRunGuard;
/// Final UI-safe summary for one backfill campaign.
pub(crate) use self::demo_backfill::DemoBackfillSummaryPayload;
/// Builds one validated pipeline request from the UI contract.
pub(crate) use self::demo_backfill::build_demo_backfill_pipeline_request;
/// Builds selectable endpoint roles from the HTTP pool snapshot.
pub(crate) use self::demo_backfill::build_role_options;
/// Converts a pipeline summary to the UI-safe summary.
pub(crate) use self::demo_backfill::demo_backfill_summary_payload;
/// Requests cooperative cancellation of the active backfill campaign.
pub(crate) use self::demo_backfill::demo_backfill_cancel;
/// Executes one bounded backfill campaign and emits progress events.
pub(crate) use self::demo_backfill::demo_backfill_execute;
/// Returns endpoint roles, known programs and conservative backfill defaults.
pub(crate) use self::demo_backfill::demo_backfill_options;
/// Configuration payload shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigPayload;
/// Builds the configuration payload from shared application state.
@@ -77,15 +79,11 @@ pub(crate) use self::demo_core_extraction::DemoCoreExtractionRunGuard;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::DemoCoreExtractionSummaryPayload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::build_demo_core_extraction_pipeline_request;
pub(crate) use self::demo_core_extraction::demo_core_extraction_cancel;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::cancel_demo_core_extraction;
pub(crate) use self::demo_core_extraction::demo_core_extraction_execute;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::demo_core_extraction_options_payload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::demo_core_extraction_summary_payload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::execute_demo_core_extraction;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::DemoDecodeCoverageSummaryPayload;
/// Internal demo decode replay item.
@@ -112,35 +110,29 @@ pub(crate) use self::demo_decode_replay::DemoDecodeReplaySummaryPayload;
pub(crate) use self::demo_decode_replay::DemoTransactionAnnotationRequest;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::DemoTransactionAnnotationRow;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::annotation_payload;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::available_decoders;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::available_materializers;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::build_demo_decode_replay_pipeline_request;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::coverage_payload;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::demo_decode_replay_summary_payload;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::optional_line_count;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::register_active_campaign;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::text_sample;
/// Loads bounded materialized transaction annotations for the replay diagnostics panel.
pub(crate) use self::demo_decode_replay::demo_decode_replay_annotations;
/// Requests cooperative cancellation of the active decode replay campaign.
pub(crate) use self::demo_decode_replay::demo_decode_replay_cancel;
/// Loads aggregate diagnostics for the decode replay panel.
pub(crate) use self::demo_decode_replay::demo_decode_replay_diagnostics;
/// Executes one bounded contextual decode replay campaign.
pub(crate) use self::demo_decode_replay::demo_decode_replay_execute;
/// Returns available decoders and default replay bounds.
pub(crate) use self::demo_decode_replay::demo_decode_replay_options;
/// Readiness report for one Devnet profile PostgreSQL store.
pub(crate) use self::demo_devnet_common::DemoExecutionDevnetStoreReadinessPayload;
/// Converts the reusable scenario readiness report to the desktop TS-RS payload.
pub(crate) use self::demo_devnet_common::devnet_store_readiness_payload;
/// Serializes a diagnostic value to indented JSON without panicking.
pub(crate) use self::demo_devnet_common::pretty_json;
/// Request sent by the Memo v4 Devnet execution panel.
pub(crate) use self::demo_execution_solana_core::DemoExecutionMemoRequest;
/// UI-safe result of one Memo v4 Devnet execution orchestration.
pub(crate) use self::demo_execution_solana_core::DemoExecutionMemoSummaryPayload;
/// Public key generated for a disposable Devnet recipient.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreGeneratedRecipientPayload;
/// ????
/// Bridges reusable execution progress to desktop events.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreObserver;
/// Initial options shown by the Solana Core execution demo.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreOptionsPayload;
@@ -150,26 +142,32 @@ pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreProfileO
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreProgressPayload;
/// Request sent by the execution demo.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreRequest;
/// ????
/// Restores the single-execution state flag when a run finishes.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreRunGuard;
/// UI-safe result of one execution orchestration.
pub(crate) use self::demo_execution_solana_core::DemoExecutionSolanaCoreSummaryPayload;
/// ????
/// Maps a confirmation status to its stable UI code.
pub(crate) use self::demo_execution_solana_core::confirmation_status_code;
/// ????
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_memo_summary_payload;
/// ????
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_summary_payload;
/// ????
pub(crate) use self::demo_execution_solana_core::devnet_profile_options;
/// ????
/// Returns compatible Devnet profiles and conservative defaults.
pub(crate) use self::demo_execution_solana_core::demo_execution_devnet_prepare_profile;
/// Requests cooperative cancellation before the next execution stage.
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_cancel;
/// Executes one bounded System transfer simulation or Devnet submission.
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_execute;
/// Generates a disposable recipient public key without exposing its private key.
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_generate_recipient;
/// Returns compatible Devnet profiles and conservative defaults.
pub(crate) use self::demo_execution_solana_core::demo_execution_solana_core_options;
/// Executes one Memo v4 simulation or explicitly authorized Devnet submission.
pub(crate) use self::demo_execution_solana_core::demo_execution_spl_memo_execute;
/// Maps an execution cluster to its stable UI code.
pub(crate) use self::demo_execution_solana_core::execution_cluster_code;
/// ????
pub(crate) use self::demo_execution_solana_core::pretty_json;
/// ????
/// Selects and validates one Devnet profile by name.
pub(crate) use self::demo_execution_solana_core::select_devnet_profile;
/// Frontend payload for one independent Devnet SPL validation scenario.
pub(crate) use self::demo_execution_spl::DevnetSplValidationScenarioPayload;
/// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6.
pub(crate) use self::demo_execution_spl::demo_execution_spl_validation_scenarios;
/// HTTP demo response payload.
pub(crate) use self::demo_http::DemoHttpExecutionPayload;
/// One selectable HTTP JSON-RPC method.
@@ -180,12 +178,12 @@ pub(crate) use self::demo_http::DemoHttpOptionsPayload;
pub(crate) use self::demo_http::DemoHttpRequest;
/// One selectable HTTP endpoint role.
pub(crate) use self::demo_http::DemoHttpRoleOption;
/// Builds the HTTP method inventory.
pub(crate) use self::demo_http::build_http_method_options;
/// Builds the HTTP role inventory from pool snapshots.
pub(crate) use self::demo_http::build_http_role_options;
/// Executes one raw HTTP JSON-RPC request.
pub(crate) use self::demo_http::demo_http_execute_request_inner;
pub(crate) use self::demo_http::demo_http_execute_request;
/// Returns the configured HTTP pool client snapshots.
pub(crate) use self::demo_http::demo_http_list_pool_clients;
/// Returns HTTP roles, methods and conservative defaults.
pub(crate) use self::demo_http::demo_http_options;
/// Request for one representative ATA creation on Devnet.
pub(crate) use self::demo_spl_ata::DemoExecutionSplAtaRequest;
/// UI-safe ATA execution result.
@@ -198,18 +196,12 @@ pub(crate) use self::demo_spl_ata::DemoSplAtaDerivationRequest;
pub(crate) use self::demo_spl_ata::DemoSplAtaJournalRequest;
/// One UI-safe ATA lifecycle journal row.
pub(crate) use self::demo_spl_ata::DemoSplAtaJournalRow;
/// ????
pub(crate) use self::demo_spl_ata::demo_spl_ata_summary_payload;
/// ????
pub(crate) use self::demo_spl_ata::derive_ata;
/// ????
pub(crate) use self::demo_spl_ata::journal_matches;
/// ????
pub(crate) use self::demo_spl_ata::journal_row;
/// ????
pub(crate) use self::demo_spl_ata::load_profile_wallet;
/// ????
pub(crate) use self::demo_spl_ata::parse_token_program;
/// Executes one representative Create or CreateIdempotent ATA flow.
pub(crate) use self::demo_spl_ata::demo_execution_spl_ata_execute;
/// Derives the representative profile wallet ATA without exposing private key material.
pub(crate) use self::demo_spl_ata::demo_spl_ata_derive;
/// Loads a bounded journal of ATA-owned lifecycle facts.
pub(crate) use self::demo_spl_ata::demo_spl_ata_journal;
/// Request for one checked classic SPL Token transfer on Devnet.
pub(crate) use self::demo_spl_token::DemoExecutionSplTokenRequest;
/// UI-safe result of one checked SPL Token transfer orchestration.
@@ -218,26 +210,20 @@ pub(crate) use self::demo_spl_token::DemoExecutionSplTokenSummaryPayload;
pub(crate) use self::demo_spl_token::DemoSplTokenJournalRequest;
/// UI-safe materialized classic SPL Token journal row.
pub(crate) use self::demo_spl_token::DemoSplTokenJournalRow;
/// ????
pub(crate) use self::demo_spl_token::demo_spl_token_journal_row;
/// Executes one checked SPL Token simulation or explicitly authorized Devnet submission.
pub(crate) use self::demo_spl_token::demo_spl_token_summary_payload;
/// ????
pub(crate) use self::demo_spl_token::payload_matches;
pub(crate) use self::demo_spl_token::demo_execution_spl_token_execute;
/// Loads a bounded, typed journal of committed classic SPL Token projections.
pub(crate) use self::demo_spl_token::validate_journal_request;
pub(crate) use self::demo_spl_token::demo_spl_token_journal;
/// Request for one independent public Token-2022 Devnet scenario.
pub(crate) use self::demo_spl_token_2022::DemoExecutionSplToken2022Request;
/// UI-safe result of one public Token-2022 Devnet orchestration.
pub(crate) use self::demo_spl_token_2022::DemoExecutionSplToken2022SummaryPayload;
/// Public values loaded from one persisted Token-2022 validation fixture.
pub(crate) use self::demo_spl_token_2022::DemoSplToken2022FixturePayload;
/// ????
pub(crate) use self::demo_spl_token_2022::demo_spl_token_2022_summary_payload;
/// ????
pub(crate) use self::demo_spl_token_2022::operation_from_request;
/// ????
pub(crate) use self::demo_spl_token_2022::parse_fixture;
/// Executes one public Token-2022 simulation or explicitly authorized Devnet submission.
pub(crate) use self::demo_spl_token_2022::demo_execution_spl_token_2022_execute;
/// Loads public Token-2022 fixture values without reading private key bytes.
pub(crate) use self::demo_spl_token_2022::demo_spl_token_2022_fixture;
/// UI-safe SQL table diagnostic snapshot.
pub(crate) use self::demo_sql_common::DemoSqlTableSnapshot;
/// Connects to the configured PostgreSQL store.
@@ -254,10 +240,16 @@ pub(crate) use self::demo_sql_common::table_snapshot_from_pg;
pub(crate) use self::demo_sql_common::table_snapshots_from_pg;
/// SQL diagnostic payload.
pub(crate) use self::demo_sql_diag::DemoSqlDiagPayload;
/// Loads read-only SQL diagnostics from the active profile.
pub(crate) use self::demo_sql_diag::load_demo_sql_diag;
/// PostgreSQL core-table payload.
pub(crate) use self::demo_sql_pg_core::DemoSqlPgCorePayload;
/// Loads read-only core table diagnostics from PostgreSQL.
pub(crate) use self::demo_sql_pg_core::load_demo_sql_pg_core;
/// PostgreSQL raw-table payload.
pub(crate) use self::demo_sql_pg_raw::DemoSqlPgRawPayload;
/// Loads read-only raw table diagnostics from PostgreSQL.
pub(crate) use self::demo_sql_pg_raw::load_demo_sql_pg_raw;
/// SQL replay entity request.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayEntityRequest;
/// SQL replay entity row.
@@ -274,22 +266,16 @@ pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayProgramRow;
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRequest;
/// SQL replay transaction row.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRow;
/// Resolves an available CSV path.
pub(crate) use self::demo_sql_replay_candidates::available_csv_export_path;
/// Parses an entity-kind code.
pub(crate) use self::demo_sql_replay_candidates::entity_kind_from_code;
/// Converts a PostgreSQL entity row.
pub(crate) use self::demo_sql_replay_candidates::entity_row_from_pg;
/// Parses an optional entity-kind code.
pub(crate) use self::demo_sql_replay_candidates::optional_entity_kind_from_code;
/// Converts a PostgreSQL program row.
pub(crate) use self::demo_sql_replay_candidates::program_row_from_pg;
/// Parses a program-scope code.
pub(crate) use self::demo_sql_replay_candidates::program_scope_from_code;
/// Converts a PostgreSQL transaction row.
pub(crate) use self::demo_sql_replay_candidates::transaction_row_from_pg;
/// Validates a CSV filename.
pub(crate) use self::demo_sql_replay_candidates::validated_csv_file_name;
/// Loads static browser options from the active PostgreSQL profile.
pub(crate) use self::demo_sql_replay_candidates::demo_sql_replay_options;
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
pub(crate) use self::demo_sql_replay_candidates::export_demo_sql_replay_csv;
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_entities;
/// Loads bounded program summaries from PostgreSQL.
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs;
/// Loads bounded transaction replay candidates from PostgreSQL.
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions;
/// WebSocket execution response payload.
pub(crate) use self::demo_ws::DemoWsExecutionPayload;
/// WebSocket message emitted to the frontend.
@@ -308,18 +294,18 @@ pub(crate) use self::demo_ws::DemoWsStatusPayload;
pub(crate) use self::demo_ws::DemoWsSubscriptionStatusPayload;
/// WebSocket unsubscribe request.
pub(crate) use self::demo_ws::DemoWsUnsubscribeRequest;
/// Builds the standard WebSocket method inventory.
pub(crate) use self::demo_ws::build_ws_method_options;
/// Builds selectable WebSocket roles from pool snapshots.
pub(crate) use self::demo_ws::build_ws_role_options;
/// Connects and subscribes through the persistent WebSocket session.
pub(crate) use self::demo_ws::demo_ws_connect_inner;
pub(crate) use self::demo_ws::demo_ws_connect;
/// Disconnects the persistent WebSocket session.
pub(crate) use self::demo_ws::demo_ws_disconnect_inner;
pub(crate) use self::demo_ws::demo_ws_disconnect;
/// Lists WebSocket endpoints available through the configured pool.
pub(crate) use self::demo_ws::demo_ws_list_pool_clients;
/// Lists selectable WebSocket roles and methods for the demo UI.
pub(crate) use self::demo_ws::demo_ws_options;
/// Returns the current WebSocket session status.
pub(crate) use self::demo_ws::demo_ws_status_inner;
pub(crate) use self::demo_ws::demo_ws_status;
/// Unsubscribes one active WebSocket subscription.
pub(crate) use self::demo_ws::demo_ws_unsubscribe_inner;
pub(crate) use self::demo_ws::demo_ws_unsubscribe;
/// Disconnects the persistent WebSocket session during application shutdown.
pub(crate) use self::demo_ws::disconnect_demo_ws_app_state;
/// Frontend logging payload.
@@ -341,10 +327,4 @@ pub(crate) use self::splash::SplashOrder;
/// Emits one order to the splash frontend.
pub(crate) use self::splash::emit_splash_order;
/// Runs the startup sequence after the splash frontend is ready.
pub(crate) use self::splash::splash_frontend_ready_inner;
/// Waits for the minimum splash duration.
pub(crate) use self::splash::wait_until_minimum;
// Keep the canonical tracing target as the final facade export.
/// Canonical tracing target for the desktop demo application.
pub(crate) use self::constants::TRACING_TARGET;
pub(crate) use self::splash::splash_frontend_ready;

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/splash.rs
// version: 4
// version: 5
//! Splash-window payloads and startup sequencing helpers.
@@ -52,7 +52,7 @@ pub(crate) fn emit_splash_order(
}
/// Runs the startup sequence after the splash frontend installed its listener.
pub(crate) async fn splash_frontend_ready_inner(
pub(crate) async fn splash_frontend_ready(
app: tauri::AppHandle,
window: tauri::WebviewWindow,
state: &crate::AppState,
@@ -120,7 +120,7 @@ pub(crate) async fn splash_frontend_ready_inner(
std::option::Option::Some("success"),
std::option::Option::None,
);
crate::wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
crate::emit_splash_order(
&window,
"fadeout",
@@ -142,7 +142,7 @@ pub(crate) async fn splash_frontend_ready_inner(
}
/// Waits until the configured minimum splash duration has elapsed.
pub(crate) async fn wait_until_minimum(start: tokio::time::Instant, minimum_ms: u64) {
async fn wait_until_minimum(start: tokio::time::Instant, minimum_ms: u64) {
let minimum = std::time::Duration::from_millis(minimum_ms);
let elapsed = start.elapsed();
if elapsed >= minimum {

File diff suppressed because it is too large Load Diff