v0.5.2-pre.006
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
<!-- file: ks-pipeline-demo-scenarios/CHANGELOG.md -->
|
||||
<!-- version: 78 -->
|
||||
<!-- version: 79 -->
|
||||
|
||||
# CHANGELOG — ks-pipeline-demo-scenarios
|
||||
|
||||
## `0.5.2-pre.006`
|
||||
|
||||
- ajoute l'inspection et l'unlock explicites du wallet natif sélectionné par `profile.wallet.wallet_alias`, avec `WalletPassword` fourni uniquement au runtime ;
|
||||
- renomme le chemin historique en `load_profile_temporary_wallet` et interdit qu'un alias persistant configuré retombe silencieusement sur le wallet temporaire ;
|
||||
- refuse les fichiers `.kswallet` dans le workflow CLI Token-2022 fondé sur `solana-keygen` / `spl-token`, exige explicitement un export `SolanaCliJson` et ne révèle plus le chemin complet d’un wallet absent ;
|
||||
- ajoute des tests de sélection persistante, unlock, refus de fallback et rejet du conteneur natif dans le workflow CLI.
|
||||
|
||||
## `0.5.1-pre.007`
|
||||
|
||||
- supprime la composition par défaut dédiée aux scénarios et charge directement les defaults partagés de `ks-config` ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# file: ks-pipeline-demo-scenarios/Cargo.toml
|
||||
# version: 12
|
||||
# version: 13
|
||||
|
||||
[package]
|
||||
name = "ks-pipeline-demo-scenarios"
|
||||
@@ -43,5 +43,8 @@ tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ks-pipeline-demo-scenarios/README.md -->
|
||||
<!-- version: 30 -->
|
||||
<!-- version: 31 -->
|
||||
|
||||
# ks-pipeline-demo-scenarios
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
La crate contient :
|
||||
|
||||
- la bibliothèque Rust `ks_pipeline_demo_scenarios` ;
|
||||
- le binaire ciblé `ks-pipeline-demo-scenarios-cli`.
|
||||
- le binaire ciblé `ks-pipeline-demo-scenarios-cli` ;
|
||||
- les adaptateurs de sélection wallet qui respectent `wallet_alias` sans stocker de password dans la configuration.
|
||||
|
||||
## Responsabilités
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ks-pipeline-demo-scenarios/USAGE.md -->
|
||||
<!-- version: 31 -->
|
||||
<!-- version: 32 -->
|
||||
|
||||
# Utilisation de ks-pipeline-demo-scenarios
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
La bibliothèque expose des scénarios Devnet contrôlés. Le binaire permet leur exécution hors de l’application desktop lorsque la commande correspondante est disponible.
|
||||
|
||||
## Wallet sélectionné par le profil
|
||||
|
||||
`inspect_selected_profile_wallet()` résout l'alias persistant non sensible configuré dans `ks-config`. `unlock_selected_profile_wallet()` exige ensuite un `ks_wallet::WalletPassword` fourni explicitement par le consommateur. Les scénarios historiques qui utilisent encore le wallet temporaire refusent un fallback silencieux lorsqu'un `wallet_alias` persistant est configuré.
|
||||
|
||||
Le CLI `prepare-token-2022-fixture` reste un workflow externe Solana CLI : son `--wallet` doit viser un keypair JSON exporté explicitement et refuse un fichier `.kswallet`.
|
||||
|
||||
## Initialiser l’environnement
|
||||
|
||||
```rust
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/bin/ks_pipeline_demo_scenarios_cli.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -102,6 +102,13 @@ fn parse_prepare_options(
|
||||
));
|
||||
},
|
||||
};
|
||||
if wallet_path.extension().and_then(|value| return value.to_str())
|
||||
== std::option::Option::Some(ks_wallet::KSWALLET_FILE_EXTENSION)
|
||||
{
|
||||
return std::result::Result::Err(ks_core::Error::config(
|
||||
"native .kswallet files cannot be passed to Solana CLI fixture preparation; export SolanaCliJson explicitly first",
|
||||
));
|
||||
}
|
||||
let wallet_dir = match wallet_dir {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => match wallet_path.parent() {
|
||||
@@ -127,6 +134,19 @@ fn usage_error<T>(message: &str) -> ks_core::Result<T> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn prepare_options_reject_native_wallet_path() {
|
||||
let arguments = vec![
|
||||
"prepare-token-2022-fixture".to_string(),
|
||||
"--rpc-url".to_string(),
|
||||
"https://api.devnet.solana.com".to_string(),
|
||||
"--wallet".to_string(),
|
||||
"operator.kswallet".to_string(),
|
||||
];
|
||||
let options = super::parse_prepare_options(arguments.as_slice());
|
||||
assert!(options.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_options_accept_explicit_values() {
|
||||
let arguments = vec![
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/lib.rs
|
||||
// version: 29
|
||||
// version: 30
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -231,10 +231,14 @@ pub(crate) use self::solana::emit;
|
||||
pub(crate) use self::solana::ensure_not_cancelled;
|
||||
/// Executes one bounded System Program transfer on Devnet.
|
||||
pub use self::solana::execute_devnet_system_transfer;
|
||||
/// Loads the persistent wallet configured by one execution profile.
|
||||
pub(crate) use self::solana::load_profile_wallet;
|
||||
/// Inspects the persistent native wallet alias selected by one execution profile.
|
||||
pub use self::solana::inspect_selected_profile_wallet;
|
||||
/// Loads the managed temporary wallet used by legacy Devnet scenarios.
|
||||
pub(crate) use self::solana::load_profile_temporary_wallet;
|
||||
/// Formats one failed simulation without discarding runtime diagnostics.
|
||||
pub(crate) use self::solana::simulation_failure_message;
|
||||
/// Unlocks the persistent native wallet alias selected by one execution profile.
|
||||
pub use self::solana::unlock_selected_profile_wallet;
|
||||
/// Formats safety violations for one denied execution plan.
|
||||
pub(crate) use self::solana::violation_message;
|
||||
/// Complete request for one Devnet Associated Token Account execution.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
|
||||
// version: 22
|
||||
// version: 23
|
||||
|
||||
//! Real Devnet simulation and submission for current Metaplex Token Metadata operations.
|
||||
|
||||
@@ -839,7 +839,7 @@ where
|
||||
),
|
||||
));
|
||||
}
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs
|
||||
// version: 21
|
||||
// version: 22
|
||||
|
||||
//! Native Metaplex Token Metadata fixture preparation for Devnet demos.
|
||||
|
||||
@@ -168,7 +168,7 @@ pub async fn prepare_metaplex_create_fixture(
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
}
|
||||
let operator = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let operator = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Real Devnet simulation and controlled submission for Solana Program Metadata.
|
||||
|
||||
@@ -355,7 +355,7 @@ where
|
||||
),
|
||||
));
|
||||
}
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs
|
||||
// version: 8
|
||||
// version: 9
|
||||
|
||||
//! Native Solana Program Metadata fixture preparation for Devnet journeys.
|
||||
|
||||
@@ -143,7 +143,7 @@ pub async fn prepare_solana_program_metadata_fixture(
|
||||
"Solana Program Metadata fixture preparation requires a Devnet endpoint",
|
||||
));
|
||||
}
|
||||
let operator = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let operator = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/solana.rs
|
||||
// version: 16
|
||||
// version: 17
|
||||
|
||||
//! Devnet Solana execution orchestration with canonical post-validation.
|
||||
|
||||
@@ -305,7 +305,7 @@ where
|
||||
if let std::result::Result::Err(error) = cancellation_result {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let wallet = match load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(wallet) => wallet,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -983,17 +983,92 @@ fn validate_devnet_profile(
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
pub(crate) async fn load_profile_wallet(
|
||||
/// Resolves the wallet directory selected by one profile relative to the workspace root.
|
||||
fn profile_wallet_directory(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> std::path::PathBuf {
|
||||
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
|
||||
if configured.is_absolute() {
|
||||
return configured;
|
||||
}
|
||||
return workspace_root.join(configured);
|
||||
}
|
||||
|
||||
/// Inspects the persistent native wallet alias selected by one profile, when configured.
|
||||
pub async fn inspect_selected_profile_wallet(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<std::option::Option<ks_wallet::WalletFileHandle>> {
|
||||
let configured_alias = match profile.wallet.wallet_alias.as_deref() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
|
||||
};
|
||||
let alias = match ks_wallet::WalletAlias::parse(configured_alias) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let manager =
|
||||
match ks_wallet::WalletManager::new(profile_wallet_directory(profile, workspace_root)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let handle = match manager.lookup(&alias).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return match handle {
|
||||
std::option::Option::Some(value) => {
|
||||
std::result::Result::Ok(std::option::Option::Some(value))
|
||||
},
|
||||
std::option::Option::None => std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_selected_alias_not_found",
|
||||
"selected native wallet alias was not found in the configured wallet directory",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Unlocks the persistent native wallet selected by one profile using an explicit password.
|
||||
pub async fn unlock_selected_profile_wallet(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
workspace_root: &std::path::Path,
|
||||
password: ks_wallet::WalletPassword,
|
||||
) -> ks_core::Result<ks_wallet::UnlockedWallet> {
|
||||
let configured_alias = match profile.wallet.wallet_alias.as_deref() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_selected_alias_missing",
|
||||
"profile does not select a persistent native wallet alias",
|
||||
));
|
||||
},
|
||||
};
|
||||
let alias = match ks_wallet::WalletAlias::parse(configured_alias) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let manager =
|
||||
match ks_wallet::WalletManager::new(profile_wallet_directory(profile, workspace_root)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return manager.unlock(&alias, password).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn load_profile_temporary_wallet(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<ks_wallet::TemporaryWallet> {
|
||||
let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str());
|
||||
let directory = if configured.is_absolute() {
|
||||
configured
|
||||
} else {
|
||||
workspace_root.join(configured)
|
||||
};
|
||||
let store = match ks_wallet::TemporaryWalletStore::new(directory) {
|
||||
if profile.wallet.wallet_alias.is_some() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"wallet_persistent_selection_requires_explicit_unlock",
|
||||
"profile selects a persistent native wallet; temporary-wallet fallback is forbidden",
|
||||
));
|
||||
}
|
||||
let store = match ks_wallet::TemporaryWalletStore::new(profile_wallet_directory(
|
||||
profile,
|
||||
workspace_root,
|
||||
)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -1223,6 +1298,18 @@ pub(crate) fn emit<O>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(unix)]
|
||||
fn make_directory_private(path: &std::path::Path) {
|
||||
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
|
||||
let permissions = std::fs::Permissions::from_mode(0o700);
|
||||
if let std::result::Result::Err(error) = std::fs::set_permissions(path, permissions) {
|
||||
panic!("temporary wallet directory permissions failed: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn make_directory_private(_path: &std::path::Path) {}
|
||||
|
||||
fn example_devnet_profile() -> ks_config::ProfileConfig {
|
||||
let config = match ks_config::parse_config_json(include_str!(
|
||||
"../../test-fixtures/config/resolved.app.config.json"
|
||||
@@ -1240,6 +1327,71 @@ mod tests {
|
||||
return ks_lib::MdPubkey("Vote111111111111111111111111111111111111111".to_string());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn selected_persistent_wallet_requires_explicit_password_path() {
|
||||
let temporary = match tempfile::tempdir() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("temporary directory failed: {error}"),
|
||||
};
|
||||
make_directory_private(temporary.path());
|
||||
let mut profile = example_devnet_profile();
|
||||
profile.wallet.wallet_dir = temporary.path().display().to_string();
|
||||
profile.wallet.wallet_alias = std::option::Option::Some("selected-operator".to_string());
|
||||
let manager = match ks_wallet::WalletManager::new(temporary.path()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet manager failed: {error}"),
|
||||
};
|
||||
let alias = match ks_wallet::WalletAlias::parse("selected-operator") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet alias failed: {error}"),
|
||||
};
|
||||
let create_password = match ks_wallet::WalletPassword::new("selected-password".to_string())
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet password failed: {error}"),
|
||||
};
|
||||
let created = match manager.create(alias, create_password).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet creation failed: {error}"),
|
||||
};
|
||||
let expected_public_key = created.public_key();
|
||||
created.lock();
|
||||
let inspected =
|
||||
match super::inspect_selected_profile_wallet(&profile, temporary.path()).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(value)) => value,
|
||||
std::result::Result::Ok(std::option::Option::None) => {
|
||||
panic!("selected wallet must be present")
|
||||
},
|
||||
std::result::Result::Err(error) => panic!("wallet inspection failed: {error}"),
|
||||
};
|
||||
assert_eq!(inspected.alias().as_str(), "selected-operator");
|
||||
assert_eq!(inspected.public_key(), expected_public_key.as_str());
|
||||
let unlock_password = match ks_wallet::WalletPassword::new("selected-password".to_string())
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet password failed: {error}"),
|
||||
};
|
||||
let unlocked = match super::unlock_selected_profile_wallet(
|
||||
&profile,
|
||||
temporary.path(),
|
||||
unlock_password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet unlock failed: {error}"),
|
||||
};
|
||||
assert_eq!(unlocked.public_key(), expected_public_key);
|
||||
let fallback = super::load_profile_temporary_wallet(&profile, temporary.path()).await;
|
||||
assert!(fallback.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn profile_without_persistent_alias_has_no_selected_native_wallet() {
|
||||
let profile = example_devnet_profile();
|
||||
assert!(profile.wallet.wallet_alias.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_and_profile_validation_are_conservative() {
|
||||
let profile = example_devnet_profile();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
|
||||
// version: 15
|
||||
// version: 16
|
||||
|
||||
//! Devnet ATA execution with stateful and canonical post-validation.
|
||||
|
||||
@@ -491,7 +491,7 @@ where
|
||||
),
|
||||
));
|
||||
}
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -1094,7 +1094,7 @@ mod tests {
|
||||
.parent()
|
||||
.map(std::path::Path::to_path_buf)
|
||||
.unwrap_or_else(|| panic!("workspace root missing"));
|
||||
let wallet = crate::load_profile_wallet(&profile, workspace_root.as_path())
|
||||
let wallet = crate::load_profile_temporary_wallet(&profile, workspace_root.as_path())
|
||||
.await
|
||||
.unwrap_or_else(|error| panic!("profile wallet failed: {error}"));
|
||||
let wallet_pubkey = wallet.public_key();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
|
||||
// version: 11
|
||||
// version: 12
|
||||
|
||||
//! Devnet SPL Memo v4 execution with canonical post-validation.
|
||||
|
||||
@@ -155,7 +155,7 @@ where
|
||||
),
|
||||
));
|
||||
}
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs
|
||||
// version: 15
|
||||
// version: 16
|
||||
|
||||
//! Devnet classic SPL Token execution with stateful and canonical post-validation.
|
||||
|
||||
@@ -479,7 +479,7 @@ where
|
||||
failed_readiness_message(&readiness),
|
||||
));
|
||||
}
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
|
||||
// version: 14
|
||||
// version: 15
|
||||
|
||||
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
|
||||
|
||||
@@ -319,7 +319,7 @@ where
|
||||
"lifecycle account preparation endpoint is not Devnet",
|
||||
));
|
||||
}
|
||||
let payer = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let payer = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -460,7 +460,7 @@ where
|
||||
"the destructive lifecycle requires submit=true and explicit operator confirmation",
|
||||
));
|
||||
}
|
||||
let profile_wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let profile_wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
//! Devnet Token-2022 execution with stateful and canonical post-validation.
|
||||
|
||||
@@ -469,7 +469,7 @@ where
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let wallet = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let wallet = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/fixture.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Idempotent Token-2022 public scenario fixture preparation.
|
||||
|
||||
@@ -181,16 +181,22 @@ pub async fn prepare_token_2022_fixture(
|
||||
}
|
||||
|
||||
fn validate_options(options: &crate::Token2022FixturePreparationOptions) -> ks_core::Result<()> {
|
||||
if options.wallet_path.extension().and_then(|value| return value.to_str())
|
||||
== std::option::Option::Some(ks_wallet::KSWALLET_FILE_EXTENSION)
|
||||
{
|
||||
return std::result::Result::Err(ks_core::Error::config(
|
||||
"Token-2022 CLI fixture preparation requires an explicit Solana CLI JSON export; native .kswallet files are never passed to external CLI tools",
|
||||
));
|
||||
}
|
||||
if options.rpc_url.trim().is_empty() {
|
||||
return std::result::Result::Err(ks_core::Error::config(
|
||||
"Token-2022 fixture RPC URL must not be empty",
|
||||
));
|
||||
}
|
||||
if !options.wallet_path.is_file() {
|
||||
return std::result::Result::Err(ks_core::Error::config(format!(
|
||||
"Token-2022 fixture wallet does not exist: {}",
|
||||
options.wallet_path.display()
|
||||
)));
|
||||
return std::result::Result::Err(ks_core::Error::config(
|
||||
"Token-2022 fixture wallet does not exist",
|
||||
));
|
||||
}
|
||||
if options.decimals > 18 {
|
||||
return std::result::Result::Err(ks_core::Error::config(
|
||||
@@ -448,6 +454,18 @@ mod tests {
|
||||
assert!(fixture.contains("export KS_PUBLIC_DEVNET_WALLET_ADDRESS=authority\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_reject_native_wallet_container_for_legacy_cli_workflow() {
|
||||
let options = super::Token2022FixturePreparationOptions {
|
||||
rpc_url: "https://api.devnet.solana.com".to_string(),
|
||||
wallet_path: std::path::PathBuf::from("operator.kswallet"),
|
||||
wallet_dir: std::path::PathBuf::from("wallets"),
|
||||
decimals: 9,
|
||||
};
|
||||
let error = super::validate_options(&options);
|
||||
assert!(error.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_reject_excessive_decimals() {
|
||||
let options = super::Token2022FixturePreparationOptions {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Native Token-2022 mint preparation for the embedded Token Metadata Devnet campaign.
|
||||
|
||||
@@ -96,7 +96,7 @@ pub async fn prepare_token_2022_metadata_fixture(
|
||||
"Token-2022 metadata fixture requires enabled Devnet submission and explicit operator confirmation",
|
||||
));
|
||||
}
|
||||
let operator = match crate::load_profile_wallet(profile, workspace_root).await {
|
||||
let operator = match crate::load_profile_temporary_wallet(profile, workspace_root).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user