mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-07-17 13:20:17 +00:00
Merge abb18568dc06bba7f2fc26fda81134f917dd9035 into ce68b4dc2717f6781beba69223cd530285bd6b67
This commit is contained in:
commit
f29c5b0d3e
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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);
|
||||
}
|
||||
|
||||
34
integration_tests/tests/private_transaction_padding.rs
Normal file
34
integration_tests/tests/private_transaction_padding.rs
Normal file
@ -0,0 +1,34 @@
|
||||
#![expect(
|
||||
clippy::tests_outside_test_module,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use anyhow::Result;
|
||||
use integration_tests::{TestContext, fetch_privacy_preserving_tx, new_account, private_mention};
|
||||
use tokio::test;
|
||||
use wallet::cli::{
|
||||
Command, SubcommandReturnValue, programs::native_token_transfer::AuthTransferSubcommand,
|
||||
};
|
||||
|
||||
#[test]
|
||||
async fn private_transaction_pads_notes_to_max() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let account_id = new_account(&mut ctx, true, None).await?;
|
||||
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
|
||||
account_id: private_mention(account_id),
|
||||
});
|
||||
let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?;
|
||||
let SubcommandReturnValue::TransactionExecuted { tx_hash } = result else {
|
||||
anyhow::bail!("Expected TransactionExecuted return value");
|
||||
};
|
||||
|
||||
let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await;
|
||||
|
||||
assert_eq!(tx.message.new_commitments.len(), 7);
|
||||
assert_eq!(tx.message.new_nullifiers.len(), 7);
|
||||
assert_eq!(tx.message.encrypted_private_post_states.len(), 7);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -9,6 +9,7 @@ fn main() {
|
||||
program_outputs,
|
||||
account_identities,
|
||||
program_id,
|
||||
dummy_inputs,
|
||||
} = env::read();
|
||||
|
||||
let execution_state = execution_state::ExecutionState::derive_from_outputs(
|
||||
@ -17,7 +18,7 @@ fn main() {
|
||||
program_outputs,
|
||||
);
|
||||
|
||||
let output = output::compute_circuit_output(execution_state, &account_identities);
|
||||
let output = output::compute_circuit_output(execution_state, &account_identities, dummy_inputs);
|
||||
|
||||
env::commit(&output);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, EncryptedAccountData, EncryptionScheme, EphemeralSecretKey,
|
||||
InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey,
|
||||
PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
|
||||
Commitment, CommitmentSetDigest, DummyInput, EncryptedAccountData, EncryptionScheme,
|
||||
EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
|
||||
account::{Account, AccountId, Nonce},
|
||||
compute_digest_for_path,
|
||||
encryption::{ViewTag, ViewingPublicKey},
|
||||
@ -12,6 +12,7 @@ use crate::execution_state::ExecutionState;
|
||||
pub fn compute_circuit_output(
|
||||
execution_state: ExecutionState,
|
||||
account_identities: &[InputAccountIdentity],
|
||||
dummy_inputs: Vec<DummyInput>,
|
||||
) -> PrivacyPreservingCircuitOutput {
|
||||
let (block_validity_window, timestamp_validity_window, pda_seed_by_position, states_iter) =
|
||||
execution_state.into_parts();
|
||||
@ -262,9 +263,31 @@ pub fn compute_circuit_output(
|
||||
}
|
||||
}
|
||||
|
||||
for dummy in dummy_inputs {
|
||||
emit_dummy_output(&mut output, dummy);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn emit_dummy_output(output: &mut PrivacyPreservingCircuitOutput, dummy: DummyInput) {
|
||||
// Note: the nullifiers and commitments are generated from seeds.
|
||||
// The prover is responsible for their randomness.
|
||||
let nullifier = Nullifier::for_dummy(&dummy.nullifier_seed);
|
||||
let commitment = Commitment::for_dummy(&nullifier, &dummy.commitment_seed);
|
||||
output
|
||||
.new_nullifiers
|
||||
.push((nullifier, dummy.commitment_root));
|
||||
output.new_commitments.push(commitment);
|
||||
// Note: the encrypted post states are pushed as fed into the circuit.
|
||||
// That means that the prover is responsible for managing the randomness
|
||||
// so as to not reveal the padding.
|
||||
//
|
||||
// In particular, it is recommended to generate the ML KEM ciphertext
|
||||
// explicitly as these are not uniformly random.
|
||||
output.encrypted_private_post_states.push(dummy.note);
|
||||
}
|
||||
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "Inputs are distinct concerns from the variant arms; bundling would be artificial"
|
||||
|
||||
@ -19,6 +19,7 @@ pub struct PrivacyPreservingCircuitInput {
|
||||
pub account_identities: Vec<InputAccountIdentity>,
|
||||
/// Program ID.
|
||||
pub program_id: ProgramId,
|
||||
pub dummy_inputs: Vec<DummyInput>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -93,6 +94,20 @@ pub enum InputAccountIdentity {
|
||||
},
|
||||
}
|
||||
|
||||
/// A struct containing necessary data for dummy nullifier and
|
||||
/// commitment generation.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct DummyInput {
|
||||
/// The seed used for generating the dummy nullifier.
|
||||
pub nullifier_seed: [u8; 32],
|
||||
/// The seed used for generating the dummy commitment.
|
||||
pub commitment_seed: [u8; 32],
|
||||
/// The dummy ciphertext, epk, and view tag.
|
||||
pub note: EncryptedAccountData,
|
||||
/// The dummy root.
|
||||
pub commitment_root: CommitmentSetDigest,
|
||||
}
|
||||
|
||||
impl InputAccountIdentity {
|
||||
#[must_use]
|
||||
pub const fn is_public(&self) -> bool {
|
||||
|
||||
@ -2,7 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::account::{Account, AccountId};
|
||||
use crate::{
|
||||
Nullifier,
|
||||
account::{Account, AccountId},
|
||||
};
|
||||
|
||||
/// A commitment to all zero data.
|
||||
/// ```python
|
||||
@ -78,6 +81,15 @@ impl Commitment {
|
||||
bytes.extend_from_slice(&account_bytes_with_hashed_data);
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn for_dummy(nullifier: &Nullifier, commitment_seed: &[u8; 32]) -> Self {
|
||||
const DUMMY_PREFIX: &[u8; 32] = b"/LEE/v0.3/Commitment/Dummy/\x00\x00\x00\x00\x00";
|
||||
let mut bytes = DUMMY_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(&nullifier.0);
|
||||
bytes.extend_from_slice(commitment_seed);
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub type CommitmentSetDigest = [u8; 32];
|
||||
@ -117,7 +129,7 @@ mod tests {
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
use crate::{
|
||||
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH,
|
||||
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, Nullifier,
|
||||
account::{Account, AccountId},
|
||||
};
|
||||
|
||||
@ -138,4 +150,18 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(DUMMY_COMMITMENT_HASH, expected_dummy_commitment_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_dummy_matches_pinned_value() {
|
||||
let nullifier = Nullifier::for_dummy(&[0; 32]);
|
||||
let commitment_seed = [1; 32];
|
||||
let expected_commitment = Commitment([
|
||||
106, 88, 233, 248, 28, 251, 254, 48, 62, 53, 61, 248, 25, 148, 223, 133, 108, 213, 184,
|
||||
83, 73, 145, 122, 104, 89, 220, 111, 132, 40, 87, 12, 105,
|
||||
]);
|
||||
assert_eq!(
|
||||
Commitment::for_dummy(&nullifier, &commitment_seed),
|
||||
expected_commitment
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
)]
|
||||
|
||||
pub use circuit_io::{
|
||||
InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
DummyInput, InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
};
|
||||
pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
|
||||
@ -109,6 +109,14 @@ impl Nullifier {
|
||||
bytes.extend_from_slice(account_id.value());
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn for_dummy(nullifier_seed: &[u8; 32]) -> Self {
|
||||
const DUMMY_PREFIX: &[u8; 32] = b"/LEE/v0.3/Nullifier/Dummy/\x00\x00\x00\x00\x00\x00";
|
||||
let mut bytes = DUMMY_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(nullifier_seed);
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -209,4 +217,14 @@ mod tests {
|
||||
|
||||
assert_eq!(account_id, expected_account_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_dummy_matches_pinned_value() {
|
||||
let nullifier_seed = [0; 32];
|
||||
let expected_nullifier = Nullifier([
|
||||
244, 220, 48, 137, 204, 138, 180, 41, 108, 86, 40, 46, 187, 7, 232, 57, 57, 167, 143,
|
||||
157, 125, 171, 137, 46, 64, 206, 191, 211, 231, 0, 11, 86,
|
||||
]);
|
||||
assert_eq!(Nullifier::for_dummy(&nullifier_seed), expected_nullifier);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,8 @@ use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use lee_core::{
|
||||
InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
DummyInput, InputAccountIdentity, PrivacyPreservingCircuitInput,
|
||||
PrivacyPreservingCircuitOutput,
|
||||
account::AccountWithMetadata,
|
||||
program::{ChainedCall, InstructionData, ProgramId, ProgramOutput},
|
||||
};
|
||||
@ -69,6 +70,22 @@ pub fn execute_and_prove(
|
||||
instruction_data: InstructionData,
|
||||
account_identities: Vec<InputAccountIdentity>,
|
||||
program_with_dependencies: &ProgramWithDependencies,
|
||||
) -> Result<(PrivacyPreservingCircuitOutput, Proof), LeeError> {
|
||||
execute_and_prove_with_dummy_inputs(
|
||||
pre_states,
|
||||
instruction_data,
|
||||
account_identities,
|
||||
vec![],
|
||||
program_with_dependencies,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execute_and_prove_with_dummy_inputs(
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
instruction_data: InstructionData,
|
||||
account_identities: Vec<InputAccountIdentity>,
|
||||
dummy_inputs: Vec<DummyInput>,
|
||||
program_with_dependencies: &ProgramWithDependencies,
|
||||
) -> Result<(PrivacyPreservingCircuitOutput, Proof), LeeError> {
|
||||
let ProgramWithDependencies {
|
||||
program: initial_program,
|
||||
@ -127,6 +144,7 @@ pub fn execute_and_prove(
|
||||
program_outputs,
|
||||
account_identities,
|
||||
program_id: program_with_dependencies.program.id(),
|
||||
dummy_inputs,
|
||||
};
|
||||
|
||||
env_builder.write(&circuit_input).unwrap();
|
||||
|
||||
@ -4,11 +4,13 @@ use anyhow::Result;
|
||||
use keycard_wallet::{KeycardWallet, python_path};
|
||||
use lee::{AccountId, PrivateKey, PublicKey, Signature};
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, Identifier, InputAccountIdentity, MembershipProof,
|
||||
NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||
account::{AccountWithMetadata, Nonce},
|
||||
Commitment, CommitmentSetDigest, DummyInput, Identifier, InputAccountIdentity, MembershipProof,
|
||||
NullifierPublicKey, NullifierSecretKey, PrivateAccountKind, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata, Nonce},
|
||||
compute_digest_for_path,
|
||||
encryption::{ViewTag, ViewingPublicKey},
|
||||
encryption::{
|
||||
Ciphertext, EncryptedAccountData, MlKem768EncapsulationKey, ViewTag, ViewingPublicKey,
|
||||
},
|
||||
};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
|
||||
@ -192,6 +194,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>,
|
||||
@ -264,8 +274,7 @@ impl AccountManager {
|
||||
} => {
|
||||
let acc = lee_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, false, (&npk, &vpk, identifier));
|
||||
let mut random_seed: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut random_seed);
|
||||
let random_seed = random_bytes();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
@ -291,8 +300,7 @@ impl AccountManager {
|
||||
} => {
|
||||
let acc = lee_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, false, account_id);
|
||||
let mut random_seed: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut random_seed);
|
||||
let random_seed = random_bytes();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
@ -398,6 +406,30 @@ impl AccountManager {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Given a count, generate that many dummy inputs with randomized seeds and notes.
|
||||
/// Uses the given commitment root from the account.
|
||||
pub fn dummy_inputs(&self, count: usize) -> Vec<DummyInput> {
|
||||
std::iter::repeat_with(|| DummyInput {
|
||||
nullifier_seed: random_bytes(),
|
||||
commitment_seed: random_bytes(),
|
||||
note: random_dummy_note(),
|
||||
commitment_root: self.dummy_commitment_root,
|
||||
})
|
||||
.take(count)
|
||||
.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
|
||||
@ -549,8 +581,7 @@ fn private_key_tree_acc_preparation(
|
||||
// support from that in the wallet.
|
||||
let sender_pre = AccountWithMetadata::new(from_acc.account.clone(), true, account_id);
|
||||
|
||||
let mut random_seed: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut random_seed);
|
||||
let random_seed = random_bytes();
|
||||
|
||||
Ok(AccountPreparedData {
|
||||
nsk: Some(nsk),
|
||||
@ -582,8 +613,7 @@ fn private_shared_acc_preparation(
|
||||
|
||||
let pre_state = AccountWithMetadata::new(acc, true, account_id);
|
||||
|
||||
let mut random_seed: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut random_seed);
|
||||
let random_seed = random_bytes();
|
||||
|
||||
AccountPreparedData {
|
||||
nsk: Some(nsk),
|
||||
@ -659,6 +689,34 @@ fn random_view_tag() -> ViewTag {
|
||||
byte[0]
|
||||
}
|
||||
|
||||
fn random_bytes() -> [u8; 32] {
|
||||
let mut bytes = [0; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
fn random_vec(len: usize) -> Vec<u8> {
|
||||
let mut bytes = vec![0; len];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Generates a dummy note: random bytes sized to a default-account ciphertext, a real
|
||||
/// ML-KEM ciphertext epk toward a throwaway key, and a random view tag.
|
||||
fn random_dummy_note() -> EncryptedAccountData {
|
||||
// Sized to a default-account ciphertext; matching real data sizes is a separate issue.
|
||||
let ciphertext_len = PrivateAccountKind::HEADER_LEN
|
||||
.checked_add(Account::default().to_bytes().len())
|
||||
.expect("dummy ciphertext length fits in usize");
|
||||
let throwaway_ek = MlKem768EncapsulationKey::from_seed(&random_bytes(), &random_bytes());
|
||||
let (_, epk) = SharedSecretKey::encapsulate(&throwaway_ek);
|
||||
EncryptedAccountData {
|
||||
ciphertext: Ciphertext::from_inner(random_vec(ciphertext_len)),
|
||||
epk,
|
||||
view_tag: random_view_tag(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -674,4 +732,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,12 +649,14 @@ impl WalletCore {
|
||||
)?;
|
||||
|
||||
let private_account_keys = acc_manager.private_account_keys();
|
||||
let (output, proof) = lee::privacy_preserving_transaction::circuit::execute_and_prove(
|
||||
pre_states,
|
||||
instruction_data,
|
||||
acc_manager.account_identities(),
|
||||
&program.to_owned(),
|
||||
)?;
|
||||
let (output, proof) =
|
||||
lee::privacy_preserving_transaction::circuit::execute_and_prove_with_dummy_inputs(
|
||||
pre_states,
|
||||
instruction_data,
|
||||
acc_manager.account_identities(),
|
||||
acc_manager.dummy_inputs_default(),
|
||||
&program.to_owned(),
|
||||
)?;
|
||||
|
||||
let message =
|
||||
lee::privacy_preserving_transaction::message::Message::try_from_circuit_output(
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user