mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-09 15:29:34 +00:00
refactor!: bundle private/public i/o into action struccts
This commit is contained in:
parent
571f35b384
commit
f3d58b4da3
@ -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(())
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
pub use circuit_io::{
|
||||
InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
PrivateAction, PublicAction,
|
||||
};
|
||||
pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
]
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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, &[]);
|
||||
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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(),
|
||||
};
|
||||
|
||||
@ -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}")))?,
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user