refactor!: bundle private/public i/o into action struccts

This commit is contained in:
Artem Gureev 2026-07-08 12:33:52 +00:00 committed by agureev
parent 571f35b384
commit f3d58b4da3
18 changed files with 386 additions and 333 deletions

View File

@ -83,10 +83,10 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
.context("Failed to get private account commitment for sender")?;
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.private_actions[0].commitment, new_commitment1);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments {
assert_eq!(tx.message.private_actions.len(), 2);
for commitment in tx.message.commitments() {
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
}
@ -172,10 +172,10 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert_eq!(tx.message.new_commitments[0], sender_commitment);
assert_eq!(tx.message.private_actions[0].commitment, sender_commitment);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments {
assert_eq!(tx.message.private_actions.len(), 2);
for commitment in tx.message.commitments() {
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
}
@ -251,7 +251,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
assert!(
verify_commitment_is_in_state(
tx.message.new_commitments[0].clone(),
tx.message.private_actions[0].commitment.clone(),
ctx.sequencer_client()
)
.await
@ -309,8 +309,8 @@ 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_eq!(tx.message.private_actions.len(), 2);
for commitment in tx.message.commitments() {
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
}
@ -675,8 +675,9 @@ async fn init_with_dummy_commitment_root_produces_valid_root() -> Result<()> {
let output = prove_init_with_commitment_root(&ctx, expected_digest).await?;
assert_eq!(output.new_nullifiers.len(), 1);
let (nullifier, digest) = &output.new_nullifiers[0];
assert_eq!(output.private_actions.len(), 1);
let action = &output.private_actions[0];
let (nullifier, digest) = (&action.nullifier, &action.root);
assert_eq!(
*nullifier,
Nullifier::for_account_initialization(&recipient_account_id)
@ -701,14 +702,14 @@ async fn init_nullifier_digest_is_bound_to_commitment_root() -> Result<()> {
let output_with_root = prove_init_with_commitment_root(&ctx, expected_digest).await?;
let output_without_root = prove_init_with_commitment_root(&ctx, DUMMY_COMMITMENT_HASH).await?;
assert_eq!(output_with_root.new_nullifiers[0].1, expected_digest);
assert_eq!(output_with_root.private_actions[0].root, expected_digest);
assert_eq!(
output_without_root.new_nullifiers[0].1,
output_without_root.private_actions[0].root,
DUMMY_COMMITMENT_HASH
);
assert_ne!(
output_with_root.new_nullifiers[0].1,
output_without_root.new_nullifiers[0].1,
output_with_root.private_actions[0].root,
output_without_root.private_actions[0].root,
);
Ok(())

View File

@ -71,10 +71,10 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
.wallet()
.get_private_account_commitment(from)
.context("Failed to get private account commitment for sender")?;
assert_eq!(tx.message.new_commitments[0], new_commitment1);
assert_eq!(tx.message.private_actions[0].commitment, new_commitment1);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments {
assert_eq!(tx.message.private_actions.len(), 2);
for commitment in tx.message.commitments() {
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
}

View File

@ -1,7 +1,8 @@
use lee_core::{
Commitment, CommitmentSetDigest, EncryptedAccountData, EncryptionScheme, EphemeralPublicKey,
InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey,
PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
PrivacyPreservingCircuitOutput, PrivateAccountKind, PrivateAction, PublicAction,
SharedSecretKey,
account::{Account, AccountId, Nonce},
compute_digest_for_path,
};
@ -10,7 +11,6 @@ use crate::execution_state::ExecutionState;
struct PrivateOutputHandler<'ctx> {
output: &'ctx mut PrivacyPreservingCircuitOutput,
output_index: &'ctx mut u32,
pre_state: &'ctx lee_core::account::AccountWithMetadata,
post_state: Account,
epk: &'ctx EphemeralPublicKey,
@ -35,10 +35,10 @@ impl PrivateOutputHandler<'_> {
"Found new private account with non default values"
);
let (new_nullifier, new_nonce) = init_nullifier_and_nonce(&account_id, commitment_root);
let (nullifier, root, nonce) = init_nullifier_and_nonce(&account_id, commitment_root);
let kind = PrivateAccountKind::Regular(self.identifier);
self.emit_private_output(&account_id, &kind, new_nullifier, new_nonce);
self.emit_private_output(&account_id, &kind, nullifier, root, nonce);
}
fn authorized_update(self, nsk: &NullifierSecretKey, membership_proof: &MembershipProof) {
@ -51,20 +51,20 @@ impl PrivateOutputHandler<'_> {
"Pre-state not authorized for authenticated private account"
);
let new_nullifier = compute_update_nullifier_and_set_digest(
let (nullifier, root) = compute_update_nullifier_and_set_digest(
membership_proof,
&self.pre_state.account,
&account_id,
nsk,
);
let new_nonce = self
let nonce = self
.pre_state
.account
.nonce
.private_account_nonce_increment(nsk);
let kind = PrivateAccountKind::Regular(self.identifier);
self.emit_private_output(&account_id, &kind, new_nullifier, new_nonce);
self.emit_private_output(&account_id, &kind, nullifier, root, nonce);
}
fn unauthorized(self, npk: &NullifierPublicKey, commitment_root: &CommitmentSetDigest) {
@ -81,10 +81,10 @@ impl PrivateOutputHandler<'_> {
"Found new private account marked as authorized."
);
let (new_nullifier, new_nonce) = init_nullifier_and_nonce(&account_id, commitment_root);
let (nullifier, root, nonce) = init_nullifier_and_nonce(&account_id, commitment_root);
let kind = PrivateAccountKind::Regular(self.identifier);
self.emit_private_output(&account_id, &kind, new_nullifier, new_nonce);
self.emit_private_output(&account_id, &kind, nullifier, root, nonce);
}
fn pda_init(
@ -109,10 +109,8 @@ impl PrivateOutputHandler<'_> {
"New private PDA must be default"
);
let (new_nullifier, new_nonce) =
init_nullifier_and_nonce(&self.pre_state.account_id, commitment_root);
let account_id = self.pre_state.account_id;
let (nullifier, root, nonce) = init_nullifier_and_nonce(&account_id, commitment_root);
let (authority_program_id, seed) = pda_seed_by_position
.get(&pos)
.expect("PrivatePdaInit position must be in pda_seed_by_position");
@ -122,7 +120,7 @@ impl PrivateOutputHandler<'_> {
identifier: self.identifier,
};
self.emit_private_output(&account_id, &kind, new_nullifier, new_nonce);
self.emit_private_output(&account_id, &kind, nullifier, root, nonce);
}
fn pda_update(
@ -144,13 +142,13 @@ impl PrivateOutputHandler<'_> {
"PrivatePdaUpdate requires authorized pre_state or external seed"
);
let new_nullifier = compute_update_nullifier_and_set_digest(
let (nullifier, root) = compute_update_nullifier_and_set_digest(
membership_proof,
&self.pre_state.account,
&self.pre_state.account_id,
nsk,
);
let new_nonce = self
let nonce = self
.pre_state
.account
.nonce
@ -166,55 +164,53 @@ impl PrivateOutputHandler<'_> {
identifier: self.identifier,
};
self.emit_private_output(&account_id, &kind, new_nullifier, new_nonce);
self.emit_private_output(&account_id, &kind, nullifier, root, nonce);
}
fn emit_private_output(
self,
account_id: &AccountId,
kind: &PrivateAccountKind,
new_nullifier: (Nullifier, CommitmentSetDigest),
new_nonce: Nonce,
nullifier: Nullifier,
root: CommitmentSetDigest,
nonce: Nonce,
) {
self.output.new_nullifiers.push(new_nullifier);
let mut post_with_updated_nonce = self.post_state;
post_with_updated_nonce.nonce = new_nonce;
post_with_updated_nonce.nonce = nonce;
let output_index = u32::try_from(self.output.private_actions.len())
.expect("Too many private accounts, output index overflow");
let commitment_post = Commitment::new(account_id, &post_with_updated_nonce);
let encrypted_account = EncryptionScheme::encrypt(
&post_with_updated_nonce,
kind,
self.ssk,
&commitment_post,
*self.output_index,
output_index,
);
self.output.new_commitments.push(commitment_post);
self.output
.encrypted_private_post_states
.push(EncryptedAccountData {
self.output.private_actions.push(PrivateAction {
nullifier,
root,
commitment: commitment_post,
encrypted_post_state: EncryptedAccountData {
ciphertext: encrypted_account,
epk: self.epk.clone(),
view_tag: self.view_tag,
});
*self.output_index = self
.output_index
.checked_add(1)
.unwrap_or_else(|| panic!("Too many private accounts, output index overflow"));
},
});
}
}
fn init_nullifier_and_nonce(
account_id: &AccountId,
commitment_root: &CommitmentSetDigest,
) -> ((Nullifier, CommitmentSetDigest), Nonce) {
let nullifier = (
) -> (Nullifier, CommitmentSetDigest, Nonce) {
(
Nullifier::for_account_initialization(account_id),
*commitment_root,
);
let nonce = Nonce::private_account_nonce_init(account_id);
(nullifier, nonce)
Nonce::private_account_nonce_init(account_id),
)
}
fn derive_and_verify_account_id(
@ -246,11 +242,8 @@ pub fn compute_circuit_output(
let (block_validity_window, timestamp_validity_window, pda_seed_by_position, states_iter) =
execution_state.into_parts();
let mut output = PrivacyPreservingCircuitOutput {
public_pre_states: Vec::new(),
public_post_states: Vec::new(),
encrypted_private_post_states: Vec::new(),
new_commitments: Vec::new(),
new_nullifiers: Vec::new(),
public_actions: Vec::new(),
private_actions: Vec::new(),
block_validity_window,
timestamp_validity_window,
};
@ -261,14 +254,15 @@ pub fn compute_circuit_output(
"Invalid account_identities length"
);
let mut output_index = 0;
for (pos, (account_identity, (pre_state, post_state))) in
account_identities.iter().zip(states_iter).enumerate()
{
match account_identity {
InputAccountIdentity::Public => {
output.public_pre_states.push(pre_state);
output.public_post_states.push(post_state);
output.public_actions.push(PublicAction {
pre: pre_state,
post: post_state,
});
}
InputAccountIdentity::PrivateAuthorizedInit {
epk,
@ -279,7 +273,6 @@ pub fn compute_circuit_output(
commitment_root,
} => PrivateOutputHandler {
output: &mut output,
output_index: &mut output_index,
pre_state: &pre_state,
post_state,
epk,
@ -297,7 +290,6 @@ pub fn compute_circuit_output(
identifier,
} => PrivateOutputHandler {
output: &mut output,
output_index: &mut output_index,
pre_state: &pre_state,
post_state,
epk,
@ -315,7 +307,6 @@ pub fn compute_circuit_output(
commitment_root,
} => PrivateOutputHandler {
output: &mut output,
output_index: &mut output_index,
pre_state: &pre_state,
post_state,
epk,
@ -334,7 +325,6 @@ pub fn compute_circuit_output(
seed: _,
} => PrivateOutputHandler {
output: &mut output,
output_index: &mut output_index,
pre_state: &pre_state,
post_state,
epk,
@ -353,7 +343,6 @@ pub fn compute_circuit_output(
seed: external_seed,
} => PrivateOutputHandler {
output: &mut output,
output_index: &mut output_index,
pre_state: &pre_state,
post_state,
epk,

View File

@ -1,3 +1,4 @@
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use crate::{
@ -131,18 +132,50 @@ impl InputAccountIdentity {
}
}
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))]
pub struct PrivateAction {
pub nullifier: Nullifier,
pub root: CommitmentSetDigest,
pub commitment: Commitment,
pub encrypted_post_state: EncryptedAccountData,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
pub struct PublicAction {
pub pre: AccountWithMetadata,
pub post: Account,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
pub struct PrivacyPreservingCircuitOutput {
pub public_pre_states: Vec<AccountWithMetadata>,
pub public_post_states: Vec<Account>,
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
pub new_commitments: Vec<Commitment>,
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
pub public_actions: Vec<PublicAction>,
pub private_actions: Vec<PrivateAction>,
pub block_validity_window: BlockValidityWindow,
pub timestamp_validity_window: TimestampValidityWindow,
}
#[cfg(any(feature = "host", test))]
impl PrivacyPreservingCircuitOutput {
#[must_use]
pub fn commitments(&self) -> Vec<Commitment> {
self.private_actions
.iter()
.map(|action| action.commitment.clone())
.collect()
}
#[must_use]
pub fn nullifiers(&self) -> Vec<(Nullifier, CommitmentSetDigest)> {
self.private_actions
.iter()
.map(|action| (action.nullifier, action.root))
.collect()
}
}
#[cfg(feature = "host")]
impl PrivacyPreservingCircuitOutput {
/// Serializes the circuit output to a byte vector.
@ -167,50 +200,57 @@ mod tests {
#[test]
fn privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() {
let output = PrivacyPreservingCircuitOutput {
public_pre_states: vec![
AccountWithMetadata::new(
Account {
public_actions: vec![
PublicAction {
pre: AccountWithMetadata::new(
Account {
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
balance: 12_345_678_901_234_567_890,
data: b"test data".to_vec().try_into().unwrap(),
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFE),
},
true,
AccountId::new([0; 32]),
),
post: Account {
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
balance: 12_345_678_901_234_567_890,
data: b"test data".to_vec().try_into().unwrap(),
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFE),
balance: 100,
data: b"post state data".to_vec().try_into().unwrap(),
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFF),
},
true,
AccountId::new([0; 32]),
),
AccountWithMetadata::new(
Account {
program_owner: [9, 9, 9, 8, 8, 8, 7, 7],
balance: 123_123_123_456_456_567_112,
data: b"test data".to_vec().try_into().unwrap(),
nonce: Nonce(9_999_999_999_999_999_999_999),
},
PublicAction {
pre: AccountWithMetadata::new(
Account {
program_owner: [9, 9, 9, 8, 8, 8, 7, 7],
balance: 123_123_123_456_456_567_112,
data: b"test data".to_vec().try_into().unwrap(),
nonce: Nonce(9_999_999_999_999_999_999_999),
},
false,
AccountId::new([1; 32]),
),
post: Account {
program_owner: [2, 3, 4, 5, 6, 7, 8, 9],
balance: 200,
data: b"post state data 2".to_vec().try_into().unwrap(),
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFD),
},
false,
AccountId::new([1; 32]),
),
},
],
public_post_states: vec![Account {
program_owner: [1, 2, 3, 4, 5, 6, 7, 8],
balance: 100,
data: b"post state data".to_vec().try_into().unwrap(),
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFF),
}],
encrypted_private_post_states: vec![EncryptedAccountData {
ciphertext: Ciphertext(vec![255, 255, 1, 1, 2, 2]),
epk: EphemeralPublicKey(vec![9, 9, 9]),
view_tag: 42,
}],
new_commitments: vec![Commitment::new(
&AccountId::new([1; 32]),
&Account::default(),
)],
new_nullifiers: vec![(
Nullifier::for_account_update(
private_actions: vec![PrivateAction {
nullifier: Nullifier::for_account_update(
&Commitment::new(&AccountId::new([2; 32]), &Account::default()),
&[1; 32],
),
[0xab; 32],
)],
root: [0xab; 32],
commitment: Commitment::new(&AccountId::new([1; 32]), &Account::default()),
encrypted_post_state: EncryptedAccountData {
ciphertext: Ciphertext(vec![255, 255, 1, 1, 2, 2]),
epk: EphemeralPublicKey(vec![9, 9, 9]),
view_tag: 42,
},
}],
block_validity_window: (1..).into(),
timestamp_validity_window: TimestampValidityWindow::new_unbounded(),
};

View File

@ -5,6 +5,7 @@
pub use circuit_io::{
InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
PrivateAction, PublicAction,
};
pub use commitment::{
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,

View File

@ -24,9 +24,9 @@ fn decrypt_kind(
idx: usize,
) -> PrivateAccountKind {
let (kind, _) = EncryptionScheme::decrypt(
&output.encrypted_private_post_states[idx].ciphertext,
&output.private_actions[idx].encrypted_post_state.ciphertext,
ssk,
&output.new_commitments[idx],
&output.private_actions[idx].commitment,
u32::try_from(idx).expect("idx fits in u32"),
)
.unwrap();
@ -105,18 +105,16 @@ fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts()
assert!(proof.is_valid_for(&output));
let [sender_pre] = output.public_pre_states.try_into().unwrap();
let [sender_post] = output.public_post_states.try_into().unwrap();
let [action] = output.public_actions.try_into().unwrap();
let (sender_pre, sender_post) = (action.pre, action.post);
assert_eq!(sender_pre, expected_sender_pre);
assert_eq!(sender_post, expected_sender_post);
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.new_nullifiers.len(), 1);
assert_eq!(output.encrypted_private_post_states.len(), 1);
assert_eq!(output.private_actions.len(), 1);
let (_identifier, recipient_post) = EncryptionScheme::decrypt(
&output.encrypted_private_post_states[0].ciphertext,
&output.private_actions[0].encrypted_post_state.ciphertext,
&shared_secret,
&output.new_commitments[0],
&output.private_actions[0].commitment,
0,
)
.unwrap();
@ -219,14 +217,13 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
.unwrap();
assert!(proof.is_valid_for(&output));
assert!(output.public_pre_states.is_empty());
assert!(output.public_post_states.is_empty());
assert_eq!(output.new_commitments, expected_new_commitments);
assert_eq!(output.new_nullifiers, expected_new_nullifiers);
assert_eq!(output.encrypted_private_post_states.len(), 2);
assert!(output.public_actions.is_empty());
assert_eq!(output.commitments(), expected_new_commitments);
assert_eq!(output.nullifiers(), expected_new_nullifiers);
assert_eq!(output.private_actions.len(), 2);
let (_identifier, sender_post) = EncryptionScheme::decrypt(
&output.encrypted_private_post_states[0].ciphertext,
&output.private_actions[0].encrypted_post_state.ciphertext,
&shared_secret_1,
&expected_new_commitments[0],
0,
@ -235,7 +232,7 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
assert_eq!(sender_post, expected_private_account_1);
let (_identifier, recipient_post) = EncryptionScheme::decrypt(
&output.encrypted_private_post_states[1].ciphertext,
&output.private_actions[1].encrypted_post_state.ciphertext,
&shared_secret_2,
&expected_new_commitments[1],
1,
@ -373,7 +370,7 @@ fn private_pda_init() {
);
let (output, _proof) = result.expect("PDA init should succeed");
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.private_actions.len(), 1);
}
/// PDA withdraw: chains to `simple_balance_transfer` to move balance from PDA to recipient.
@ -431,7 +428,7 @@ fn private_pda_withdraw() {
);
let (output, _proof) = result.expect("PDA withdraw should succeed");
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.private_actions.len(), 1);
}
/// Shared regular private account: receives funds via `authenticated_transfer` directly,
@ -486,7 +483,7 @@ fn shared_account_receives_via_simple_transfer() {
let (output, _proof) = result.expect("shared account receive should succeed");
// Sender is public (no commitment), recipient is private (1 commitment)
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.private_actions.len(), 1);
}
/// `PrivateAuthorizedInit` with a non-default identifier produces a ciphertext that decrypts

View File

@ -1,24 +1,27 @@
use borsh::{BorshDeserialize, BorshSerialize};
use lee_core::{
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput,
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput, PrivateAction,
account::{Account, Nonce},
program::{BlockValidityWindow, TimestampValidityWindow},
};
pub use lee_core::{EncryptedAccountData, ViewTag};
use sha2::{Digest as _, Sha256};
use crate::{AccountId, error::LeeError};
use crate::AccountId;
const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Privacy/\x00\x00\x00\x00\x00\x00";
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct PublicActionWithOwner {
pub account_id: AccountId,
pub post_state: Account,
}
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Message {
pub public_account_ids: Vec<AccountId>,
pub public_actions: Vec<PublicActionWithOwner>,
pub nonces: Vec<Nonce>,
pub public_post_states: Vec<Account>,
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
pub new_commitments: Vec<Commitment>,
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
pub private_actions: Vec<PrivateAction>,
pub block_validity_window: BlockValidityWindow,
pub timestamp_validity_window: TimestampValidityWindow,
}
@ -31,21 +34,22 @@ impl std::fmt::Debug for Message {
write!(f, "{}", hex::encode(self.0))
}
}
let nullifiers: Vec<_> = self
.new_nullifiers
let private_actions: Vec<_> = self
.private_actions
.iter()
.map(|(n, d)| (n, HexDigest(d)))
.map(|a| {
(
&a.nullifier,
HexDigest(&a.root),
&a.commitment,
&a.encrypted_post_state,
)
})
.collect();
f.debug_struct("Message")
.field("public_account_ids", &self.public_account_ids)
.field("public_actions", &self.public_actions)
.field("nonces", &self.nonces)
.field("public_post_states", &self.public_post_states)
.field(
"encrypted_private_post_states",
&self.encrypted_private_post_states,
)
.field("new_commitments", &self.new_commitments)
.field("new_nullifiers", &nullifiers)
.field("private_actions", &private_actions)
.field("block_validity_window", &self.block_validity_window)
.field("timestamp_validity_window", &self.timestamp_validity_window)
.finish()
@ -53,21 +57,55 @@ impl std::fmt::Debug for Message {
}
impl Message {
pub fn try_from_circuit_output(
public_account_ids: Vec<AccountId>,
nonces: Vec<Nonce>,
output: PrivacyPreservingCircuitOutput,
) -> Result<Self, LeeError> {
Ok(Self {
public_account_ids,
#[must_use]
pub fn from_circuit_output(nonces: Vec<Nonce>, output: PrivacyPreservingCircuitOutput) -> Self {
let public_actions = output
.public_actions
.into_iter()
.map(|action| PublicActionWithOwner {
account_id: action.pre.account_id,
post_state: action.post,
})
.collect();
Self {
public_actions,
nonces,
public_post_states: output.public_post_states,
encrypted_private_post_states: output.encrypted_private_post_states,
new_commitments: output.new_commitments,
new_nullifiers: output.new_nullifiers,
private_actions: output.private_actions,
block_validity_window: output.block_validity_window,
timestamp_validity_window: output.timestamp_validity_window,
})
}
}
#[must_use]
pub fn commitments(&self) -> Vec<Commitment> {
self.private_actions
.iter()
.map(|action| action.commitment.clone())
.collect()
}
#[must_use]
pub fn nullifiers(&self) -> Vec<(Nullifier, CommitmentSetDigest)> {
self.private_actions
.iter()
.map(|action| (action.nullifier, action.root))
.collect()
}
#[must_use]
pub fn public_account_ids(&self) -> Vec<AccountId> {
self.public_actions
.iter()
.map(|action| action.account_id)
.collect()
}
#[must_use]
pub fn public_post_states(&self) -> Vec<Account> {
self.public_actions
.iter()
.map(|action| action.post_state.clone())
.collect()
}
#[must_use]
@ -89,15 +127,15 @@ impl Message {
#[cfg(test)]
pub mod tests {
use lee_core::{
Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, PrivateAccountKind,
SharedSecretKey,
Commitment, EncryptionScheme, EphemeralPublicKey, Nullifier, NullifierPublicKey,
PrivateAccountKind, PrivateAction, SharedSecretKey,
account::{Account, AccountId, Nonce},
encryption::ViewingPublicKey,
encryption::{Ciphertext, ViewingPublicKey},
program::{BlockValidityWindow, TimestampValidityWindow},
};
use sha2::{Digest as _, Sha256};
use super::{EncryptedAccountData, Message, PREFIX};
use super::{EncryptedAccountData, Message, PREFIX, PublicActionWithOwner};
#[must_use]
pub fn message_for_tests() -> Message {
@ -110,31 +148,31 @@ pub mod tests {
let npk1 = NullifierPublicKey::from(&nsk1);
let npk2 = NullifierPublicKey::from(&nsk2);
let public_account_ids = vec![AccountId::new([1; 32])];
let nonces = vec![1_u128.into(), 2_u128.into(), 3_u128.into()];
let public_post_states = vec![Account::default()];
let encrypted_private_post_states = Vec::new();
let account_id2 = lee_core::account::AccountId::for_regular_private_account(&npk2, 0);
let new_commitments = vec![Commitment::new(&account_id2, &account2)];
let commitment = Commitment::new(&account_id2, &account2);
let account_id1 = lee_core::account::AccountId::for_regular_private_account(&npk1, 0);
let old_commitment = Commitment::new(&account_id1, &account1);
let new_nullifiers = vec![(
Nullifier::for_account_update(&old_commitment, &nsk1),
[0; 32],
)];
let nullifier = Nullifier::for_account_update(&old_commitment, &nsk1);
Message {
public_account_ids,
public_actions: vec![PublicActionWithOwner {
account_id: AccountId::new([1; 32]),
post_state: Account::default(),
}],
nonces,
public_post_states,
encrypted_private_post_states,
new_commitments,
new_nullifiers,
private_actions: vec![PrivateAction {
nullifier,
root: [0; 32],
commitment,
encrypted_post_state: EncryptedAccountData {
ciphertext: Ciphertext::from_inner(vec![]),
epk: EphemeralPublicKey(vec![]),
view_tag: 0,
},
}],
block_validity_window: BlockValidityWindow::new_unbounded(),
timestamp_validity_window: TimestampValidityWindow::new_unbounded(),
}
@ -143,31 +181,24 @@ pub mod tests {
#[test]
fn hash_privacy_pinned() {
let msg = Message {
public_account_ids: vec![AccountId::new([42_u8; 32])],
public_actions: vec![],
nonces: vec![Nonce(5)],
public_post_states: vec![],
encrypted_private_post_states: vec![],
new_commitments: vec![],
new_nullifiers: vec![],
private_actions: vec![],
block_validity_window: BlockValidityWindow::new_unbounded(),
timestamp_validity_window: TimestampValidityWindow::new_unbounded(),
};
let public_account_ids_bytes: &[u8] = &[42_u8; 32];
// empty vec fields: u32 len=0
let public_actions_bytes: &[u8] = &[0, 0, 0, 0];
let nonces_bytes: &[u8] = &[1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// all remaining vec fields are empty: u32 len=0
let empty_vec_bytes: &[u8] = &[0_u8; 4];
let private_actions_bytes: &[u8] = &[0, 0, 0, 0];
// validity windows: unbounded = {from: None (0_u8), to: None (0_u8)}
let unbounded_window_bytes: &[u8] = &[0_u8; 2];
let unbounded_window_bytes: &[u8] = &[0, 0];
let expected_borsh_vec: Vec<u8> = [
&[1_u8, 0, 0, 0], // public_account_ids
public_account_ids_bytes,
public_actions_bytes,
nonces_bytes,
empty_vec_bytes, // public_post_state
empty_vec_bytes, // encrypted_private_post_states
empty_vec_bytes, // new_commitments
empty_vec_bytes, // new_nullifiers
private_actions_bytes,
unbounded_window_bytes, // block_validity_window
unbounded_window_bytes, // timestamp_validity_window
]

View File

@ -53,7 +53,7 @@ impl PrivacyPreservingTransaction {
.signer_account_ids()
.into_iter()
.collect::<HashSet<_>>();
acc_set.extend(&self.message.public_account_ids);
acc_set.extend(self.message.public_actions.iter().map(|action| action.account_id));
acc_set.into_iter().collect()
}

View File

@ -481,11 +481,8 @@ fn private_pda_claim_succeeds() {
);
let (output, _proof) = result.expect("private PDA claim should succeed");
assert_eq!(output.new_nullifiers.len(), 1);
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.encrypted_private_post_states.len(), 1);
assert!(output.public_pre_states.is_empty());
assert!(output.public_post_states.is_empty());
assert_eq!(output.private_actions.len(), 1);
assert!(output.public_actions.is_empty());
}
/// An npk is supplied that does not match the `pre_state`'s `account_id` under
@ -563,8 +560,7 @@ fn caller_pda_seeds_authorize_private_pda_for_callee() {
let (output, _proof) =
result.expect("caller-seeds authorization of private PDA should succeed");
assert_eq!(output.new_commitments.len(), 1);
assert_eq!(output.new_nullifiers.len(), 1);
assert_eq!(output.private_actions.len(), 1);
}
/// The delegator chains with a different seed than the one it claimed with. In the callee
@ -843,7 +839,7 @@ fn private_authorized_uninitialized_account() {
.unwrap();
// Create message from circuit output
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
@ -890,7 +886,7 @@ fn private_unauthorized_uninitialized_account_can_still_be_claimed() {
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
@ -942,7 +938,7 @@ fn private_account_claimed_then_used_without_init_flag_should_fail() {
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
@ -1062,7 +1058,7 @@ fn two_private_pda_family_members_receive_and_spend() {
)
.unwrap();
let message =
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output).unwrap();
Message::from_circuit_output(vec![funder_nonce], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]);
state
.transition_from_privacy_preserving_transaction(
@ -1099,7 +1095,7 @@ fn two_private_pda_family_members_receive_and_spend() {
)
.unwrap();
let message =
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output).unwrap();
Message::from_circuit_output(vec![funder_nonce], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]);
state
.transition_from_privacy_preserving_transaction(
@ -1143,7 +1139,7 @@ fn two_private_pda_family_members_receive_and_spend() {
)
.unwrap();
let message =
Message::try_from_circuit_output(vec![recipient_id], vec![Nonce(0)], output).unwrap();
Message::from_circuit_output(vec![Nonce(0)], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]);
state
.transition_from_privacy_preserving_transaction(
@ -1180,7 +1176,7 @@ fn two_private_pda_family_members_receive_and_spend() {
&spend_with_deps,
)
.unwrap();
let message = Message::try_from_circuit_output(vec![recipient_id], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
state
.transition_from_privacy_preserving_transaction(
@ -1232,9 +1228,7 @@ fn two_private_pda_family_members_receive_and_spend() {
&crate::test_methods::simple_balance_transfer().into(),
)
.unwrap();
let message =
Message::try_from_circuit_output(vec![recipient_id], vec![recipient_nonce], output)
.unwrap();
let message = Message::from_circuit_output(vec![recipient_nonce], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]);
state
.transition_from_privacy_preserving_transaction(

View File

@ -342,9 +342,7 @@ fn authorized_public_account_claiming_succeeds_when_executed_privately() {
)
.unwrap();
let message =
Message::try_from_circuit_output(vec![recipient_account_id], vec![Nonce(0)], output)
.unwrap();
let message = Message::from_circuit_output(vec![Nonce(0)], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_private_key]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
@ -475,7 +473,7 @@ fn private_chained_call(number_of_calls: u32) {
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let transaction = PrivacyPreservingTransaction::new(message, witness_set);

View File

@ -293,12 +293,7 @@ fn shielded_balance_transfer_for_tests(
)
.unwrap();
let message = Message::try_from_circuit_output(
vec![sender_keys.account_id()],
vec![sender_nonce],
output,
)
.unwrap();
let message = Message::from_circuit_output(vec![sender_nonce], output);
let witness_set = WitnessSet::for_message(&message, proof, &[&sender_keys.signing_key]);
PrivacyPreservingTransaction::new(message, witness_set)
@ -361,7 +356,7 @@ fn private_balance_transfer_for_tests(
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
@ -416,7 +411,7 @@ fn deshielded_balance_transfer_for_tests(
.unwrap();
let message =
Message::try_from_circuit_output(vec![*recipient_account_id], vec![], output).unwrap();
Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);

View File

@ -26,7 +26,7 @@ fn transition_from_privacy_preserving_transaction_shielded() {
this
};
let [expected_new_commitment] = tx.message().new_commitments.clone().try_into().unwrap();
let [expected_new_commitment] = tx.message().commitments().try_into().unwrap();
assert!(!state.private_state.0.contains(&expected_new_commitment));
state
@ -126,7 +126,7 @@ fn privacy_tampered_epk_is_rejected() {
);
// Flip a byte of the first note's epk
tx.message.encrypted_private_post_states[0].epk.0[0] ^= 0xFF;
tx.message.private_actions[0].encrypted_post_state.epk.0[0] ^= 0xFF;
assert!(
matches!(
@ -152,7 +152,7 @@ fn privacy_tampered_view_tag_is_rejected() {
);
// Flip the first note's view_tag
tx.message.encrypted_private_post_states[0].view_tag ^= 0xFF;
tx.message.private_actions[0].encrypted_post_state.view_tag ^= 0xFF;
assert!(
matches!(

View File

@ -152,7 +152,7 @@ fn validity_window_works_in_privacy_preserving_transactions(
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
PrivacyPreservingTransaction::new(message, witness_set)
@ -220,7 +220,7 @@ fn timestamp_validity_window_works_in_privacy_preserving_transactions(
)
.unwrap();
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
let message = Message::from_circuit_output(vec![], output);
let witness_set = WitnessSet::for_message(&message, proof, &[]);
PrivacyPreservingTransaction::new(message, witness_set)

View File

@ -4,7 +4,7 @@ use std::{
};
use lee_core::{
BlockId, Commitment, Nullifier, PrivacyPreservingCircuitOutput, Timestamp,
BlockId, Commitment, Nullifier, PrivacyPreservingCircuitOutput, PublicAction, Timestamp,
account::{Account, AccountId, AccountWithMetadata},
program::{
ChainedCall, Claim, DEFAULT_PROGRAM_ID, ProgramId, compute_public_authorized_pdas,
@ -310,10 +310,13 @@ impl ValidatedStateDiff {
) -> Result<Self, LeeError> {
let message = &tx.message;
let witness_set = &tx.witness_set;
let commitments = message.commitments();
let nullifiers = message.nullifiers();
let public_account_ids = message.public_account_ids();
// 1. Commitments or nullifiers are non empty
ensure!(
!message.new_commitments.is_empty() || !message.new_nullifiers.is_empty(),
!message.private_actions.is_empty(),
LeeError::InvalidInput(
"Empty commitments and empty nullifiers found in message".into(),
)
@ -321,25 +324,19 @@ impl ValidatedStateDiff {
// 2. Check there are no duplicate account_ids in the public_account_ids list.
ensure!(
n_unique(&message.public_account_ids) == message.public_account_ids.len(),
n_unique(&public_account_ids) == public_account_ids.len(),
LeeError::InvalidInput("Duplicate account_ids found in message".into())
);
// Check there are no duplicate nullifiers in the new_nullifiers list
ensure!(
n_unique(
&message
.new_nullifiers
.iter()
.map(|(n, _)| n)
.collect::<Vec<_>>()
) == message.new_nullifiers.len(),
n_unique(&nullifiers.iter().map(|(n, _)| n).collect::<Vec<_>>()) == nullifiers.len(),
LeeError::InvalidInput("Duplicate nullifiers found in message".into())
);
// Check there are no duplicate commitments in the new_commitments list
ensure!(
n_unique(&message.new_commitments) == message.new_commitments.len(),
n_unique(&commitments) == commitments.len(),
LeeError::InvalidInput("Duplicate commitments found in message".into())
);
@ -376,8 +373,7 @@ impl ValidatedStateDiff {
);
// Build pre_states for proof verification
let public_pre_states: Vec<_> = message
.public_account_ids
let public_pre_states: Vec<_> = public_account_ids
.iter()
.map(|account_id| {
AccountWithMetadata::new(
@ -396,28 +392,22 @@ impl ValidatedStateDiff {
)?;
// 5. Commitment freshness
state.check_commitments_are_new(&message.new_commitments)?;
state.check_commitments_are_new(&commitments)?;
// 6. Nullifier uniqueness
state.check_nullifiers_are_valid(&message.new_nullifiers)?;
state.check_nullifiers_are_valid(&nullifiers)?;
let public_diff = message
.public_account_ids
let public_diff = public_account_ids
.iter()
.copied()
.zip(message.public_post_states.clone())
.collect();
let new_nullifiers = message
.new_nullifiers
.iter()
.copied()
.map(|(nullifier, _)| nullifier)
.zip(message.public_post_states())
.collect();
let new_nullifiers = nullifiers.iter().map(|(nullifier, _)| *nullifier).collect();
Ok(Self(StateDiff {
signer_account_ids,
public_diff,
new_commitments: message.new_commitments.clone(),
new_commitments: commitments,
new_nullifiers,
program: None,
}))
@ -498,11 +488,13 @@ fn check_privacy_preserving_circuit_proof_is_valid(
message: &Message,
) -> Result<(), LeeError> {
let output = PrivacyPreservingCircuitOutput {
public_pre_states: public_pre_states.to_vec(),
public_post_states: message.public_post_states.clone(),
encrypted_private_post_states: message.encrypted_private_post_states.clone(),
new_commitments: message.new_commitments.clone(),
new_nullifiers: message.new_nullifiers.clone(),
public_actions: public_pre_states
.iter()
.cloned()
.zip(message.public_post_states())
.map(|(pre, post)| PublicAction { pre, post })
.collect(),
private_actions: message.private_actions.clone(),
block_validity_window: message.block_validity_window,
timestamp_validity_window: message.timestamp_validity_window,
};

View File

@ -191,12 +191,10 @@ fn privacy_malicious_programs_cannot_drain_public_victim() {
// public_account_ids lists the Public entries from account_identities, in order.
// The single ciphertext belongs to attacker's private account update.
let message = Message::try_from_circuit_output(
vec![victim_id, recipient_id],
let message = Message::from_circuit_output(
vec![], // no public signers, no nonces
circuit_output,
)
.unwrap();
);
let witness_set = WitnessSet::for_message(&message, proof, &[]); // no signatures
let tx = PrivacyPreservingTransaction::new(message, witness_set);
@ -355,12 +353,10 @@ fn privacy_malicious_programs_cannot_drain_private_victim() {
// public_account_ids lists the Public entries from account_identities, in order.
// The single ciphertext belongs to attacker's private account update.
let message = Message::try_from_circuit_output(
vec![victim_id, recipient_id],
let message = Message::from_circuit_output(
vec![], // no public signers, no nonces
circuit_output,
)
.unwrap();
);
let witness_set = WitnessSet::for_message(&message, proof, &[]); // no signatures
let tx = PrivacyPreservingTransaction::new(message, witness_set);
@ -485,8 +481,9 @@ fn malicious_programs_cannot_drain_victim_without_signature() {
#[test]
fn privacy_garbage_proof_is_rejected() {
use lee_core::{
Commitment,
Commitment, EncryptedAccountData, Nullifier, PrivateAction,
account::Account,
encryption::{Ciphertext, EphemeralPublicKey},
program::{BlockValidityWindow, TimestampValidityWindow},
};
@ -508,12 +505,18 @@ fn privacy_garbage_proof_is_rejected() {
));
let commitment = Commitment::new(&account_id, &Account::default());
let message = Message {
public_account_ids: vec![],
public_actions: vec![],
nonces: vec![],
public_post_states: vec![],
encrypted_private_post_states: vec![],
new_commitments: vec![commitment],
new_nullifiers: vec![],
private_actions: vec![PrivateAction {
nullifier: Nullifier::for_account_initialization(&account_id),
root: [0; 32],
commitment,
encrypted_post_state: EncryptedAccountData {
ciphertext: Ciphertext::from_inner(vec![]),
epk: EphemeralPublicKey(vec![]),
view_tag: 0,
},
}],
block_validity_window: BlockValidityWindow::new_unbounded(),
timestamp_validity_window: TimestampValidityWindow::new_unbounded(),
};

View File

@ -281,27 +281,24 @@ impl From<PublicMessage> for lee::public_transaction::Message {
impl From<lee::privacy_preserving_transaction::message::Message> for PrivacyPreservingMessage {
fn from(value: lee::privacy_preserving_transaction::message::Message) -> Self {
let lee::privacy_preserving_transaction::message::Message {
public_account_ids,
public_actions,
nonces,
public_post_states,
encrypted_private_post_states,
new_commitments,
new_nullifiers,
private_actions,
block_validity_window,
timestamp_validity_window,
} = value;
Self {
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
public_account_ids: public_actions.iter().map(|a| a.account_id.into()).collect(),
nonces: nonces.iter().map(|x| x.0).collect(),
public_post_states: public_post_states.into_iter().map(Into::into).collect(),
encrypted_private_post_states: encrypted_private_post_states
.into_iter()
.map(Into::into)
public_post_states: public_actions.into_iter().map(|a| a.post_state.into()).collect(),
encrypted_private_post_states: private_actions
.iter()
.map(|a| a.encrypted_post_state.clone().into())
.collect(),
new_commitments: new_commitments.into_iter().map(Into::into).collect(),
new_nullifiers: new_nullifiers
new_commitments: private_actions.iter().map(|a| a.commitment.clone().into()).collect(),
new_nullifiers: private_actions
.into_iter()
.map(|(n, d)| (n.into(), d.into()))
.map(|a| (a.nullifier.into(), a.root.into()))
.collect(),
block_validity_window: block_validity_window.into(),
timestamp_validity_window: timestamp_validity_window.into(),
@ -323,26 +320,50 @@ impl TryFrom<PrivacyPreservingMessage> for lee::privacy_preserving_transaction::
block_validity_window,
timestamp_validity_window,
} = value;
let public_post_states = public_post_states
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<lee_core::account::Account>, _>>()
.map_err(|e| lee::error::LeeError::InvalidInput(format!("{e}")))?;
let public_actions = public_account_ids
.into_iter()
.map(Into::into)
.zip(public_post_states)
.map(|(account_id, post_state)| {
lee::privacy_preserving_transaction::message::PublicActionWithOwner {
account_id,
post_state,
}
})
.collect();
let private_actions = new_commitments
.into_iter()
.map(Into::into)
.zip(
new_nullifiers
.into_iter()
.map(|(n, d)| (n.into(), d.into())),
)
.zip(encrypted_private_post_states.into_iter().map(Into::into))
.map(|((commitment, (nullifier, root)), encrypted_post_state)| {
lee_core::PrivateAction {
nullifier,
root,
commitment,
encrypted_post_state,
}
})
.collect();
Ok(Self {
public_account_ids: public_account_ids.into_iter().map(Into::into).collect(),
public_actions,
nonces: nonces
.iter()
.map(|x| lee_core::account::Nonce(*x))
.collect(),
public_post_states: public_post_states
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| lee::error::LeeError::InvalidInput(format!("{e}")))?,
encrypted_private_post_states: encrypted_private_post_states
.into_iter()
.map(Into::into)
.collect(),
new_commitments: new_commitments.into_iter().map(Into::into).collect(),
new_nullifiers: new_nullifiers
.into_iter()
.map(|(n, d)| (n.into(), d.into()))
.collect(),
private_actions,
block_validity_window: block_validity_window
.try_into()
.map_err(|e| lee::error::LeeError::InvalidInput(format!("{e}")))?,

View File

@ -860,8 +860,7 @@ fn private_bridge_withdraw_invocation_is_dropped() {
)
.expect("Execution should succeed");
let message = Message::try_from_circuit_output(vec![bridge_account_id], vec![], output)
.expect("Message construction should succeed");
let message = Message::from_circuit_output(vec![], output);
let witness_set =
lee::privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]);
let tx =

View File

@ -525,13 +525,12 @@ impl WalletCore {
for (output_index, acc_decode_data) in acc_decode_mask.iter().enumerate() {
match acc_decode_data {
AccDecodeData::Decode(secret, acc_account_id) => {
let acc_ead = tx.message.encrypted_private_post_states[output_index].clone();
let acc_comm = tx.message.new_commitments[output_index].clone();
let action = &tx.message.private_actions[output_index];
let (kind, res_acc) = lee_core::EncryptionScheme::decrypt(
&acc_ead.ciphertext,
&action.encrypted_post_state.ciphertext,
secret,
&acc_comm,
&action.commitment,
output_index
.try_into()
.expect("Output index is expected to fit in u32"),
@ -617,12 +616,10 @@ impl WalletCore {
&program.to_owned(),
)?;
let message =
lee::privacy_preserving_transaction::message::Message::try_from_circuit_output(
acc_manager.public_account_ids(),
acc_manager.public_account_nonces(),
output,
)?;
let message = lee::privacy_preserving_transaction::message::Message::from_circuit_output(
acc_manager.public_account_nonces(),
output,
);
let message_hash = message.hash();
let signatures_public_keys = acc_manager
@ -773,18 +770,16 @@ impl WalletCore {
&key_chain.nullifier_public_key,
&key_chain.viewing_public_key,
);
let new_commitments = &tx.message.new_commitments;
tx.message()
.encrypted_private_post_states
.private_actions
.iter()
.enumerate()
.filter(move |(_, encrypted_data)| encrypted_data.view_tag == view_tag)
.filter_map(move |(ciph_id, encrypted_data)| {
let ciphertext = &encrypted_data.ciphertext;
let commitment = &new_commitments[ciph_id];
let shared_secret =
key_chain.calculate_shared_secret_receiver(&encrypted_data.epk)?;
.filter(move |(_, action)| action.encrypted_post_state.view_tag == view_tag)
.filter_map(move |(ciph_id, action)| {
let ciphertext = &action.encrypted_post_state.ciphertext;
let commitment = &action.commitment;
let shared_secret = key_chain
.calculate_shared_secret_receiver(&action.encrypted_post_state.epk)?;
lee_core::EncryptionScheme::decrypt(
ciphertext,
@ -856,25 +851,22 @@ impl WalletCore {
for (account_id, npk, vpk, vsk) in shared_keys {
let view_tag = EncryptedAccountData::compute_view_tag(&npk, &vpk);
for (ciph_id, encrypted_data) in tx
.message()
.encrypted_private_post_states
.iter()
.enumerate()
{
if encrypted_data.view_tag != view_tag {
for (ciph_id, action) in tx.message().private_actions.iter().enumerate() {
if action.encrypted_post_state.view_tag != view_tag {
continue;
}
let Some(shared_secret) =
SharedSecretKey::decapsulate(&encrypted_data.epk, &vsk.d, &vsk.z)
else {
let Some(shared_secret) = SharedSecretKey::decapsulate(
&action.encrypted_post_state.epk,
&vsk.d,
&vsk.z,
) else {
continue;
};
let commitment = &tx.message.new_commitments[ciph_id];
let commitment = &action.commitment;
if let Some((_kind, new_acc)) = lee_core::EncryptionScheme::decrypt(
&encrypted_data.ciphertext,
&action.encrypted_post_state.ciphertext,
&shared_secret,
commitment,
ciph_id