test: add unit tests for SharedAccountEntry and shared account derivation

This commit is contained in:
Moudy 2026-05-06 00:15:05 +02:00
parent d0a88e91e1
commit 5bf24b191d
6 changed files with 175 additions and 0 deletions

View File

@ -44,6 +44,10 @@ async fn new_private_account(ctx: &mut TestContext) -> Result<nssa::AccountId> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),

View File

@ -160,6 +160,10 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
}));
@ -328,6 +332,10 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
}));
@ -393,6 +401,10 @@ async fn initialize_private_account() -> Result<()> {
let mut ctx = TestContext::new().await?;
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
}));
@ -493,6 +505,10 @@ async fn initialize_private_account_using_label() -> Result<()> {
// Create a new private account with a label
let label = "init-private-label".to_owned();
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: Some(label.clone()),
}));

View File

@ -30,6 +30,10 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
// Create a new private account
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
}));
@ -40,6 +44,10 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -119,6 +127,10 @@ async fn restore_keys_from_seed() -> Result<()> {
// Create first private account at root
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: Some(ChainIndex::root()),
label: None,
}));
@ -132,6 +144,10 @@ async fn restore_keys_from_seed() -> Result<()> {
// Create second private account at /0
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: Some(ChainIndex::from_str("/0")?),
label: None,
}));

View File

@ -85,6 +85,10 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<()
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -229,6 +233,10 @@ async fn claim_pinata_to_new_private_account() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),

View File

@ -297,6 +297,10 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -313,6 +317,10 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -460,6 +468,10 @@ async fn create_token_with_private_definition() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: Some(ChainIndex::root()),
label: None,
})),
@ -532,6 +544,10 @@ async fn create_token_with_private_definition() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -662,6 +678,10 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -678,6 +698,10 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -740,6 +764,10 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -855,6 +883,10 @@ async fn shielded_token_transfer() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -966,6 +998,10 @@ async fn deshielded_token_transfer() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -1077,6 +1113,10 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -1093,6 +1133,10 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),
@ -1126,6 +1170,10 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
let result = wallet::cli::execute_subcommand(
ctx.wallet_mut(),
Command::Account(AccountSubcommand::New(NewSubcommand::Private {
for_gms: None,
pda: false,
seed: None,
program_id: None,
cci: None,
label: None,
})),

View File

@ -274,6 +274,89 @@ mod tests {
fn group_key_holders_default_empty() {
let user_data = NSSAUserData::default();
assert!(user_data.group_key_holders.is_empty());
assert!(user_data.shared_accounts.is_empty());
}
#[test]
fn shared_account_entry_serde_round_trip() {
use nssa_core::program::PdaSeed;
let entry = SharedAccountEntry {
group_label: String::from("test-group"),
identifier: 42,
pda_seed: None,
account: nssa_core::account::Account::default(),
};
let encoded = bincode::serialize(&entry).expect("serialize");
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
assert_eq!(decoded.group_label, "test-group");
assert_eq!(decoded.identifier, 42);
assert!(decoded.pda_seed.is_none());
let pda_entry = SharedAccountEntry {
group_label: String::from("pda-group"),
identifier: u128::MAX,
pda_seed: Some(PdaSeed::new([7_u8; 32])),
account: nssa_core::account::Account::default(),
};
let pda_encoded = bincode::serialize(&pda_entry).expect("serialize pda");
let pda_decoded: SharedAccountEntry =
bincode::deserialize(&pda_encoded).expect("deserialize pda");
assert_eq!(pda_decoded.group_label, "pda-group");
assert_eq!(pda_decoded.identifier, u128::MAX);
assert_eq!(pda_decoded.pda_seed.unwrap(), PdaSeed::new([7_u8; 32]));
}
#[test]
fn shared_account_entry_none_pda_seed_round_trips() {
// Verify that an entry with pda_seed=None serializes and deserializes correctly,
// confirming the #[serde(default)] attribute works for backward compatibility.
let entry = SharedAccountEntry {
group_label: String::from("old"),
identifier: 1,
pda_seed: None,
account: nssa_core::account::Account::default(),
};
let encoded = bincode::serialize(&entry).expect("serialize");
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
assert_eq!(decoded.group_label, "old");
assert_eq!(decoded.identifier, 1);
assert!(decoded.pda_seed.is_none());
}
#[test]
fn shared_account_derives_consistent_keys_from_group() {
use nssa_core::program::PdaSeed;
let mut user_data = NSSAUserData::default();
let gms_holder = GroupKeyHolder::from_gms([42_u8; 32]);
user_data.insert_group_key_holder(String::from("my-group"), gms_holder);
let holder = user_data.group_key_holder("my-group").unwrap();
// Regular shared account: derive via tag
let tag = [1_u8; 32];
let keys_a = holder.derive_keys_for_shared_account(&tag);
let keys_b = holder.derive_keys_for_shared_account(&tag);
assert_eq!(
keys_a.generate_nullifier_public_key(),
keys_b.generate_nullifier_public_key(),
);
// PDA shared account: derive via seed
let seed = PdaSeed::new([2_u8; 32]);
let pda_keys_a = holder.derive_keys_for_pda(&seed);
let pda_keys_b = holder.derive_keys_for_pda(&seed);
assert_eq!(
pda_keys_a.generate_nullifier_public_key(),
pda_keys_b.generate_nullifier_public_key(),
);
// PDA and shared derivations don't collide
assert_ne!(
keys_a.generate_nullifier_public_key(),
pda_keys_a.generate_nullifier_public_key(),
);
}
#[test]