mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-25 23:23:11 +00:00
merge main
This commit is contained in:
@@ -60,11 +60,11 @@ impl InitialData {
|
||||
|
||||
let mut private_charlie_key_chain = KeyChain::new_os_random();
|
||||
let mut private_charlie_account_id =
|
||||
AccountId::from(&private_charlie_key_chain.nullifier_public_key);
|
||||
AccountId::from((&private_charlie_key_chain.nullifier_public_key, 0));
|
||||
|
||||
let mut private_david_key_chain = KeyChain::new_os_random();
|
||||
let mut private_david_account_id =
|
||||
AccountId::from(&private_david_key_chain.nullifier_public_key);
|
||||
AccountId::from((&private_david_key_chain.nullifier_public_key, 0));
|
||||
|
||||
// Ensure consistent ordering
|
||||
if private_charlie_account_id > private_david_account_id {
|
||||
@@ -139,11 +139,10 @@ impl InitialData {
|
||||
})
|
||||
})
|
||||
.chain(self.private_accounts.iter().map(|(key_chain, account)| {
|
||||
let account_id = AccountId::from(&key_chain.nullifier_public_key);
|
||||
InitialAccountData::Private(Box::new(PrivateAccountPrivateInitialData {
|
||||
account_id,
|
||||
account: account.clone(),
|
||||
key_chain: key_chain.clone(),
|
||||
identifier: 0,
|
||||
}))
|
||||
}))
|
||||
.collect()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
)]
|
||||
|
||||
use anyhow::Result;
|
||||
use integration_tests::TestContext;
|
||||
use integration_tests::{TestContext, format_private_account_id};
|
||||
use log::info;
|
||||
use nssa::program::Program;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
@@ -70,34 +70,29 @@ async fn new_public_account_with_label() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn new_private_account_with_label() -> Result<()> {
|
||||
async fn add_label_to_existing_account() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let account_id = ctx.existing_private_accounts()[0];
|
||||
let label = "my-test-private-account".to_owned();
|
||||
let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {
|
||||
cci: None,
|
||||
label: Some(label.clone()),
|
||||
}));
|
||||
let command = Command::Account(AccountSubcommand::Label {
|
||||
account_id: Some(format_private_account_id(account_id)),
|
||||
account_label: None,
|
||||
label: label.clone(),
|
||||
});
|
||||
|
||||
let result = execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
// Extract the account_id from the result
|
||||
|
||||
let wallet::cli::SubcommandReturnValue::RegisterAccount { account_id } = result else {
|
||||
panic!("Expected RegisterAccount return value")
|
||||
};
|
||||
|
||||
// Verify the label was stored
|
||||
let stored_label = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.labels
|
||||
.get(&account_id.to_string())
|
||||
.expect("Label should be stored for the new account");
|
||||
.expect("Label should be stored for the account");
|
||||
|
||||
assert_eq!(stored_label.to_string(), label);
|
||||
|
||||
info!("Successfully created private account with label");
|
||||
info!("Successfully set label on existing private account");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ async fn amm_public() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 7,
|
||||
};
|
||||
|
||||
@@ -162,6 +163,7 @@ async fn amm_public() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 7,
|
||||
};
|
||||
|
||||
@@ -550,6 +552,7 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 5,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
@@ -574,6 +577,7 @@ async fn amm_new_pool_using_labels() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 5,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
|
||||
@@ -268,6 +268,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: fund_amount,
|
||||
}),
|
||||
)
|
||||
@@ -500,6 +501,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: fund_amount,
|
||||
}),
|
||||
)
|
||||
@@ -614,6 +616,7 @@ async fn burn_via_ata_private_owner() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: fund_amount,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@ async fn private_transfer_to_owned_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -71,6 +72,7 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: Some(to_npk_string),
|
||||
to_vpk: Some(hex::encode(to_vpk.0)),
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -121,6 +123,7 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -170,12 +173,11 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
||||
};
|
||||
|
||||
// Get the keys for the newly created account
|
||||
let (to_keys, _) = ctx
|
||||
let (to_keys, _, to_identifier) = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||
@@ -186,6 +188,7 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
to_identifier: Some(to_identifier),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -236,6 +239,7 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -280,6 +284,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: Some(to_npk_string),
|
||||
to_vpk: Some(hex::encode(to_vpk.0)),
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -336,12 +341,11 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
||||
};
|
||||
|
||||
// Get the newly created account's keys
|
||||
let (to_keys, _) = ctx
|
||||
let (to_keys, _, to_identifier) = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send transfer using nullifier and viewing public keys
|
||||
@@ -352,6 +356,7 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
to_identifier: Some(to_identifier),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -455,6 +460,7 @@ async fn private_transfer_using_from_label() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -527,3 +533,112 @@ async fn initialize_private_account_using_label() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
// Both transfers below will target this same node with distinct identifiers.
|
||||
let chain_index = ctx.wallet_mut().create_private_accounts_key(None);
|
||||
let (npk, vpk) = {
|
||||
let node = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.key_map
|
||||
.get(&chain_index)
|
||||
.expect("node was just inserted");
|
||||
let key_chain = &node.value.0;
|
||||
(
|
||||
key_chain.nullifier_public_key,
|
||||
key_chain.viewing_public_key.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
let npk_hex = hex::encode(npk.0);
|
||||
let vpk_hex = hex::encode(vpk.0);
|
||||
|
||||
let identifier_1 = 1_u128;
|
||||
let identifier_2 = 2_u128;
|
||||
|
||||
let sender_0: AccountId = ctx.existing_public_accounts()[0];
|
||||
let sender_1: AccountId = ctx.existing_public_accounts()[1];
|
||||
|
||||
wallet::cli::execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: Some(format_public_account_id(sender_0)),
|
||||
from_label: None,
|
||||
to: None,
|
||||
to_label: None,
|
||||
to_npk: Some(npk_hex.clone()),
|
||||
to_vpk: Some(vpk_hex.clone()),
|
||||
to_identifier: Some(identifier_1),
|
||||
amount: 100,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
wallet::cli::execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: Some(format_public_account_id(sender_1)),
|
||||
from_label: None,
|
||||
to: None,
|
||||
to_label: None,
|
||||
to_npk: Some(npk_hex),
|
||||
to_vpk: Some(vpk_hex),
|
||||
to_identifier: Some(identifier_2),
|
||||
amount: 200,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
wallet::cli::execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
Command::Account(AccountSubcommand::SyncPrivate {}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Both accounts must be discovered with the correct balances.
|
||||
let account_id_1 = AccountId::from((&npk, identifier_1));
|
||||
let acc_1 = ctx
|
||||
.wallet()
|
||||
.get_account_private(account_id_1)
|
||||
.context("account for identifier 1 not found after sync")?;
|
||||
assert_eq!(acc_1.balance, 100);
|
||||
|
||||
let account_id_2 = AccountId::from((&npk, identifier_2));
|
||||
let acc_2 = ctx
|
||||
.wallet()
|
||||
.get_account_private(account_id_2)
|
||||
.context("account for identifier 2 not found after sync")?;
|
||||
assert_eq!(acc_2.balance, 200);
|
||||
|
||||
// Both account ids must resolve to the same key node.
|
||||
let tree = &ctx.wallet().storage().user_data.private_key_tree;
|
||||
let ci_1 = tree
|
||||
.account_id_map
|
||||
.get(&account_id_1)
|
||||
.context("account_id_1 missing from private_key_tree.account_id_map")?;
|
||||
let ci_2 = tree
|
||||
.account_id_map
|
||||
.get(&account_id_2)
|
||||
.context("account_id_2 missing from private_key_tree.account_id_map")?;
|
||||
assert_eq!(
|
||||
ci_1, ci_2,
|
||||
"identifiers 1 and 2 under the same NPK must share a single chain_index"
|
||||
);
|
||||
assert_eq!(
|
||||
ci_1, &chain_index,
|
||||
"both accounts must resolve to the key node created at the start of the test"
|
||||
);
|
||||
|
||||
info!("Successfully transferred to two distinct identifiers under the same NPK");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ async fn successful_transfer_to_existing_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -81,6 +82,7 @@ pub async fn successful_transfer_to_new_account() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -119,6 +121,7 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 1_000_000,
|
||||
});
|
||||
|
||||
@@ -159,6 +162,7 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -193,6 +197,7 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -278,6 +283,7 @@ async fn successful_transfer_using_from_label() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -325,6 +331,7 @@ async fn successful_transfer_using_to_label() -> Result<()> {
|
||||
to_label: Some(label),
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ async fn indexer_state_consistency() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -147,6 +148,7 @@ async fn indexer_state_consistency() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -233,6 +235,7 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
|
||||
to_label: Some(to_label_str),
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
amount: 100,
|
||||
to_identifier: Some(0),
|
||||
});
|
||||
|
||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||
@@ -133,6 +134,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
amount: 100,
|
||||
to_identifier: Some(0),
|
||||
});
|
||||
|
||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||
@@ -235,6 +237,7 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
amount: 100,
|
||||
to_identifier: Some(0),
|
||||
});
|
||||
|
||||
runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?;
|
||||
|
||||
@@ -59,12 +59,11 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
||||
};
|
||||
|
||||
// Get the keys for the newly created account
|
||||
let (to_keys, _) = ctx
|
||||
let (to_keys, _, to_identifier) = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(to_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account")?;
|
||||
|
||||
// Send to this account using claiming path (using npk and vpk instead of account ID)
|
||||
@@ -75,6 +74,7 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)),
|
||||
to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)),
|
||||
to_identifier: Some(to_identifier),
|
||||
amount: 100,
|
||||
});
|
||||
|
||||
@@ -151,6 +151,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 100,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
@@ -163,6 +164,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 101,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
@@ -203,6 +205,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 102,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
@@ -215,6 +218,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 103,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
@@ -259,16 +263,16 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
.expect("Acc 4 should be restored");
|
||||
|
||||
assert_eq!(
|
||||
acc1.value.1.program_owner,
|
||||
acc1.value.1[0].1.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
assert_eq!(
|
||||
acc2.value.1.program_owner,
|
||||
acc2.value.1[0].1.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
|
||||
assert_eq!(acc1.value.1.balance, 100);
|
||||
assert_eq!(acc2.value.1.balance, 101);
|
||||
assert_eq!(acc1.value.1[0].1.balance, 100);
|
||||
assert_eq!(acc2.value.1[0].1.balance, 101);
|
||||
|
||||
info!("Tree checks passed, testing restored accounts can transact");
|
||||
|
||||
@@ -280,6 +284,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 10,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
@@ -291,6 +296,7 @@ async fn restore_keys_from_seed() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: 11,
|
||||
});
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
|
||||
@@ -134,6 +134,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
@@ -227,6 +228,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
|
||||
holder_label: None,
|
||||
holder_npk: None,
|
||||
holder_vpk: None,
|
||||
holder_identifier: None,
|
||||
amount: mint_amount,
|
||||
};
|
||||
|
||||
@@ -372,6 +374,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
@@ -566,6 +569,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
holder_label: None,
|
||||
holder_npk: None,
|
||||
holder_vpk: None,
|
||||
holder_identifier: None,
|
||||
amount: mint_amount_public,
|
||||
};
|
||||
|
||||
@@ -614,6 +618,7 @@ async fn create_token_with_private_definition() -> Result<()> {
|
||||
holder_label: None,
|
||||
holder_npk: None,
|
||||
holder_vpk: None,
|
||||
holder_identifier: None,
|
||||
amount: mint_amount_private,
|
||||
};
|
||||
|
||||
@@ -756,6 +761,7 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
@@ -887,6 +893,7 @@ async fn shielded_token_transfer() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
@@ -1013,6 +1020,7 @@ async fn deshielded_token_transfer() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
|
||||
@@ -1131,12 +1139,11 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
};
|
||||
|
||||
// Get keys for foreign mint (claiming path)
|
||||
let (holder_keys, _) = ctx
|
||||
let (holder_keys, _, holder_identifier) = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.user_data
|
||||
.get_private_account(recipient_account_id)
|
||||
.cloned()
|
||||
.context("Failed to get private account keys")?;
|
||||
|
||||
// Mint using claiming path (foreign account)
|
||||
@@ -1148,6 +1155,7 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
|
||||
holder_label: None,
|
||||
holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)),
|
||||
holder_vpk: Some(hex::encode(holder_keys.viewing_public_key.0)),
|
||||
holder_identifier: Some(holder_identifier),
|
||||
amount: mint_amount,
|
||||
};
|
||||
|
||||
@@ -1351,6 +1359,7 @@ async fn transfer_token_using_from_label() -> Result<()> {
|
||||
to_label: None,
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
amount: transfer_amount,
|
||||
};
|
||||
wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?;
|
||||
|
||||
@@ -220,14 +220,17 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
data: Data::default(),
|
||||
},
|
||||
true,
|
||||
AccountId::from(&sender_npk),
|
||||
AccountId::from((&sender_npk, 0)),
|
||||
);
|
||||
let recipient_nsk = [2; 32];
|
||||
let recipient_vsk = [99; 32];
|
||||
let recipient_vpk = ViewingPublicKey::from_scalar(recipient_vsk);
|
||||
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
|
||||
let recipient_pre =
|
||||
AccountWithMetadata::new(Account::default(), false, AccountId::from(&recipient_npk));
|
||||
let recipient_pre = AccountWithMetadata::new(
|
||||
Account::default(),
|
||||
false,
|
||||
AccountId::from((&recipient_npk, 0)),
|
||||
);
|
||||
|
||||
let eph_holder_from = EphemeralKeyHolder::new(&sender_npk);
|
||||
let sender_ss = eph_holder_from.calculate_shared_secret_sender(&sender_vpk);
|
||||
@@ -249,7 +252,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
vec![sender_pre, recipient_pre],
|
||||
Program::serialize_instruction(balance_to_move).unwrap(),
|
||||
vec![1, 2],
|
||||
vec![(sender_npk, sender_ss), (recipient_npk, recipient_ss)],
|
||||
vec![(sender_npk, 0, sender_ss), (recipient_npk, 0, recipient_ss)],
|
||||
vec![sender_nsk],
|
||||
vec![Some(proof)],
|
||||
&program.into(),
|
||||
|
||||
@@ -26,7 +26,7 @@ use nssa_core::program::DEFAULT_PROGRAM_ID;
|
||||
use tempfile::tempdir;
|
||||
use wallet_ffi::{
|
||||
FfiAccount, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey,
|
||||
FfiTransferResult, WalletHandle, error,
|
||||
FfiTransferResult, FfiU128, WalletHandle, error,
|
||||
};
|
||||
|
||||
unsafe extern "C" {
|
||||
@@ -53,6 +53,11 @@ unsafe extern "C" {
|
||||
out_account_id: *mut FfiBytes32,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_create_private_accounts_key(
|
||||
handle: *mut WalletHandle,
|
||||
out_keys: *mut FfiPrivateAccountKeys,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_list_accounts(
|
||||
handle: *mut WalletHandle,
|
||||
out_list: *mut FfiAccountList,
|
||||
@@ -116,6 +121,7 @@ unsafe extern "C" {
|
||||
handle: *mut WalletHandle,
|
||||
from: *const FfiBytes32,
|
||||
to_keys: *const FfiPrivateAccountKeys,
|
||||
to_identifier: *const FfiU128,
|
||||
amount: *const [u8; 16],
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
@@ -132,6 +138,7 @@ unsafe extern "C" {
|
||||
handle: *mut WalletHandle,
|
||||
from: *const FfiBytes32,
|
||||
to_keys: *const FfiPrivateAccountKeys,
|
||||
to_identifier: *const FfiU128,
|
||||
amount: *const [u8; 16],
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
@@ -260,33 +267,28 @@ fn wallet_ffi_create_public_accounts() -> Result<()> {
|
||||
fn wallet_ffi_create_private_accounts() -> Result<()> {
|
||||
let password = "password_for_tests";
|
||||
let n_accounts = 10;
|
||||
// Create `n_accounts` private accounts with wallet FFI
|
||||
let new_private_account_ids_ffi = unsafe {
|
||||
let mut account_ids = Vec::new();
|
||||
// Create `n_accounts` receiving keys with wallet FFI
|
||||
let new_npks_ffi = unsafe {
|
||||
let mut npks = Vec::new();
|
||||
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_default_config(password)?;
|
||||
for _ in 0..n_accounts {
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
wallet_ffi_create_account_private(wallet_ffi_handle, &raw mut out_account_id);
|
||||
account_ids.push(out_account_id.data);
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||
npks.push(out_keys.nullifier_public_key.data);
|
||||
wallet_ffi_free_private_account_keys(&raw mut out_keys);
|
||||
}
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
account_ids
|
||||
npks
|
||||
};
|
||||
|
||||
// All returned IDs must be unique and non-zero
|
||||
assert_eq!(new_private_account_ids_ffi.len(), n_accounts);
|
||||
let unique: HashSet<_> = new_private_account_ids_ffi.iter().collect();
|
||||
assert_eq!(
|
||||
unique.len(),
|
||||
n_accounts,
|
||||
"Duplicate private account IDs returned"
|
||||
);
|
||||
// All returned NPKs must be unique and non-zero
|
||||
assert_eq!(new_npks_ffi.len(), n_accounts);
|
||||
let unique: HashSet<_> = new_npks_ffi.iter().collect();
|
||||
assert_eq!(unique.len(), n_accounts, "Duplicate NPKs returned");
|
||||
assert!(
|
||||
new_private_account_ids_ffi
|
||||
.iter()
|
||||
.all(|id| *id != [0_u8; 32]),
|
||||
"Zero account ID returned"
|
||||
new_npks_ffi.iter().all(|id| *id != [0_u8; 32]),
|
||||
"Zero NPK returned"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -294,46 +296,35 @@ fn wallet_ffi_create_private_accounts() -> Result<()> {
|
||||
#[test]
|
||||
fn wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let mut out_private_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
let home = tempfile::tempdir()?;
|
||||
|
||||
// Create a private account with the wallet FFI and save it
|
||||
unsafe {
|
||||
// Create a receiving key and save
|
||||
let first_npk = unsafe {
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
wallet_ffi_create_account_private(wallet_ffi_handle, &raw mut out_private_account_id);
|
||||
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||
let npk = out_keys.nullifier_public_key.data;
|
||||
wallet_ffi_free_private_account_keys(&raw mut out_keys);
|
||||
wallet_ffi_save(wallet_ffi_handle);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
let private_account_keys = unsafe {
|
||||
let wallet_ffi_handle = load_existing_ffi_wallet(home.path())?;
|
||||
|
||||
let mut private_account = FfiAccount::default();
|
||||
|
||||
let result = wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
&raw const out_private_account_id,
|
||||
&raw mut private_account,
|
||||
);
|
||||
assert_eq!(result, error::WalletFfiError::Success);
|
||||
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
let result = wallet_ffi_get_private_account_keys(
|
||||
wallet_ffi_handle,
|
||||
&raw const out_private_account_id,
|
||||
&raw mut out_keys,
|
||||
);
|
||||
assert_eq!(result, error::WalletFfiError::Success);
|
||||
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
|
||||
out_keys
|
||||
npk
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
nssa::AccountId::from(&private_account_keys.npk()),
|
||||
out_private_account_id.into()
|
||||
// After loading, creating a new key should yield a different NPK (state was persisted)
|
||||
let second_npk = unsafe {
|
||||
let wallet_ffi_handle = load_existing_ffi_wallet(home.path())?;
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||
let npk = out_keys.nullifier_public_key.data;
|
||||
wallet_ffi_free_private_account_keys(&raw mut out_keys);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
npk
|
||||
};
|
||||
|
||||
assert_ne!(first_npk, [0_u8; 32], "First NPK should be non-zero");
|
||||
assert_ne!(second_npk, [0_u8; 32], "Second NPK should be non-zero");
|
||||
assert_ne!(
|
||||
first_npk, second_npk,
|
||||
"Keys should differ after state was persisted"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -344,22 +335,22 @@ fn test_wallet_ffi_list_accounts() -> Result<()> {
|
||||
let password = "password_for_tests";
|
||||
|
||||
// Create the wallet FFI and track which account IDs were created as public/private
|
||||
let (wallet_ffi_handle, created_public_ids, created_private_ids) = unsafe {
|
||||
let (wallet_ffi_handle, created_public_ids) = unsafe {
|
||||
let handle = new_wallet_ffi_with_default_config(password)?;
|
||||
let mut public_ids: Vec<[u8; 32]> = Vec::new();
|
||||
let mut private_ids: Vec<[u8; 32]> = Vec::new();
|
||||
|
||||
// Create 5 public accounts and 5 private accounts, recording their IDs
|
||||
// Create 5 public accounts and 5 receiving keys
|
||||
for _ in 0..5 {
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
wallet_ffi_create_account_public(handle, &raw mut out_account_id);
|
||||
public_ids.push(out_account_id.data);
|
||||
|
||||
wallet_ffi_create_account_private(handle, &raw mut out_account_id);
|
||||
private_ids.push(out_account_id.data);
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_private_accounts_key(handle, &raw mut out_keys);
|
||||
wallet_ffi_free_private_account_keys(&raw mut out_keys);
|
||||
}
|
||||
|
||||
(handle, public_ids, private_ids)
|
||||
(handle, public_ids)
|
||||
};
|
||||
|
||||
// Get the account list with FFI method
|
||||
@@ -382,31 +373,19 @@ fn test_wallet_ffi_list_accounts() -> Result<()> {
|
||||
.filter(|e| e.is_public)
|
||||
.map(|e| e.account_id.data)
|
||||
.collect();
|
||||
let listed_private_ids: HashSet<[u8; 32]> = wallet_ffi_account_list_slice
|
||||
.iter()
|
||||
.filter(|e| !e.is_public)
|
||||
.map(|e| e.account_id.data)
|
||||
.collect();
|
||||
|
||||
for id in &created_public_ids {
|
||||
assert!(
|
||||
listed_public_ids.contains(id),
|
||||
"Created public account not found in list with is_public=true"
|
||||
);
|
||||
}
|
||||
for id in &created_private_ids {
|
||||
assert!(
|
||||
listed_private_ids.contains(id),
|
||||
"Created private account not found in list with is_public=false"
|
||||
);
|
||||
}
|
||||
|
||||
// Total listed accounts must be at least the number we created
|
||||
// Total listed accounts must be at least the number of public accounts created
|
||||
// (receiving keys without synced accounts don't appear in the list)
|
||||
assert!(
|
||||
wallet_ffi_account_list.count >= created_public_ids.len() + created_private_ids.len(),
|
||||
"Listed account count ({}) is less than the number of created accounts ({})",
|
||||
wallet_ffi_account_list.count >= created_public_ids.len(),
|
||||
"Listed account count ({}) is less than the number of created public accounts ({})",
|
||||
wallet_ffi_account_list.count,
|
||||
created_public_ids.len() + created_private_ids.len()
|
||||
created_public_ids.len()
|
||||
);
|
||||
|
||||
unsafe {
|
||||
@@ -710,25 +689,13 @@ fn wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
|
||||
let home = tempfile::tempdir()?;
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
|
||||
// Create a new uninitialized public account
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
// Create a new private account
|
||||
let mut out_account_id = FfiBytes32::default();
|
||||
unsafe {
|
||||
wallet_ffi_create_account_private(wallet_ffi_handle, &raw mut out_account_id);
|
||||
}
|
||||
|
||||
// Check its program owner is the default program id
|
||||
let account: Account = unsafe {
|
||||
let mut out_account = FfiAccount::default();
|
||||
wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
&raw const out_account_id,
|
||||
&raw mut out_account,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
assert_eq!(account.program_owner, DEFAULT_PROGRAM_ID);
|
||||
|
||||
// Call the init funciton
|
||||
// Call the init function
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
wallet_ffi_register_private_account(
|
||||
@@ -832,24 +799,24 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
let from: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
||||
let (to, to_keys) = unsafe {
|
||||
let mut out_account_id = FfiBytes32::default();
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_account_private(wallet_ffi_handle, &raw mut out_account_id);
|
||||
wallet_ffi_get_private_account_keys(
|
||||
wallet_ffi_handle,
|
||||
&raw const out_account_id,
|
||||
&raw mut out_keys,
|
||||
);
|
||||
(out_account_id, out_keys)
|
||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
||||
let to: FfiBytes32 = (&account_id).into();
|
||||
(to, out_keys)
|
||||
};
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
let to_identifier = FfiU128 {
|
||||
data: 0_u128.to_le_bytes(),
|
||||
};
|
||||
wallet_ffi_transfer_shielded(
|
||||
wallet_ffi_handle,
|
||||
&raw const from,
|
||||
&raw const to_keys,
|
||||
&raw const to_identifier,
|
||||
&raw const amount,
|
||||
&raw mut transfer_result,
|
||||
);
|
||||
@@ -966,25 +933,25 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
|
||||
|
||||
let from: FfiBytes32 = (&ctx.ctx().existing_private_accounts()[0]).into();
|
||||
let (to, to_keys) = unsafe {
|
||||
let mut out_account_id = FfiBytes32::default();
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
wallet_ffi_create_account_private(wallet_ffi_handle, &raw mut out_account_id);
|
||||
wallet_ffi_get_private_account_keys(
|
||||
wallet_ffi_handle,
|
||||
&raw const out_account_id,
|
||||
&raw mut out_keys,
|
||||
);
|
||||
(out_account_id, out_keys)
|
||||
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys);
|
||||
let account_id = nssa::AccountId::from((&out_keys.npk(), 0_u128));
|
||||
let to: FfiBytes32 = (&account_id).into();
|
||||
(to, out_keys)
|
||||
};
|
||||
|
||||
let amount: [u8; 16] = 100_u128.to_le_bytes();
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
let to_identifier = FfiU128 {
|
||||
data: 0_u128.to_le_bytes(),
|
||||
};
|
||||
wallet_ffi_transfer_private(
|
||||
wallet_ffi_handle,
|
||||
&raw const from,
|
||||
&raw const to_keys,
|
||||
&raw const to_identifier,
|
||||
&raw const amount,
|
||||
&raw mut transfer_result,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user