339 lines
15 KiB
Rust
339 lines
15 KiB
Rust
// file: kb-app-demo-desktop/src/demo_execution_metadata_solana_program.rs
|
|
// version: 3
|
|
|
|
//! Thin desktop adapter for complete Solana Program Metadata Devnet campaigns.
|
|
|
|
/// UI-safe step in one Solana Program Metadata Devnet journey.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ts_rs::TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramScenarioStepPayload.ts"
|
|
)]
|
|
pub(crate) struct DemoExecutionMetadataSolanaProgramScenarioStepPayload {
|
|
/// Stable scenario-step identifier.
|
|
pub id: std::string::String,
|
|
/// Operator-visible label.
|
|
pub label: std::string::String,
|
|
/// Stable executor operation code.
|
|
pub operation_code: std::string::String,
|
|
/// Expected state before execution.
|
|
pub initial_state: std::string::String,
|
|
/// Expected state after confirmed execution.
|
|
pub resulting_state: std::string::String,
|
|
/// Whether explicit approval is required.
|
|
pub requires_explicit_approval: bool,
|
|
/// Evidence kinds required by the validation contract.
|
|
pub required_evidence: std::vec::Vec<std::string::String>,
|
|
}
|
|
|
|
/// UI-safe Solana Program Metadata Devnet journey.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ts_rs::TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramScenarioPayload.ts"
|
|
)]
|
|
pub(crate) struct DemoExecutionMetadataSolanaProgramScenarioPayload {
|
|
/// Stable scenario identifier.
|
|
pub id: std::string::String,
|
|
/// Operator-visible label.
|
|
pub label: std::string::String,
|
|
/// Fixture account selected by the journey.
|
|
pub fixture_account: std::string::String,
|
|
/// Ordered steps.
|
|
pub steps: std::vec::Vec<crate::DemoExecutionMetadataSolanaProgramScenarioStepPayload>,
|
|
}
|
|
|
|
/// Request for one complete confirmed Solana Program Metadata Devnet campaign.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ts_rs::TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignRequest.ts"
|
|
)]
|
|
pub(crate) struct DemoExecutionMetadataSolanaProgramCampaignRequest {
|
|
/// Selected Devnet profile.
|
|
pub profile_name: std::string::String,
|
|
/// Explicit operator confirmation for prefunding and all nine mutations.
|
|
pub operator_confirmed: bool,
|
|
}
|
|
|
|
/// UI-safe result of one complete Solana Program Metadata Devnet campaign.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ts_rs::TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(
|
|
export,
|
|
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignSummaryPayload.ts"
|
|
)]
|
|
pub(crate) struct DemoExecutionMetadataSolanaProgramCampaignSummaryPayload {
|
|
/// Profile used by the campaign.
|
|
pub profile_name: std::string::String,
|
|
/// Profile-wallet authority.
|
|
pub authority: std::string::String,
|
|
/// Executable program described by both non-canonical PDAs.
|
|
pub described_program: std::string::String,
|
|
/// Fresh Buffer PDA.
|
|
pub buffer: std::string::String,
|
|
/// Confirmed Buffer prefunding signature.
|
|
pub buffer_prefund_signature: std::string::String,
|
|
/// Fresh Metadata PDA.
|
|
pub metadata: std::string::String,
|
|
/// Confirmed Metadata prefunding signature.
|
|
pub metadata_prefund_signature: std::string::String,
|
|
/// Total number of stable operations executed.
|
|
pub step_count: usize,
|
|
/// Number of operations reaching confirmed or finalized state.
|
|
pub confirmed_step_count: usize,
|
|
/// Total number of canonical account snapshots materialized.
|
|
pub materialized_snapshot_count: usize,
|
|
/// Whether all nine steps completed with required evidence.
|
|
pub completed: bool,
|
|
/// Complete fixture as formatted JSON.
|
|
pub fixture_json: std::string::String,
|
|
/// Ordered execution evidence as formatted JSON.
|
|
pub steps_json: std::string::String,
|
|
}
|
|
|
|
/// Returns the two canonical Solana Program Metadata Devnet journeys.
|
|
pub(crate) fn demo_execution_metadata_solana_program_scenarios()
|
|
-> std::vec::Vec<crate::DemoExecutionMetadataSolanaProgramScenarioPayload> {
|
|
return ks_pipeline_demo_scenarios::solana_program_metadata_devnet_scenarios()
|
|
.into_iter()
|
|
.map(|scenario| {
|
|
return crate::DemoExecutionMetadataSolanaProgramScenarioPayload {
|
|
id: scenario.id,
|
|
label: scenario.label,
|
|
fixture_account: scenario.fixture_account,
|
|
steps: scenario
|
|
.steps
|
|
.into_iter()
|
|
.map(|step| {
|
|
return crate::DemoExecutionMetadataSolanaProgramScenarioStepPayload {
|
|
id: step.id,
|
|
label: step.label,
|
|
operation_code: step.operation_code,
|
|
initial_state: fixture_state_code(step.initial_state),
|
|
resulting_state: fixture_state_code(step.resulting_state),
|
|
requires_explicit_approval: step.requires_explicit_approval,
|
|
required_evidence: step.required_evidence,
|
|
};
|
|
})
|
|
.collect(),
|
|
};
|
|
})
|
|
.collect();
|
|
}
|
|
|
|
/// Executes the complete eleven-transaction Solana Program Metadata Devnet campaign.
|
|
pub(crate) async fn demo_execution_metadata_solana_program_execute_campaign(
|
|
app_handle: tauri::AppHandle,
|
|
state: tauri::State<'_, crate::AppState>,
|
|
request: crate::DemoExecutionMetadataSolanaProgramCampaignRequest,
|
|
) -> std::result::Result<
|
|
crate::DemoExecutionMetadataSolanaProgramCampaignSummaryPayload,
|
|
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::DemoExecutionRunGuard {
|
|
running: state.demo_execution_solana_core_running(),
|
|
};
|
|
state
|
|
.demo_execution_solana_core_cancel_requested()
|
|
.store(false, std::sync::atomic::Ordering::Release);
|
|
if !request.operator_confirmed {
|
|
return std::result::Result::Err(
|
|
"Solana Program Metadata campaign requires explicit operator confirmation".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 http_pool = match ks_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 mut options =
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixturePreparationOptions::new();
|
|
options.operator_confirmed = true;
|
|
let observer = crate::DemoExecutionMetadataObserver {
|
|
app_handle,
|
|
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
|
|
};
|
|
let workspace_root = crate::workspace_root_dir();
|
|
let summary = match ks_pipeline_demo_scenarios::execute_devnet_solana_program_metadata_campaign(
|
|
&http_pool,
|
|
&profile,
|
|
workspace_root.as_path(),
|
|
&options,
|
|
&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(campaign_summary_payload(summary));
|
|
}
|
|
|
|
fn campaign_summary_payload(
|
|
summary: ks_pipeline_demo_scenarios::DevnetSolanaProgramMetadataCampaignSummary,
|
|
) -> crate::DemoExecutionMetadataSolanaProgramCampaignSummaryPayload {
|
|
let mut confirmed_step_count = 0_usize;
|
|
let mut materialized_snapshot_count = 0_usize;
|
|
let mut step_values = std::vec::Vec::with_capacity(summary.steps.len());
|
|
for step in &summary.steps {
|
|
let confirmation_status = step
|
|
.execution
|
|
.confirmation
|
|
.as_ref()
|
|
.map(|value| return format!("{:?}", value.status).to_lowercase());
|
|
let confirmed = match step.execution.confirmation.as_ref() {
|
|
std::option::Option::Some(value) => matches!(
|
|
value.status,
|
|
ks_lib::ExApiExecutionConfirmationStatus::Confirmed
|
|
| ks_lib::ExApiExecutionConfirmationStatus::Finalized
|
|
),
|
|
std::option::Option::None => false,
|
|
};
|
|
if confirmed {
|
|
confirmed_step_count = confirmed_step_count.saturating_add(1);
|
|
}
|
|
materialized_snapshot_count =
|
|
materialized_snapshot_count.saturating_add(step.execution.materialized_snapshots.len());
|
|
let operation_code = summary
|
|
.fixture
|
|
.prepared_steps
|
|
.iter()
|
|
.find(|prepared| return prepared.step_id == step.step_id)
|
|
.map(|prepared| return prepared.operation.operation_code().to_string());
|
|
step_values.push(serde_json::json!({
|
|
"scenarioId": &step.scenario_id,
|
|
"stepId": &step.step_id,
|
|
"stepIndex": step.step_index,
|
|
"operationCode": operation_code,
|
|
"simulationSuccess": step.execution.simulation.success,
|
|
"transactionSignature": step.execution.send_result.as_ref().map(|value| return value.signature.0.clone()),
|
|
"confirmationStatus": confirmation_status,
|
|
"before": &step.execution.before,
|
|
"statefulPreflight": &step.execution.stateful_preflight,
|
|
"plan": &step.execution.plan,
|
|
"simulation": &step.execution.simulation,
|
|
"readiness": &step.execution.readiness,
|
|
"after": &step.execution.after,
|
|
"postExecution": &step.execution.post_execution,
|
|
"materializedSnapshots": &step.execution.materialized_snapshots
|
|
}));
|
|
}
|
|
let expected_step_count = ks_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.len();
|
|
let completed = summary.steps.len() == expected_step_count
|
|
&& confirmed_step_count == expected_step_count
|
|
&& summary.steps.iter().all(|step| {
|
|
let close_step = match summary
|
|
.fixture
|
|
.prepared_steps
|
|
.iter()
|
|
.find(|prepared| return prepared.step_id == step.step_id)
|
|
{
|
|
std::option::Option::Some(prepared) => {
|
|
prepared.operation.operation_code() == ks_lib::EX_METADATA_SPM_CLOSE_OPERATION
|
|
},
|
|
std::option::Option::None => false,
|
|
};
|
|
return step.execution.post_execution.is_some()
|
|
&& (!step.execution.materialized_snapshots.is_empty() || close_step);
|
|
});
|
|
let fixture_json = crate::pretty_json(&serde_json::json!({
|
|
"profileName": &summary.fixture.profile_name,
|
|
"authority": &summary.fixture.authority,
|
|
"describedProgram": &summary.fixture.described_program,
|
|
"bufferSeed": &summary.fixture.buffer_seed,
|
|
"buffer": &summary.fixture.buffer,
|
|
"bufferPrefundLamports": summary.fixture.buffer_prefund_lamports.to_string(),
|
|
"bufferPrefundSignature": &summary.fixture.buffer_prefund_signature,
|
|
"metadataSeed": &summary.fixture.metadata_seed,
|
|
"metadata": &summary.fixture.metadata,
|
|
"metadataPrefundLamports": summary.fixture.metadata_prefund_lamports.to_string(),
|
|
"metadataPrefundSignature": &summary.fixture.metadata_prefund_signature,
|
|
"preparedSteps": &summary.fixture.prepared_steps
|
|
}));
|
|
return crate::DemoExecutionMetadataSolanaProgramCampaignSummaryPayload {
|
|
profile_name: summary.fixture.profile_name.clone(),
|
|
authority: summary.fixture.authority.clone(),
|
|
described_program: summary.fixture.described_program.clone(),
|
|
buffer: summary.fixture.buffer.clone(),
|
|
buffer_prefund_signature: summary.fixture.buffer_prefund_signature.clone(),
|
|
metadata: summary.fixture.metadata.clone(),
|
|
metadata_prefund_signature: summary.fixture.metadata_prefund_signature.clone(),
|
|
step_count: summary.steps.len(),
|
|
confirmed_step_count,
|
|
materialized_snapshot_count,
|
|
completed,
|
|
fixture_json,
|
|
steps_json: crate::pretty_json(&step_values),
|
|
};
|
|
}
|
|
|
|
fn fixture_state_code(
|
|
value: ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState,
|
|
) -> std::string::String {
|
|
return match value {
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState::Prefunded => {
|
|
"prefunded".to_string()
|
|
},
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState::Buffer => {
|
|
"buffer".to_string()
|
|
},
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState::MutableMetadata => {
|
|
"mutable_metadata".to_string()
|
|
},
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState::ImmutableMetadata => {
|
|
"immutable_metadata".to_string()
|
|
},
|
|
ks_pipeline_demo_scenarios::SolanaProgramMetadataFixtureState::Closed => {
|
|
"closed".to_string()
|
|
},
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn desktop_inventory_matches_the_two_reusable_journeys() {
|
|
let payloads = crate::demo_execution_metadata_solana_program_scenarios();
|
|
assert_eq!(payloads.len(), 2);
|
|
assert_eq!(
|
|
payloads.iter().map(|scenario| return scenario.steps.len()).sum::<usize>(),
|
|
ks_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES.len()
|
|
);
|
|
assert!(payloads.iter().flat_map(|scenario| return scenario.steps.iter()).all(|step| {
|
|
return !step.required_evidence.is_empty()
|
|
&& ks_lib::EX_METADATA_SPM_SUPPORTED_OPERATION_CODES
|
|
.contains(&step.operation_code.as_str());
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn metadata_frontend_contains_solana_program_campaign_controls() {
|
|
let source = std::include_str!("../frontend/demo_execution_metadata.html");
|
|
for id in [
|
|
"solanaProgramMetadataScenarioSelect",
|
|
"solanaProgramMetadataScenarioOutput",
|
|
"solanaProgramMetadataOperatorConfirmed",
|
|
"executeSolanaProgramMetadataCampaignButton",
|
|
] {
|
|
assert!(source.contains(format!("id=\"{id}\"").as_str()));
|
|
}
|
|
}
|
|
}
|