v0.2.6-pre.012
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
||||
// version: 15
|
||||
// version: 16
|
||||
|
||||
//! Shared backend state owned by the Wallet Desk Tauri application.
|
||||
|
||||
@@ -126,7 +126,7 @@ impl AppState {
|
||||
effective_wallets_directory_created_on_startup: self.wallet_config_startup.effective_directory_created_on_startup(),
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
root_wallets_directory_created_on_startup: self.wallet_config_startup.root_directory_created_on_startup(),
|
||||
shell_phase: "pre.010-credential-rotation".to_owned(),
|
||||
shell_phase: "pre.012-owner-transfer-export".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
transport_available_endpoint_count,
|
||||
transport_clusters: self.transport_runtime.clusters(),
|
||||
@@ -722,6 +722,71 @@ impl AppState {
|
||||
return self.finish_owner_operation(context, result).await;
|
||||
}
|
||||
|
||||
/// Returns the native save-picker default filename when an OWNER session is currently authorized.
|
||||
pub(crate) fn wallet_export_picker_filename(&self, format: crate::WalletTransferFormatDto) -> ksp_core_lib::Result<String> {
|
||||
let session = self.wallet_session.lock();
|
||||
let session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
return match &*session {
|
||||
crate::WalletSession::Owner { wallet_id, .. } => std::result::Result::Ok(crate::wallet_export_default_filename(wallet_id.as_str(), format)),
|
||||
_ => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_WALLET_AUTHORIZATION_REQUIRED,
|
||||
"Wallet transfer export requires an authorized OWNER session",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Exports the immutable Solana keypair to one Rust-owned native save-picker destination.
|
||||
pub(crate) async fn export_wallet_transfer(
|
||||
&self,
|
||||
destination: std::path::PathBuf,
|
||||
request: crate::WalletExportRequestDto,
|
||||
) -> ksp_core_lib::Result<crate::WalletExportResultDto> {
|
||||
let destination_name = crate::wallet_export_destination_name(destination.as_path());
|
||||
let destination_name = match destination_name {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let format = request.format;
|
||||
let context = self.begin_owner_operation("keypair_export");
|
||||
let context = match context {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let wallet_id = context.wallet_id.clone();
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_WALLET_EXPORT,
|
||||
wallet_id = wallet_id.as_str(),
|
||||
transfer_format = format.code(),
|
||||
"Wallet OWNER transfer export started"
|
||||
);
|
||||
let result = context.owner.export_transfer_file(destination.as_path(), format.wallet_format()).await;
|
||||
let restored = self.finish_owner_operation(context, result).await;
|
||||
if let std::result::Result::Err(error) = restored {
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_WALLET_EXPORT,
|
||||
wallet_id = wallet_id.as_str(),
|
||||
transfer_format = format.code(),
|
||||
error_domain = error.code().domain(),
|
||||
error_code = error.code().code(),
|
||||
"Wallet OWNER transfer export failed"
|
||||
);
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
ksp_logging_lib::info!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_WALLET_EXPORT,
|
||||
wallet_id = wallet_id.as_str(),
|
||||
transfer_format = format.code(),
|
||||
"Wallet OWNER transfer export completed"
|
||||
);
|
||||
return std::result::Result::Ok(crate::WalletExportResultDto { destination_name, format, wallet_id });
|
||||
}
|
||||
|
||||
/// Strongly disables VIEW from the current OWNER session while preserving OWNER authorization.
|
||||
pub(crate) async fn disable_wallet_view(&self) -> ksp_core_lib::Result<crate::WalletViewSecurityStatusDto> {
|
||||
let context = self.begin_owner_operation("view_disable");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/constants.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
//! Logging targets, domains and composite component identifiers owned by Wallet Desk.
|
||||
|
||||
@@ -19,6 +19,8 @@ pub(crate) const TRACING_DOMAIN_SHELL: &str = "wallet.shell";
|
||||
pub(crate) const TRACING_DOMAIN_TRANSPORT: &str = "wallet.transport";
|
||||
/// Structured domain used while preparing Wallet filesystem roots.
|
||||
pub(crate) const TRACING_DOMAIN_WALLET_CONFIG: &str = "wallet.config";
|
||||
/// Structured domain used by native-save-picker OWNER transfer export operations.
|
||||
pub(crate) const TRACING_DOMAIN_WALLET_EXPORT: &str = "wallet.export";
|
||||
/// Structured domain used by native-picker Wallet transfer import operations.
|
||||
pub(crate) const TRACING_DOMAIN_WALLET_IMPORT: &str = "wallet.import";
|
||||
/// Structured domain used while enumerating and inspecting locked Wallet files.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/errors.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
//! Application-local error codes for Wallet Desk composition and desktop runtime surfaces.
|
||||
|
||||
@@ -34,6 +34,9 @@ pub(crate) const ERROR_CODE_WALLET_DIRECTORY_INVALID: ksp_core_lib::ErrorCode =
|
||||
/// Wallet Desk could not inspect or create the configured Wallet directory tree.
|
||||
pub(crate) const ERROR_CODE_WALLET_DIRECTORY_PREPARE_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("wallet_desk", "wallet_directory_prepare_failed");
|
||||
/// A native save-picker Wallet export destination is not a usable local filesystem path.
|
||||
pub(crate) const ERROR_CODE_WALLET_EXPORT_DESTINATION_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("wallet_desk", "wallet_export_destination_invalid");
|
||||
/// A native-picker Wallet transfer source is not a supported regular file or cannot be staged safely.
|
||||
pub(crate) const ERROR_CODE_WALLET_IMPORT_SOURCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("wallet_desk", "wallet_import_source_invalid");
|
||||
/// Wallet import was requested without one previously inspected native-picker source.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/lib.rs
|
||||
// version: 12
|
||||
// version: 13
|
||||
|
||||
//! Tauri desktop application shell for KSP Wallet management and inspection.
|
||||
|
||||
@@ -21,12 +21,14 @@ mod tw_main;
|
||||
mod tw_splash;
|
||||
mod wallet_balance;
|
||||
mod wallet_config;
|
||||
mod wallet_export;
|
||||
mod wallet_import;
|
||||
mod wallet_inventory;
|
||||
mod wallet_metadata;
|
||||
mod wallet_secrets;
|
||||
mod wallet_security;
|
||||
mod wallet_session;
|
||||
mod wallet_transfer;
|
||||
|
||||
/// Runs the KSP wallet desktop application.
|
||||
pub use self::tauri::run;
|
||||
@@ -59,6 +61,8 @@ pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_TRANSPORT;
|
||||
/// Structured domain used while preparing Wallet filesystem roots.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_WALLET_CONFIG;
|
||||
/// Structured domain used by native-save-picker OWNER transfer export operations.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_WALLET_EXPORT;
|
||||
/// Structured domain used by native-picker Wallet transfer import operations.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_WALLET_IMPORT;
|
||||
/// Structured domain used while enumerating and inspecting locked Wallet files.
|
||||
@@ -111,6 +115,8 @@ pub(crate) use self::errors::ERROR_CODE_WALLET_AUTHORIZATION_REQUIRED;
|
||||
pub(crate) use self::errors::ERROR_CODE_WALLET_DIRECTORY_INVALID;
|
||||
/// Wallet Desk could not inspect or create the configured Wallet directory tree.
|
||||
pub(crate) use self::errors::ERROR_CODE_WALLET_DIRECTORY_PREPARE_FAILED;
|
||||
/// Native save-picker Wallet export destination is invalid.
|
||||
pub(crate) use self::errors::ERROR_CODE_WALLET_EXPORT_DESTINATION_INVALID;
|
||||
/// Native-picker transfer source is invalid or cannot be staged.
|
||||
pub(crate) use self::errors::ERROR_CODE_WALLET_IMPORT_SOURCE_INVALID;
|
||||
/// Wallet import has no staged native-picker source.
|
||||
@@ -155,14 +161,20 @@ pub(crate) use self::wallet_balance::format_lamports_as_sol;
|
||||
pub(crate) use self::wallet_config::WalletConfigStartup;
|
||||
/// Resolves the composite-selected Wallet Config and prepares its application-owned directory tree.
|
||||
pub(crate) use self::wallet_config::initialize_wallet_config;
|
||||
/// OWNER transfer export request carrying only the format selected by the frontend.
|
||||
pub(crate) use self::wallet_export::WalletExportRequestDto;
|
||||
/// Safe OWNER transfer export result without path or key material.
|
||||
pub(crate) use self::wallet_export::WalletExportResultDto;
|
||||
/// Builds the deterministic native save-picker filename for the current OWNER Wallet.
|
||||
pub(crate) use self::wallet_export::wallet_export_default_filename;
|
||||
/// Extracts a safe basename from the Rust-only native save-picker destination.
|
||||
pub(crate) use self::wallet_export::wallet_export_destination_name;
|
||||
/// Rust-only staged import source containing bounded zeroizing transfer bytes.
|
||||
pub(crate) use self::wallet_import::PendingWalletImport;
|
||||
/// Import request whose new native Wallet credentials move frontend -> Rust only.
|
||||
pub(crate) use self::wallet_import::WalletImportRequestDto;
|
||||
/// Native picker request containing only the expected transfer format.
|
||||
pub(crate) use self::wallet_import::WalletImportSourceRequestDto;
|
||||
/// Safe transfer format DTO used by the import picker.
|
||||
pub(crate) use self::wallet_import::WalletTransferFormatDto;
|
||||
/// Safe inspection projection for one staged external transfer source.
|
||||
pub(crate) use self::wallet_import::WalletTransferInspectionDto;
|
||||
/// Loads and validates one native-picker import source into bounded zeroizing Rust memory.
|
||||
@@ -225,3 +237,5 @@ pub(crate) use self::wallet_session::WalletUnlockRequestDto;
|
||||
pub(crate) use self::wallet_session::owner_projection;
|
||||
/// Builds an authorized VIEW DTO from a Rust-only Wallet handle.
|
||||
pub(crate) use self::wallet_session::view_projection;
|
||||
/// Shared import/export transfer format selected by Wallet Desk.
|
||||
pub(crate) use self::wallet_transfer::WalletTransferFormatDto;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/tauri.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
//! Tauri runtime assembly for the KSP wallet desktop application.
|
||||
|
||||
@@ -47,6 +47,7 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
|
||||
deselect_wallet,
|
||||
disable_wallet_view,
|
||||
emit_frontend_log,
|
||||
export_wallet_transfer,
|
||||
get_runtime_status,
|
||||
import_wallet,
|
||||
inspect_import_source,
|
||||
@@ -155,6 +156,51 @@ fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Resu
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn export_wallet_transfer(
|
||||
app: tauri::AppHandle,
|
||||
request: crate::WalletExportRequestDto,
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<std::option::Option<crate::WalletExportResultDto>, crate::CommandErrorDto> {
|
||||
let format = request.format;
|
||||
let default_filename = state.wallet_export_picker_filename(format);
|
||||
let default_filename = match default_filename {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||
};
|
||||
let extensions = [format.default_extension()];
|
||||
let selected = app
|
||||
.dialog()
|
||||
.file()
|
||||
.set_title("Export Solana keypair — secret material")
|
||||
.set_file_name(default_filename)
|
||||
.add_filter("Solana keypair transfer", &extensions)
|
||||
.blocking_save_file();
|
||||
let selected = match selected {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_EXPORT, transfer_format = format.code(), "Wallet OWNER transfer export native save picker cancelled");
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
},
|
||||
};
|
||||
let path = selected.into_path();
|
||||
let path = match path {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
let error = ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_WALLET_EXPORT_DESTINATION_INVALID,
|
||||
"Selected Wallet transfer export destination is not a local filesystem path",
|
||||
);
|
||||
return std::result::Result::Err(crate::CommandErrorDto::from_error(&error));
|
||||
},
|
||||
};
|
||||
let result = state.export_wallet_transfer(path, request).await;
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::RuntimeStatusDto, crate::CommandErrorDto> {
|
||||
let result = state.runtime_status();
|
||||
|
||||
52
crates/ksp-app-wallet-desk/src/wallet_export.rs
Normal file
52
crates/ksp-app-wallet-desk/src/wallet_export.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/wallet_export.rs
|
||||
// version: 1
|
||||
|
||||
//! OWNER-only native save-picker Wallet transfer export contracts.
|
||||
|
||||
use ts_rs::TS; // rust-rules: trait-import
|
||||
|
||||
/// OWNER export request containing only the explicit transfer format.
|
||||
#[derive(serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_export/WalletExportRequestDto.ts")]
|
||||
pub(crate) struct WalletExportRequestDto {
|
||||
/// Secret-transfer encoding selected before the Rust-owned save picker opens.
|
||||
pub(crate) format: crate::WalletTransferFormatDto,
|
||||
}
|
||||
|
||||
/// Safe result returned after an OWNER keypair export completes.
|
||||
#[derive(serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_export/WalletExportResultDto.ts")]
|
||||
pub(crate) struct WalletExportResultDto {
|
||||
/// Basename only for the newly created export file; the parent path remains Rust-only.
|
||||
pub(crate) destination_name: String,
|
||||
/// Explicit transfer format used by the export.
|
||||
pub(crate) format: crate::WalletTransferFormatDto,
|
||||
/// Root-scoped native Wallet identifier that authorized the export.
|
||||
pub(crate) wallet_id: String,
|
||||
}
|
||||
|
||||
/// Builds a deterministic native-save default filename without exposing Wallet metadata or Pubkey.
|
||||
#[must_use]
|
||||
pub(crate) fn wallet_export_default_filename(wallet_id: &str, format: crate::WalletTransferFormatDto) -> String {
|
||||
let stem = wallet_id.strip_suffix(crate::WALLET_FILE_SUFFIX).unwrap_or(wallet_id);
|
||||
let stem = if stem.is_empty() { "wallet" } else { stem };
|
||||
return format!("{stem}-solana-keypair.{}", format.default_extension());
|
||||
}
|
||||
|
||||
/// Extracts one safe basename from a native save-picker destination.
|
||||
pub(crate) fn wallet_export_destination_name(destination: &std::path::Path) -> ksp_core_lib::Result<String> {
|
||||
let name = destination.file_name().and_then(std::ffi::OsStr::to_str);
|
||||
return match name {
|
||||
std::option::Option::Some(value) if !value.is_empty() => std::result::Result::Ok(value.to_owned()),
|
||||
_ => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_WALLET_EXPORT_DESTINATION_INVALID,
|
||||
"Selected Wallet export destination has no usable local filename",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/wallet_export.rs"]
|
||||
mod tests;
|
||||
@@ -1,65 +1,18 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/wallet_import.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Native-picker transfer import staging with bounded secret material kept only in Rust.
|
||||
|
||||
use tokio::io::AsyncReadExt; // rust-rules: trait-import
|
||||
use ts_rs::TS; // rust-rules: trait-import
|
||||
|
||||
/// Transfer format selected before opening the native import picker.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_import/WalletTransferFormatDto.ts")]
|
||||
pub(crate) enum WalletTransferFormatDto {
|
||||
/// Standard Solana CLI keypair JSON array containing 64 byte values.
|
||||
SolanaCliJson,
|
||||
/// Canonical Base58 encoding of the complete 64-byte Solana keypair.
|
||||
SolanaKeypairBase58,
|
||||
}
|
||||
|
||||
impl WalletTransferFormatDto {
|
||||
/// Maps the desktop DTO to the stable Wallet transfer format.
|
||||
#[must_use]
|
||||
pub(crate) const fn wallet_format(self) -> ksp_wallet_lib::WalletTransferFormat {
|
||||
return match self {
|
||||
Self::SolanaCliJson => ksp_wallet_lib::WalletTransferFormat::SolanaCliJson,
|
||||
Self::SolanaKeypairBase58 => ksp_wallet_lib::WalletTransferFormat::SolanaKeypairBase58,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the native picker extension used as a convenience filter.
|
||||
#[must_use]
|
||||
pub(crate) const fn default_extension(self) -> &'static str {
|
||||
return match self {
|
||||
Self::SolanaCliJson => "json",
|
||||
Self::SolanaKeypairBase58 => "txt",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the stable transfer format code used by safe diagnostics.
|
||||
#[must_use]
|
||||
pub(crate) const fn code(self) -> &'static str {
|
||||
return match self {
|
||||
Self::SolanaCliJson => "solana_cli_json",
|
||||
Self::SolanaKeypairBase58 => "solana_keypair_base58",
|
||||
};
|
||||
}
|
||||
|
||||
const fn maximum_source_bytes(self) -> usize {
|
||||
return match self {
|
||||
Self::SolanaCliJson => ksp_wallet_lib::KSPWALLET_TRANSFER_MAX_SOLANA_CLI_JSON_BYTES,
|
||||
Self::SolanaKeypairBase58 => ksp_wallet_lib::KSPWALLET_TRANSFER_MAX_BASE58_BYTES,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Request that chooses only the transfer format; the source path comes from the native Rust picker.
|
||||
#[derive(serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_import/WalletImportSourceRequestDto.ts")]
|
||||
pub(crate) struct WalletImportSourceRequestDto {
|
||||
/// Transfer encoding expected for the selected external file.
|
||||
pub(crate) format: WalletTransferFormatDto,
|
||||
pub(crate) format: crate::WalletTransferFormatDto,
|
||||
}
|
||||
|
||||
/// Safe projection returned after validating one native-picker import source.
|
||||
@@ -68,7 +21,7 @@ pub(crate) struct WalletImportSourceRequestDto {
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_import/WalletTransferInspectionDto.ts")]
|
||||
pub(crate) struct WalletTransferInspectionDto {
|
||||
/// Transfer format used to validate the secret source.
|
||||
pub(crate) format: WalletTransferFormatDto,
|
||||
pub(crate) format: crate::WalletTransferFormatDto,
|
||||
/// Public identity derived from the imported keypair.
|
||||
pub(crate) pubkey: String,
|
||||
/// Basename only; no parent path is ever projected.
|
||||
@@ -95,7 +48,7 @@ pub(crate) struct WalletImportRequestDto {
|
||||
/// Rust-only staged transfer source. Secret bytes are bounded and zeroized on drop.
|
||||
pub(crate) struct PendingWalletImport {
|
||||
bytes: zeroize::Zeroizing<std::vec::Vec<u8>>,
|
||||
format: WalletTransferFormatDto,
|
||||
format: crate::WalletTransferFormatDto,
|
||||
inspection: WalletTransferInspectionDto,
|
||||
}
|
||||
|
||||
@@ -120,7 +73,10 @@ impl PendingWalletImport {
|
||||
}
|
||||
|
||||
/// Reads, bounds and validates one native-picker transfer source while retaining no external path.
|
||||
pub(crate) async fn stage_wallet_import_source(source: std::path::PathBuf, format: WalletTransferFormatDto) -> ksp_core_lib::Result<PendingWalletImport> {
|
||||
pub(crate) async fn stage_wallet_import_source(
|
||||
source: std::path::PathBuf,
|
||||
format: crate::WalletTransferFormatDto,
|
||||
) -> ksp_core_lib::Result<PendingWalletImport> {
|
||||
let metadata = tokio::fs::symlink_metadata(source.as_path()).await;
|
||||
let metadata = match metadata {
|
||||
std::result::Result::Ok(value) => value,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/wallet_session.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
//! Durable root-scoped Wallet session lifecycle for Wallet Desk.
|
||||
|
||||
@@ -170,7 +170,7 @@ pub(crate) enum WalletSession {
|
||||
/// Whether the authenticated Wallet currently exposes a VIEW slot.
|
||||
view_enabled: bool,
|
||||
},
|
||||
/// One OWNER-authorized privileged mutation temporarily owns the handle outside the session mutex.
|
||||
/// One OWNER-authorized privileged operation temporarily owns the handle outside the session mutex.
|
||||
OwnerOperation {
|
||||
/// Native Wallet filename / root-scoped identifier.
|
||||
wallet_id: String,
|
||||
|
||||
59
crates/ksp-app-wallet-desk/src/wallet_transfer.rs
Normal file
59
crates/ksp-app-wallet-desk/src/wallet_transfer.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/wallet_transfer.rs
|
||||
// version: 1
|
||||
|
||||
//! Shared Wallet Desk transfer format contract used by import and OWNER export workflows.
|
||||
|
||||
use ts_rs::TS; // rust-rules: trait-import
|
||||
|
||||
/// Explicit Solana keypair transfer format selected by Wallet Desk.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_transfer/WalletTransferFormatDto.ts")]
|
||||
pub(crate) enum WalletTransferFormatDto {
|
||||
/// Standard Solana CLI keypair JSON array containing 64 byte values.
|
||||
SolanaCliJson,
|
||||
/// Canonical Base58 encoding of the complete 64-byte Solana keypair.
|
||||
SolanaKeypairBase58,
|
||||
}
|
||||
|
||||
impl WalletTransferFormatDto {
|
||||
/// Maps the desktop DTO to the stable Wallet transfer format.
|
||||
#[must_use]
|
||||
pub(crate) const fn wallet_format(self) -> ksp_wallet_lib::WalletTransferFormat {
|
||||
return match self {
|
||||
Self::SolanaCliJson => ksp_wallet_lib::WalletTransferFormat::SolanaCliJson,
|
||||
Self::SolanaKeypairBase58 => ksp_wallet_lib::WalletTransferFormat::SolanaKeypairBase58,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the native picker/save extension used as a convenience filter.
|
||||
#[must_use]
|
||||
pub(crate) const fn default_extension(self) -> &'static str {
|
||||
return match self {
|
||||
Self::SolanaCliJson => "json",
|
||||
Self::SolanaKeypairBase58 => "txt",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the stable transfer format code used by safe diagnostics.
|
||||
#[must_use]
|
||||
pub(crate) const fn code(self) -> &'static str {
|
||||
return match self {
|
||||
Self::SolanaCliJson => "solana_cli_json",
|
||||
Self::SolanaKeypairBase58 => "solana_keypair_base58",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the maximum accepted import source size for this transfer encoding.
|
||||
#[must_use]
|
||||
pub(crate) const fn maximum_source_bytes(self) -> usize {
|
||||
return match self {
|
||||
Self::SolanaCliJson => ksp_wallet_lib::KSPWALLET_TRANSFER_MAX_SOLANA_CLI_JSON_BYTES,
|
||||
Self::SolanaKeypairBase58 => ksp_wallet_lib::KSPWALLET_TRANSFER_MAX_BASE58_BYTES,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/wallet_transfer.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user