From 6e376900f7369e9c6cb9feb23943e5ac99df05ff Mon Sep 17 00:00:00 2001 From: Moudy Date: Fri, 8 May 2026 20:16:10 +0200 Subject: [PATCH] fix: remove export/import commands, rewrite test to use invite/join --- integration_tests/tests/shared_accounts.rs | 44 +++++++++++++------ wallet/src/cli/group.rs | 50 ---------------------- 2 files changed, 31 insertions(+), 63 deletions(-) diff --git a/integration_tests/tests/shared_accounts.rs b/integration_tests/tests/shared_accounts.rs index 09e39c18..fddb9e6b 100644 --- a/integration_tests/tests/shared_accounts.rs +++ b/integration_tests/tests/shared_accounts.rs @@ -2,6 +2,10 @@ clippy::tests_outside_test_module, reason = "Integration test file, not inside a #[cfg(test)] module" )] +#![expect( + clippy::shadow_unrelated, + reason = "Sequential wallet commands naturally reuse the `command` binding" +)] //! Shared account integration tests. //! @@ -75,34 +79,48 @@ async fn group_create_and_shared_account_registration() -> Result<()> { Ok(()) } -/// GMS seal/unseal round-trip: export GMS, re-import under a new name, verify key agreement. +/// GMS seal/unseal round-trip via invite/join, verify key agreement. #[test] -async fn group_export_import_key_agreement() -> Result<()> { +async fn group_invite_join_key_agreement() -> Result<()> { let mut ctx = TestContext::new().await?; + // Generate a sealing key + let command = Command::Group(GroupSubcommand::NewSealingKey); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + // Create a group let command = Command::Group(GroupSubcommand::New { name: "alice-group".into(), }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; - // Export the GMS + // Seal GMS for ourselves (simulating invite to another wallet) + let sealing_sk = ctx + .wallet() + .storage() + .user_data + .sealing_secret_key + .context("Sealing key not found")?; + let sealing_pk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point::from_scalar(sealing_sk); + let holder = ctx .wallet() .storage() .user_data .group_key_holder("alice-group") .context("Group not found")?; - let gms_hex = hex::encode(holder.dangerous_raw_gms()); + let sealed = holder.seal_for(&sealing_pk); + let sealed_hex = hex::encode(&sealed); - // Import under a different name (simulating Bob receiving the GMS) - let command = Command::Group(GroupSubcommand::Import { + // Join under a different name (simulating Bob receiving the sealed GMS) + let command = Command::Group(GroupSubcommand::Join { name: "bob-copy".into(), - gms: gms_hex, + sealed: sealed_hex, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; - // Both derive the same keys for the same tag + // Both derive the same keys for the same derivation seed let alice_holder = ctx .wallet() .storage() @@ -116,12 +134,12 @@ async fn group_export_import_key_agreement() -> Result<()> { .group_key_holder("bob-copy") .unwrap(); - let tag = [42_u8; 32]; + let seed = [42_u8; 32]; let alice_npk = alice_holder - .derive_keys_for_shared_account(&tag) + .derive_keys_for_shared_account(&seed) .generate_nullifier_public_key(); let bob_npk = bob_holder - .derive_keys_for_shared_account(&tag) + .derive_keys_for_shared_account(&seed) .generate_nullifier_public_key(); assert_eq!( @@ -129,14 +147,14 @@ async fn group_export_import_key_agreement() -> Result<()> { "Key agreement: same GMS produces same keys" ); - info!("Key agreement verified"); + info!("Key agreement verified via invite/join"); Ok(()) } /// Fund a shared account from a public account via auth-transfer, then sync. /// TODO: Requires auth-transfer init to work with shared accounts (authorization flow). #[test] -#[ignore] +#[ignore = "Requires auth-transfer init to work with shared accounts (authorization flow)"] async fn fund_shared_account_from_public() -> Result<()> { let mut ctx = TestContext::new().await?; diff --git a/wallet/src/cli/group.rs b/wallet/src/cli/group.rs index 5d5bf045..e7e7c136 100644 --- a/wallet/src/cli/group.rs +++ b/wallet/src/cli/group.rs @@ -15,19 +15,6 @@ pub enum GroupSubcommand { /// Human-readable name for the group. name: String, }, - /// Import a group from raw GMS bytes. - Import { - /// Human-readable name for the group. - name: String, - /// Raw GMS as 64-character hex string. - #[arg(long)] - gms: String, - }, - /// Export the raw GMS hex for backup or manual distribution. - Export { - /// Group name. - name: String, - }, /// List all groups. #[command(visible_alias = "ls")] List, @@ -82,43 +69,6 @@ impl WalletSubcommand for GroupSubcommand { Ok(SubcommandReturnValue::Empty) } - Self::Import { name, gms } => { - if wallet_core - .storage() - .user_data - .group_key_holder(&name) - .is_some() - { - anyhow::bail!("Group '{name}' already exists"); - } - - let gms_bytes: [u8; 32] = hex::decode(&gms) - .context("Invalid GMS hex")? - .try_into() - .map_err(|_err| anyhow::anyhow!("GMS must be exactly 32 bytes"))?; - - let holder = GroupKeyHolder::from_gms(gms_bytes); - wallet_core.insert_group_key_holder(name.clone(), holder); - wallet_core.store_persistent_data().await?; - - println!("Imported group '{name}'"); - Ok(SubcommandReturnValue::Empty) - } - - Self::Export { name } => { - let holder = wallet_core - .storage() - .user_data - .group_key_holder(&name) - .context(format!("Group '{name}' not found"))?; - - let gms_hex = hex::encode(holder.dangerous_raw_gms()); - - println!("Group: {name}"); - println!("GMS: {gms_hex}"); - Ok(SubcommandReturnValue::Empty) - } - Self::List => { let holders = &wallet_core.storage().user_data.group_key_holders; if holders.is_empty() {