mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-11 08:19:35 +00:00
feat(wallet): pad private transactions to a fixed maximum
This commit is contained in:
parent
a7511eecfc
commit
301273dd44
@ -83,7 +83,6 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
|
||||
let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await;
|
||||
assert_eq!(tx.message.new_commitments[0], new_commitment1);
|
||||
|
||||
assert_eq!(tx.message.new_commitments.len(), 2);
|
||||
for commitment in tx.message.new_commitments {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
@ -172,7 +171,6 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
|
||||
.context("Failed to get private account commitment for sender")?;
|
||||
assert_eq!(tx.message.new_commitments[0], sender_commitment);
|
||||
|
||||
assert_eq!(tx.message.new_commitments.len(), 2);
|
||||
for commitment in tx.message.new_commitments {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
@ -307,7 +305,6 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
// Verify commitments are in state
|
||||
assert_eq!(tx.message.new_commitments.len(), 2);
|
||||
for commitment in tx.message.new_commitments {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -73,7 +73,6 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
|
||||
.context("Failed to get private account commitment for sender")?;
|
||||
assert_eq!(tx.message.new_commitments[0], new_commitment1);
|
||||
|
||||
assert_eq!(tx.message.new_commitments.len(), 2);
|
||||
for commitment in tx.message.new_commitments {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -193,6 +193,14 @@ pub struct AccountManager {
|
||||
}
|
||||
|
||||
impl AccountManager {
|
||||
/// The private-account count that every privacy-preserving transaction is padded up to with
|
||||
/// dummy inputs via the default interface.
|
||||
///
|
||||
/// The value is selected based on the largest account number per-tx currently supported
|
||||
/// (it is 7 for AMM). It is recommended to reassess this value per new actively supported
|
||||
/// application and that all users share the value for a larger anonymity set.
|
||||
const MAX_PRIVATE_ACCOUNTS: usize = 7;
|
||||
|
||||
pub async fn new(
|
||||
wallet: &WalletCore,
|
||||
accounts: Vec<AccountIdentity>,
|
||||
@ -410,6 +418,17 @@ impl AccountManager {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate the dummy inputs that pad this transaction's private-account count up to
|
||||
/// `MAX_PRIVATE_ACCOUNTS`.
|
||||
pub fn dummy_inputs_default(&self) -> Vec<DummyInput> {
|
||||
let private_count = self
|
||||
.states
|
||||
.iter()
|
||||
.filter(|state| matches!(state, State::Private(_)))
|
||||
.count();
|
||||
self.dummy_inputs(Self::MAX_PRIVATE_ACCOUNTS.saturating_sub(private_count))
|
||||
}
|
||||
|
||||
/// Build the per-account input vec for the privacy-preserving circuit. Each variant carries
|
||||
/// exactly the fields the circuit's code path for that account needs, with the ephemeral
|
||||
/// keys (`ssk`) drawn from the cached values that `private_account_keys` and the message
|
||||
@ -684,4 +703,66 @@ mod tests {
|
||||
assert!(acc.is_private());
|
||||
assert!(!acc.is_public());
|
||||
}
|
||||
|
||||
fn private_state() -> State {
|
||||
let npk = NullifierPublicKey([0; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[0; 32], &[0; 32]);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, (&npk, &vpk, 0));
|
||||
State::Private(AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
identifier: 0,
|
||||
vpk,
|
||||
pre_state,
|
||||
proof: None,
|
||||
random_seed: [0; 32],
|
||||
is_pda: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn public_state() -> State {
|
||||
let npk = NullifierPublicKey([0; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[0; 32], &[0; 32]);
|
||||
let account = AccountWithMetadata::new(Account::default(), false, (&npk, &vpk, 0));
|
||||
State::Public { account, sk: None }
|
||||
}
|
||||
|
||||
fn manager(states: Vec<State>) -> AccountManager {
|
||||
AccountManager {
|
||||
states,
|
||||
pin: None,
|
||||
dummy_commitment_root: [0; 32],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_inputs_default_pads_private_count_to_max() {
|
||||
let max = AccountManager::MAX_PRIVATE_ACCOUNTS;
|
||||
|
||||
// Empty txs get padded to the max.
|
||||
assert_eq!(manager(vec![]).dummy_inputs_default().len(), max);
|
||||
// In a padded transaction, the padding amount depends on
|
||||
// the amount of private accounts used.
|
||||
assert_eq!(
|
||||
manager(vec![private_state(), private_state()])
|
||||
.dummy_inputs_default()
|
||||
.len(),
|
||||
max - 2
|
||||
);
|
||||
assert_eq!(
|
||||
manager(vec![private_state(), public_state(), private_state()])
|
||||
.dummy_inputs_default()
|
||||
.len(),
|
||||
max - 2
|
||||
);
|
||||
|
||||
// If the private accounts in the transaction exceed the max, no padding
|
||||
// is done.
|
||||
let full: Vec<State> = std::iter::repeat_with(private_state).take(max).collect();
|
||||
assert_eq!(manager(full).dummy_inputs_default().len(), 0);
|
||||
let over: Vec<State> = std::iter::repeat_with(private_state)
|
||||
.take(max + 2)
|
||||
.collect();
|
||||
assert_eq!(manager(over).dummy_inputs_default().len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -557,13 +557,9 @@ impl WalletCore {
|
||||
instruction_data: InstructionData,
|
||||
program: &ProgramWithDependencies,
|
||||
) -> Result<(HashType, Vec<SharedSecretKey>), ExecutionFailureKind> {
|
||||
self.send_privacy_preserving_tx_with_pre_check(
|
||||
accounts,
|
||||
instruction_data,
|
||||
program,
|
||||
|_| Ok(()),
|
||||
0,
|
||||
)
|
||||
self.send_privacy_preserving_tx_with_pre_check(accounts, instruction_data, program, |_| {
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@ -573,7 +569,6 @@ impl WalletCore {
|
||||
instruction_data: InstructionData,
|
||||
program: &ProgramWithDependencies,
|
||||
tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>,
|
||||
dummy_count: usize,
|
||||
) -> Result<(HashType, Vec<SharedSecretKey>), ExecutionFailureKind> {
|
||||
let acc_manager = account_manager::AccountManager::new(self, accounts).await?;
|
||||
|
||||
@ -592,7 +587,7 @@ impl WalletCore {
|
||||
pre_states,
|
||||
instruction_data,
|
||||
acc_manager.account_identities(),
|
||||
acc_manager.dummy_inputs(dummy_count),
|
||||
acc_manager.dummy_inputs_default(),
|
||||
&program.to_owned(),
|
||||
)?;
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
|
||||
@ -58,7 +58,6 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -92,7 +91,6 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
|
||||
@ -24,7 +24,6 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
@ -58,7 +57,6 @@ impl NativeTokenTransfer<'_> {
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
tx_pre_check,
|
||||
0,
|
||||
)
|
||||
.await
|
||||
.map(|(resp, secrets)| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user