mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-07-17 13:20:17 +00:00
Merge 9a2c9a2e1b5a59629a25488cf7564217cdf2d991 into 2e669e76f83fb27e9d0ebbb62b0d9a8799aea1fd
This commit is contained in:
commit
e6a7ceaee1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -81,9 +81,9 @@ 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!(tx.message.new_commitments.contains(&new_commitment1));
|
||||
assert!(tx.message.commitments().contains(&new_commitment1));
|
||||
|
||||
for commitment in tx.message.new_commitments {
|
||||
for commitment in tx.message.commitments() {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -169,9 +169,9 @@ 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!(tx.message.new_commitments.contains(&sender_commitment));
|
||||
assert!(tx.message.commitments().contains(&sender_commitment));
|
||||
|
||||
for commitment in tx.message.new_commitments {
|
||||
for commitment in tx.message.commitments() {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -245,7 +245,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
|
||||
|
||||
let acc_1_balance = account_balance(&ctx, from).await?;
|
||||
|
||||
for commitment in tx.message.new_commitments {
|
||||
for commitment in tx.message.commitments() {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ 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
|
||||
for commitment in tx.message.new_commitments {
|
||||
for commitment in tx.message.commitments() {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
@ -657,8 +657,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)
|
||||
@ -678,14 +679,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(())
|
||||
|
||||
@ -201,12 +201,10 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
.context("Failed to execute/prove bridge deposit")?;
|
||||
|
||||
// Create privacy-preserving transaction from circuit output
|
||||
let message = privacy_preserving_transaction::Message::try_from_circuit_output(
|
||||
vec![bridge_account_id, recipient_vault_id],
|
||||
let message = privacy_preserving_transaction::Message::from_circuit_output(
|
||||
vec![bridge_pre.account.nonce, vault_pre.account.nonce],
|
||||
output,
|
||||
)
|
||||
.context("Failed to build privacy-preserving bridge deposit message")?;
|
||||
);
|
||||
|
||||
let witness_set = privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]);
|
||||
let attack_tx = LeeTransaction::PrivacyPreserving(lee::PrivacyPreservingTransaction::new(
|
||||
|
||||
@ -71,9 +71,9 @@ 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!(tx.message.new_commitments.contains(&new_commitment1));
|
||||
assert!(tx.message.commitments().contains(&new_commitment1));
|
||||
|
||||
for commitment in tx.message.new_commitments {
|
||||
for commitment in tx.message.commitments() {
|
||||
assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await);
|
||||
}
|
||||
|
||||
|
||||
@ -83,9 +83,7 @@ async fn fund_private_pda(
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("circuit proving failed: {e}"))?;
|
||||
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![sender], vec![sender_account.nonce], output)
|
||||
.map_err(|e| anyhow::anyhow!("message build failed: {e}"))?;
|
||||
let message = Message::from_circuit_output(vec![sender_account.nonce], output);
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[sender_sk]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
@ -26,9 +26,7 @@ async fn private_transaction_pads_notes_to_max() -> Result<()> {
|
||||
|
||||
let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await;
|
||||
|
||||
assert_eq!(tx.message.new_commitments.len(), 7);
|
||||
assert_eq!(tx.message.new_nullifiers.len(), 7);
|
||||
assert_eq!(tx.message.encrypted_private_post_states.len(), 7);
|
||||
assert_eq!(tx.message.private_actions.len(), 7);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -310,7 +310,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
&program.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = pptx::message::Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
let message = pptx::message::Message::from_circuit_output(vec![], output);
|
||||
let witness_set = pptx::witness_set::WitnessSet::for_message(&message, proof, &[]);
|
||||
pptx::PrivacyPreservingTransaction::new(message, witness_set)
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, DummyInput, EncryptedAccountData, EncryptionScheme,
|
||||
EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
|
||||
NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, PrivateAction,
|
||||
PublicAction, SharedSecretKey,
|
||||
account::{Account, AccountId, Nonce},
|
||||
compute_digest_for_path,
|
||||
encryption::{ViewTag, ViewingPublicKey},
|
||||
@ -17,11 +18,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,
|
||||
};
|
||||
@ -37,8 +35,10 @@ pub fn compute_circuit_output(
|
||||
{
|
||||
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 {
|
||||
vpk,
|
||||
@ -267,16 +267,20 @@ pub fn compute_circuit_output(
|
||||
}
|
||||
|
||||
fn obfuscate_output_ordering(output: &mut PrivacyPreservingCircuitOutput) {
|
||||
output
|
||||
.new_commitments
|
||||
.sort_unstable_by_key(Commitment::to_byte_array);
|
||||
|
||||
let mut notes: Vec<_> = core::mem::take(&mut output.new_nullifiers)
|
||||
.into_iter()
|
||||
.zip(core::mem::take(&mut output.encrypted_private_post_states))
|
||||
let mut commitments: Vec<_> = output
|
||||
.private_actions
|
||||
.iter()
|
||||
.map(|action| action.commitment.clone())
|
||||
.collect();
|
||||
notes.sort_unstable_by_key(|((nullifier, _), _)| nullifier.to_byte_array());
|
||||
(output.new_nullifiers, output.encrypted_private_post_states) = notes.into_iter().unzip();
|
||||
commitments.sort_unstable_by_key(Commitment::to_byte_array);
|
||||
|
||||
output
|
||||
.private_actions
|
||||
.sort_unstable_by_key(|action| action.nullifier.to_byte_array());
|
||||
|
||||
for (action, commitment) in output.private_actions.iter_mut().zip(commitments) {
|
||||
action.commitment = commitment;
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_dummy_output(output: &mut PrivacyPreservingCircuitOutput, dummy: DummyInput) {
|
||||
@ -284,17 +288,18 @@ fn emit_dummy_output(output: &mut PrivacyPreservingCircuitOutput, dummy: DummyIn
|
||||
// The prover is responsible for their randomness.
|
||||
let nullifier = Nullifier::for_dummy(&dummy.nullifier_seed);
|
||||
let commitment = Commitment::for_dummy(&nullifier, &dummy.commitment_seed);
|
||||
output
|
||||
.new_nullifiers
|
||||
.push((nullifier, dummy.commitment_root));
|
||||
output.new_commitments.push(commitment);
|
||||
// Note: the encrypted post states are pushed as fed into the circuit.
|
||||
// That means that the prover is responsible for managing the randomness
|
||||
// so as to not reveal the padding.
|
||||
//
|
||||
// In particular, it is recommended to generate the ML KEM ciphertext
|
||||
// explicitly as these are not uniformly random.
|
||||
output.encrypted_private_post_states.push(dummy.note);
|
||||
output.private_actions.push(PrivateAction {
|
||||
nullifier,
|
||||
root: dummy.commitment_root,
|
||||
commitment,
|
||||
encrypted_post_state: dummy.note,
|
||||
});
|
||||
}
|
||||
|
||||
#[expect(
|
||||
@ -327,15 +332,16 @@ fn emit_private_output(
|
||||
&new_nullifier.0,
|
||||
);
|
||||
|
||||
output.new_nullifiers.push(new_nullifier);
|
||||
output.new_commitments.push(commitment_post);
|
||||
output
|
||||
.encrypted_private_post_states
|
||||
.push(EncryptedAccountData {
|
||||
output.private_actions.push(PrivateAction {
|
||||
nullifier: new_nullifier.0,
|
||||
root: new_nullifier.1,
|
||||
commitment: commitment_post,
|
||||
encrypted_post_state: EncryptedAccountData {
|
||||
ciphertext: encrypted_account,
|
||||
epk,
|
||||
view_tag,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn compute_update_nullifier_and_set_digest(
|
||||
@ -358,7 +364,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
fn note(tag: u8) -> (Nullifier, Commitment, EncryptedAccountData) {
|
||||
fn note(tag: u8) -> PrivateAction {
|
||||
let nullifier = Nullifier::for_dummy(&[tag; 32]);
|
||||
let commitment = Commitment::for_dummy(&nullifier, &[tag; 32]);
|
||||
let ciphertext = EncryptionScheme::encrypt(
|
||||
@ -367,37 +373,36 @@ mod tests {
|
||||
&SharedSecretKey([0; 32]),
|
||||
&nullifier,
|
||||
);
|
||||
let encrypted = EncryptedAccountData {
|
||||
ciphertext,
|
||||
epk: EphemeralPublicKey(vec![tag]),
|
||||
view_tag: 0,
|
||||
};
|
||||
(nullifier, commitment, encrypted)
|
||||
PrivateAction {
|
||||
nullifier,
|
||||
root: DUMMY_COMMITMENT_HASH,
|
||||
commitment,
|
||||
encrypted_post_state: EncryptedAccountData {
|
||||
ciphertext,
|
||||
epk: EphemeralPublicKey(vec![tag]),
|
||||
view_tag: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obfuscate_byte_sorts_commitments_and_nullifiers() {
|
||||
let mut output = PrivacyPreservingCircuitOutput::default();
|
||||
for tag in 0..3 {
|
||||
let (nullifier, commitment, encrypted) = note(tag);
|
||||
output
|
||||
.new_nullifiers
|
||||
.push((nullifier, DUMMY_COMMITMENT_HASH));
|
||||
output.new_commitments.push(commitment);
|
||||
output.encrypted_private_post_states.push(encrypted);
|
||||
output.private_actions.push(note(tag));
|
||||
}
|
||||
|
||||
obfuscate_output_ordering(&mut output);
|
||||
|
||||
assert!(
|
||||
output
|
||||
.new_commitments
|
||||
.is_sorted_by_key(Commitment::to_byte_array)
|
||||
.private_actions
|
||||
.is_sorted_by_key(|action| action.nullifier.to_byte_array())
|
||||
);
|
||||
assert!(
|
||||
output
|
||||
.new_nullifiers
|
||||
.is_sorted_by_key(|(nullifier, _)| nullifier.to_byte_array())
|
||||
.private_actions
|
||||
.is_sorted_by_key(|action| action.commitment.to_byte_array())
|
||||
);
|
||||
}
|
||||
|
||||
@ -405,27 +410,26 @@ mod tests {
|
||||
fn obfuscate_keeps_each_nullifier_with_its_ciphertext() {
|
||||
let mut output = PrivacyPreservingCircuitOutput::default();
|
||||
for tag in 0..3 {
|
||||
let (nullifier, _, encrypted) = note(tag);
|
||||
output
|
||||
.new_nullifiers
|
||||
.push((nullifier, DUMMY_COMMITMENT_HASH));
|
||||
output.encrypted_private_post_states.push(encrypted);
|
||||
output.private_actions.push(note(tag));
|
||||
}
|
||||
let paired: HashMap<[u8; 32], EphemeralPublicKey> = output
|
||||
.new_nullifiers
|
||||
.private_actions
|
||||
.iter()
|
||||
.zip(&output.encrypted_private_post_states)
|
||||
.map(|((nullifier, _), note)| (nullifier.to_byte_array(), note.epk.clone()))
|
||||
.map(|action| {
|
||||
(
|
||||
action.nullifier.to_byte_array(),
|
||||
action.encrypted_post_state.epk.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
obfuscate_output_ordering(&mut output);
|
||||
|
||||
for ((nullifier, _), note) in output
|
||||
.new_nullifiers
|
||||
.iter()
|
||||
.zip(&output.encrypted_private_post_states)
|
||||
{
|
||||
assert_eq!(paired[&nullifier.to_byte_array()], note.epk);
|
||||
for action in &output.private_actions {
|
||||
assert_eq!(
|
||||
paired[&action.nullifier.to_byte_array()],
|
||||
action.encrypted_post_state.epk
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@ -147,18 +148,53 @@ impl InputAccountIdentity {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Debug, Clone, Default, 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, Default))]
|
||||
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.
|
||||
@ -183,50 +219,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(),
|
||||
};
|
||||
|
||||
@ -32,10 +32,10 @@ pub const DUMMY_COMMITMENT_HASH: [u8; 32] = [
|
||||
129, 241, 118, 39, 41, 253, 141, 171, 184, 71, 8, 41,
|
||||
];
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)
|
||||
derive(Default, PartialEq, Eq, Hash, PartialOrd, Ord)
|
||||
)]
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
|
||||
@ -45,13 +45,15 @@ pub struct SharedSecretKey(pub [u8; 32]);
|
||||
|
||||
/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the
|
||||
/// former ECDH ephemeral public key. Always `ML_KEM_768_CIPHERTEXT_LEN` (1088) bytes.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub struct EphemeralPublicKey(pub Vec<u8>);
|
||||
|
||||
pub struct EncryptionScheme;
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Clone, PartialEq, Eq))]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Clone, Default, PartialEq, Eq))]
|
||||
pub struct Ciphertext(pub(crate) Vec<u8>);
|
||||
|
||||
#[cfg(any(feature = "host", test))]
|
||||
@ -71,7 +73,10 @@ pub type ViewTag = u8;
|
||||
|
||||
/// Encrypted private-account note for one output.
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Debug, Clone, Default, PartialEq, Eq)
|
||||
)]
|
||||
pub struct EncryptedAccountData {
|
||||
pub ciphertext: Ciphertext,
|
||||
pub epk: EphemeralPublicKey,
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
)]
|
||||
|
||||
pub use circuit_io::{
|
||||
DummyInput, InputAccountIdentity, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
DummyInput, InputAccountIdentity, PrivacyPreservingCircuitInput,
|
||||
PrivacyPreservingCircuitOutput, PrivateAction, PublicAction,
|
||||
};
|
||||
pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
|
||||
@ -72,7 +72,7 @@ pub type NullifierSecretKey = [u8; 32];
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)
|
||||
derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)
|
||||
)]
|
||||
pub struct Nullifier(pub(super) [u8; 32]);
|
||||
|
||||
|
||||
@ -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_nullifiers[idx].0,
|
||||
&output.private_actions[idx].nullifier,
|
||||
)
|
||||
.unwrap();
|
||||
kind
|
||||
@ -102,18 +102,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_nullifiers[0].0,
|
||||
&output.private_actions[0].nullifier,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(recipient_post, expected_recipient_post);
|
||||
@ -216,43 +214,46 @@ 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!(output.public_actions.is_empty());
|
||||
let sender_nullifier = expected_new_nullifiers[0].0;
|
||||
let recipient_nullifier = expected_new_nullifiers[1].0;
|
||||
|
||||
let mut expected_new_commitments = expected_new_commitments;
|
||||
expected_new_commitments.sort_unstable_by_key(Commitment::to_byte_array);
|
||||
assert_eq!(output.new_commitments, expected_new_commitments);
|
||||
let mut sorted_commitments = expected_new_commitments;
|
||||
sorted_commitments.sort_unstable_by_key(Commitment::to_byte_array);
|
||||
assert_eq!(output.commitments(), sorted_commitments);
|
||||
|
||||
let mut expected_new_nullifiers = expected_new_nullifiers;
|
||||
expected_new_nullifiers.sort_unstable_by_key(|(nullifier, _)| nullifier.to_byte_array());
|
||||
assert_eq!(output.new_nullifiers, expected_new_nullifiers);
|
||||
let mut sorted_nullifiers = expected_new_nullifiers;
|
||||
sorted_nullifiers.sort_unstable_by_key(|(nullifier, _)| nullifier.to_byte_array());
|
||||
assert_eq!(output.nullifiers(), sorted_nullifiers);
|
||||
|
||||
assert_eq!(output.encrypted_private_post_states.len(), 2);
|
||||
assert_eq!(output.private_actions.len(), 2);
|
||||
|
||||
let sender_slot = output
|
||||
.new_nullifiers
|
||||
.private_actions
|
||||
.iter()
|
||||
.position(|(nullifier, _)| *nullifier == sender_nullifier)
|
||||
.position(|action| action.nullifier == sender_nullifier)
|
||||
.unwrap();
|
||||
let (_identifier, sender_post) = EncryptionScheme::decrypt(
|
||||
&output.encrypted_private_post_states[sender_slot].ciphertext,
|
||||
&output.private_actions[sender_slot]
|
||||
.encrypted_post_state
|
||||
.ciphertext,
|
||||
&shared_secret_1,
|
||||
&output.new_nullifiers[sender_slot].0,
|
||||
&output.private_actions[sender_slot].nullifier,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(sender_post, expected_private_account_1);
|
||||
|
||||
let recipient_slot = output
|
||||
.new_nullifiers
|
||||
.private_actions
|
||||
.iter()
|
||||
.position(|(nullifier, _)| *nullifier == recipient_nullifier)
|
||||
.position(|action| action.nullifier == recipient_nullifier)
|
||||
.unwrap();
|
||||
let (_identifier, recipient_post) = EncryptionScheme::decrypt(
|
||||
&output.encrypted_private_post_states[recipient_slot].ciphertext,
|
||||
&output.private_actions[recipient_slot]
|
||||
.encrypted_post_state
|
||||
.ciphertext,
|
||||
&shared_secret_2,
|
||||
&output.new_nullifiers[recipient_slot].0,
|
||||
&output.private_actions[recipient_slot].nullifier,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(recipient_post, expected_private_account_2);
|
||||
@ -281,9 +282,9 @@ fn init_note_view_tag_is_derived_from_account_keys() {
|
||||
.unwrap();
|
||||
|
||||
assert!(proof.is_valid_for(&output));
|
||||
assert_eq!(output.encrypted_private_post_states.len(), 1);
|
||||
assert_eq!(output.private_actions.len(), 1);
|
||||
assert_eq!(
|
||||
output.encrypted_private_post_states[0].view_tag,
|
||||
output.private_actions[0].encrypted_post_state.view_tag,
|
||||
EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()),
|
||||
);
|
||||
}
|
||||
@ -324,8 +325,11 @@ fn update_note_view_tag_is_the_supplied_value() {
|
||||
.unwrap();
|
||||
|
||||
assert!(proof.is_valid_for(&output));
|
||||
assert_eq!(output.encrypted_private_post_states.len(), 1);
|
||||
assert_eq!(output.encrypted_private_post_states[0].view_tag, fed_tag);
|
||||
assert_eq!(output.private_actions.len(), 1);
|
||||
assert_eq!(
|
||||
output.private_actions[0].encrypted_post_state.view_tag,
|
||||
fed_tag
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -448,7 +452,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.
|
||||
@ -502,7 +506,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,
|
||||
@ -554,7 +558,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 PublicActionWithID {
|
||||
pub account_id: AccountId,
|
||||
pub post_state: Account,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Message {
|
||||
pub public_account_ids: Vec<AccountId>,
|
||||
pub public_actions: Vec<PublicActionWithID>,
|
||||
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,47 @@ 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| PublicActionWithID {
|
||||
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]
|
||||
@ -84,34 +114,20 @@ impl Message {
|
||||
|
||||
Sha256::digest(bytes).into()
|
||||
}
|
||||
|
||||
/// Ensure that the commitments, nullifiers, and ciphertexts agree.
|
||||
pub fn validate_note_lengths(&self) -> Result<usize, LeeError> {
|
||||
let count = self.new_nullifiers.len();
|
||||
if self.new_commitments.len() != count || self.encrypted_private_post_states.len() != count
|
||||
{
|
||||
return Err(LeeError::InvalidInput(format!(
|
||||
"Note vectors disagree in length with {count} nullifiers, {} commitments, and {} ciphertexts",
|
||||
self.new_commitments.len(),
|
||||
self.encrypted_private_post_states.len(),
|
||||
)));
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use lee_core::{
|
||||
Commitment, EncryptionScheme, EphemeralSecretKey, Nullifier, NullifierPublicKey,
|
||||
PrivateAccountKind, SharedSecretKey,
|
||||
Commitment, EncryptionScheme, EphemeralPublicKey, EphemeralSecretKey, 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, PublicActionWithID};
|
||||
|
||||
#[must_use]
|
||||
pub fn message_for_tests() -> Message {
|
||||
@ -125,78 +141,57 @@ pub mod tests {
|
||||
let npk2 = NullifierPublicKey::from(&nsk2);
|
||||
let vpk = ViewingPublicKey::from_seed(&[7; 32], &[8; 32]);
|
||||
|
||||
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, &vpk, 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, &vpk, 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![PublicActionWithID {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_note_lengths_accepts_matching_and_rejects_mismatched() {
|
||||
assert_eq!(Message::default().validate_note_lengths().unwrap(), 0);
|
||||
|
||||
let mismatched = Message {
|
||||
new_commitments: vec![Commitment::new(
|
||||
&AccountId::new([0; 32]),
|
||||
&Account::default(),
|
||||
)],
|
||||
..Default::default()
|
||||
};
|
||||
assert!(mismatched.validate_note_lengths().is_err());
|
||||
}
|
||||
|
||||
#[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,12 @@ 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()
|
||||
}
|
||||
|
||||
@ -402,11 +402,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
|
||||
@ -482,8 +479,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
|
||||
@ -756,7 +752,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, &[]);
|
||||
|
||||
@ -801,7 +797,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);
|
||||
@ -851,7 +847,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);
|
||||
@ -961,8 +957,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
&simple_transfer.clone().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output).unwrap();
|
||||
let message = 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(
|
||||
@ -997,8 +992,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
&simple_transfer.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output).unwrap();
|
||||
let message = 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(
|
||||
@ -1041,8 +1035,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![Nonce(0)], output).unwrap();
|
||||
let message = 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(
|
||||
@ -1079,7 +1072,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(
|
||||
@ -1130,9 +1123,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(
|
||||
|
||||
@ -341,9 +341,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);
|
||||
@ -466,7 +464,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);
|
||||
|
||||
|
||||
@ -290,12 +290,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)
|
||||
@ -349,7 +344,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, &[]);
|
||||
|
||||
@ -398,8 +393,7 @@ fn deshielded_balance_transfer_for_tests(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![*recipient_account_id], vec![], output).unwrap();
|
||||
let message = 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
|
||||
@ -128,7 +128,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!(
|
||||
@ -154,7 +154,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!(
|
||||
|
||||
@ -149,7 +149,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)
|
||||
@ -214,7 +214,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
|
||||
.public_actions
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(message.public_post_states.clone())
|
||||
.collect();
|
||||
let new_nullifiers = message
|
||||
.new_nullifiers
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|(nullifier, _)| nullifier)
|
||||
.map(|action| (action.account_id, action.post_state.clone()))
|
||||
.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,16 @@ 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_actions)
|
||||
.map(|(pre, action)| PublicAction {
|
||||
pre,
|
||||
post: action.post_state.clone(),
|
||||
})
|
||||
.collect(),
|
||||
private_actions: message.private_actions.clone(),
|
||||
block_validity_window: message.block_validity_window,
|
||||
timestamp_validity_window: message.timestamp_validity_window,
|
||||
};
|
||||
|
||||
@ -188,12 +188,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);
|
||||
@ -350,12 +348,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);
|
||||
@ -480,8 +476,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},
|
||||
};
|
||||
|
||||
@ -503,12 +500,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(),
|
||||
};
|
||||
|
||||
@ -68,15 +68,18 @@ pub fn PrivacyPreservingTxDetails(tx: PrivacyPreservingTransaction) -> impl Into
|
||||
witness_set,
|
||||
} = tx;
|
||||
let PrivacyPreservingMessage {
|
||||
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,
|
||||
} = message;
|
||||
let private_action_count = private_actions.len();
|
||||
let public_account_ids: Vec<_> = public_actions
|
||||
.into_iter()
|
||||
.map(|action| action.account_id)
|
||||
.collect();
|
||||
let public_account_count = public_account_ids.len();
|
||||
let WitnessSet {
|
||||
signatures_and_public_keys: _,
|
||||
proof,
|
||||
@ -90,22 +93,12 @@ pub fn PrivacyPreservingTxDetails(tx: PrivacyPreservingTransaction) -> impl Into
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Public Accounts:"</span>
|
||||
<span class="info-value">
|
||||
{public_account_ids.len().to_string()}
|
||||
{public_account_count.to_string()}
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"New Commitments:"</span>
|
||||
<span class="info-value">{new_commitments.len().to_string()}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Nullifiers:"</span>
|
||||
<span class="info-value">{new_nullifiers.len().to_string()}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Encrypted States:"</span>
|
||||
<span class="info-value">
|
||||
{encrypted_private_post_states.len().to_string()}
|
||||
</span>
|
||||
<span class="info-label">"Private Actions:"</span>
|
||||
<span class="info-value">{private_action_count.to_string()}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">"Proof Size:"</span>
|
||||
|
||||
@ -40,8 +40,8 @@ pub fn TransactionPreview(transaction: Transaction) -> impl IntoView {
|
||||
} = tx;
|
||||
format!(
|
||||
"{} public accounts, {} commitments",
|
||||
message.public_account_ids.len(),
|
||||
message.new_commitments.len()
|
||||
message.public_actions.len(),
|
||||
message.private_actions.len()
|
||||
)
|
||||
}
|
||||
Transaction::ProgramDeployment(tx) => {
|
||||
|
||||
@ -225,13 +225,18 @@ typedef struct FfiAccount {
|
||||
struct FfiU128 nonce;
|
||||
} FfiAccount;
|
||||
|
||||
typedef struct FfiVec_FfiAccount {
|
||||
struct FfiAccount *entries;
|
||||
typedef struct FfiPublicAction {
|
||||
FfiAccountId account_id;
|
||||
struct FfiAccount post_state;
|
||||
} FfiPublicAction;
|
||||
|
||||
typedef struct FfiVec_FfiPublicAction {
|
||||
struct FfiPublicAction *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiAccount;
|
||||
} FfiVec_FfiPublicAction;
|
||||
|
||||
typedef struct FfiVec_FfiAccount FfiAccountList;
|
||||
typedef struct FfiVec_FfiPublicAction FfiPublicActionList;
|
||||
|
||||
typedef struct FfiVec_u8 {
|
||||
uint8_t *entries;
|
||||
@ -247,42 +252,25 @@ typedef struct FfiEncryptedAccountData {
|
||||
uint8_t view_tag;
|
||||
} FfiEncryptedAccountData;
|
||||
|
||||
typedef struct FfiVec_FfiEncryptedAccountData {
|
||||
struct FfiEncryptedAccountData *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiEncryptedAccountData;
|
||||
|
||||
typedef struct FfiVec_FfiEncryptedAccountData FfiEncryptedAccountDataList;
|
||||
|
||||
typedef struct FfiVec_FfiBytes32 {
|
||||
struct FfiBytes32 *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiBytes32;
|
||||
|
||||
typedef struct FfiVec_FfiBytes32 FfiVecBytes32;
|
||||
|
||||
typedef struct FfiNullifierCommitmentSet {
|
||||
typedef struct FfiPrivateAction {
|
||||
struct FfiBytes32 nullifier;
|
||||
struct FfiBytes32 commitment_set_digest;
|
||||
} FfiNullifierCommitmentSet;
|
||||
struct FfiBytes32 root;
|
||||
struct FfiBytes32 commitment;
|
||||
struct FfiEncryptedAccountData encrypted_post_state;
|
||||
} FfiPrivateAction;
|
||||
|
||||
typedef struct FfiVec_FfiNullifierCommitmentSet {
|
||||
struct FfiNullifierCommitmentSet *entries;
|
||||
typedef struct FfiVec_FfiPrivateAction {
|
||||
struct FfiPrivateAction *entries;
|
||||
uintptr_t len;
|
||||
uintptr_t capacity;
|
||||
} FfiVec_FfiNullifierCommitmentSet;
|
||||
} FfiVec_FfiPrivateAction;
|
||||
|
||||
typedef struct FfiVec_FfiNullifierCommitmentSet FfiNullifierCommitmentSetList;
|
||||
typedef struct FfiVec_FfiPrivateAction FfiPrivateActionList;
|
||||
|
||||
typedef struct FfiPrivacyPreservingMessage {
|
||||
FfiAccountIdList public_account_ids;
|
||||
FfiPublicActionList public_actions;
|
||||
FfiNonceList nonces;
|
||||
FfiAccountList public_post_states;
|
||||
FfiEncryptedAccountDataList encrypted_private_post_states;
|
||||
FfiVecBytes32 new_commitments;
|
||||
FfiNullifierCommitmentSetList new_nullifiers;
|
||||
FfiPrivateActionList private_actions;
|
||||
uint64_t block_validity_window[2];
|
||||
uint64_t timestamp_validity_window[2];
|
||||
} FfiPrivacyPreservingMessage;
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
use indexer_service_protocol::{
|
||||
AccountId, Ciphertext, Commitment, CommitmentSetDigest, EncryptedAccountData,
|
||||
EphemeralPublicKey, HashType, Nullifier, PrivacyPreservingMessage,
|
||||
PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction,
|
||||
ProgramId, Proof, PublicKey, PublicMessage, PublicTransaction, Signature, Transaction,
|
||||
ValidityWindow, WitnessSet,
|
||||
PrivacyPreservingTransaction, PrivateAction, ProgramDeploymentMessage,
|
||||
ProgramDeploymentTransaction, ProgramId, Proof, PublicActionWithID, PublicKey, PublicMessage,
|
||||
PublicTransaction, Signature, Transaction, ValidityWindow, WitnessSet,
|
||||
};
|
||||
|
||||
use crate::api::types::{
|
||||
FfiBytes32, FfiHashType, FfiOption, FfiProgramId, FfiPublicKey, FfiSignature, FfiVec,
|
||||
FfiAccountId, FfiBytes32, FfiHashType, FfiOption, FfiProgramId, FfiPublicKey, FfiSignature,
|
||||
FfiVec,
|
||||
account::FfiAccount,
|
||||
vectors::{
|
||||
FfiAccountIdList, FfiAccountList, FfiEncryptedAccountDataList, FfiInstructionDataList,
|
||||
FfiNonceList, FfiNullifierCommitmentSetList, FfiProgramDeploymentMessage, FfiProof,
|
||||
FfiSignaturePubKeyList, FfiVecBytes32, FfiVecU8,
|
||||
FfiAccountIdList, FfiInstructionDataList, FfiNonceList, FfiPrivateActionList,
|
||||
FfiProgramDeploymentMessage, FfiProof, FfiPublicActionList, FfiSignaturePubKeyList,
|
||||
FfiVecU8,
|
||||
},
|
||||
};
|
||||
|
||||
@ -156,12 +158,15 @@ impl From<Box<FfiPrivateTransactionBody>> for PrivacyPreservingTransaction {
|
||||
Self {
|
||||
hash: HashType(value.hash.data),
|
||||
message: PrivacyPreservingMessage {
|
||||
public_account_ids: {
|
||||
let std_vec: Vec<_> = value.message.public_account_ids.into();
|
||||
public_actions: {
|
||||
let std_vec: Vec<_> = value.message.public_actions.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| AccountId {
|
||||
value: ffi_val.data,
|
||||
.map(|ffi_val| PublicActionWithID {
|
||||
account_id: AccountId {
|
||||
value: ffi_val.account_id.data,
|
||||
},
|
||||
post_state: ffi_val.post_state.into(),
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
@ -169,37 +174,21 @@ impl From<Box<FfiPrivateTransactionBody>> for PrivacyPreservingTransaction {
|
||||
let std_vec: Vec<_> = value.message.nonces.into();
|
||||
std_vec.into_iter().map(Into::into).collect()
|
||||
},
|
||||
public_post_states: {
|
||||
let std_vec: Vec<_> = value.message.public_post_states.into();
|
||||
std_vec.into_iter().map(Into::into).collect()
|
||||
},
|
||||
encrypted_private_post_states: {
|
||||
let std_vec: Vec<_> = value.message.encrypted_private_post_states.into();
|
||||
private_actions: {
|
||||
let std_vec: Vec<_> = value.message.private_actions.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| EncryptedAccountData {
|
||||
ciphertext: Ciphertext(ffi_val.ciphertext.into()),
|
||||
epk: EphemeralPublicKey(ffi_val.epk.into()),
|
||||
view_tag: ffi_val.view_tag,
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
new_commitments: {
|
||||
let std_vec: Vec<_> = value.message.new_commitments.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| Commitment(ffi_val.data))
|
||||
.collect()
|
||||
},
|
||||
new_nullifiers: {
|
||||
let std_vec: Vec<_> = value.message.new_nullifiers.into();
|
||||
std_vec
|
||||
.into_iter()
|
||||
.map(|ffi_val| {
|
||||
(
|
||||
Nullifier(ffi_val.nullifier.data),
|
||||
CommitmentSetDigest(ffi_val.commitment_set_digest.data),
|
||||
)
|
||||
.map(|ffi_val| PrivateAction {
|
||||
nullifier: Nullifier(ffi_val.nullifier.data),
|
||||
root: CommitmentSetDigest(ffi_val.root.data),
|
||||
commitment: Commitment(ffi_val.commitment.data),
|
||||
encrypted_post_state: EncryptedAccountData {
|
||||
ciphertext: Ciphertext(
|
||||
ffi_val.encrypted_post_state.ciphertext.into(),
|
||||
),
|
||||
epk: EphemeralPublicKey(ffi_val.encrypted_post_state.epk.into()),
|
||||
view_tag: ffi_val.encrypted_post_state.view_tag,
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
@ -229,14 +218,53 @@ impl From<Box<FfiPrivateTransactionBody>> for PrivacyPreservingTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPublicAction {
|
||||
pub account_id: FfiAccountId,
|
||||
pub post_state: FfiAccount,
|
||||
}
|
||||
|
||||
impl From<PublicActionWithID> for FfiPublicAction {
|
||||
fn from(value: PublicActionWithID) -> Self {
|
||||
let post_state: lee::Account = value
|
||||
.post_state
|
||||
.try_into()
|
||||
.expect("Source is in blocks, must fit");
|
||||
Self {
|
||||
account_id: value.account_id.into(),
|
||||
post_state: post_state.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPrivateAction {
|
||||
pub nullifier: FfiBytes32,
|
||||
pub root: FfiBytes32,
|
||||
pub commitment: FfiBytes32,
|
||||
pub encrypted_post_state: FfiEncryptedAccountData,
|
||||
}
|
||||
|
||||
impl From<PrivateAction> for FfiPrivateAction {
|
||||
fn from(value: PrivateAction) -> Self {
|
||||
Self {
|
||||
nullifier: FfiBytes32 {
|
||||
data: value.nullifier.0,
|
||||
},
|
||||
root: FfiBytes32 { data: value.root.0 },
|
||||
commitment: FfiBytes32 {
|
||||
data: value.commitment.0,
|
||||
},
|
||||
encrypted_post_state: value.encrypted_post_state.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiPrivacyPreservingMessage {
|
||||
pub public_account_ids: FfiAccountIdList,
|
||||
pub public_actions: FfiPublicActionList,
|
||||
pub nonces: FfiNonceList,
|
||||
pub public_post_states: FfiAccountList,
|
||||
pub encrypted_private_post_states: FfiEncryptedAccountDataList,
|
||||
pub new_commitments: FfiVecBytes32,
|
||||
pub new_nullifiers: FfiNullifierCommitmentSetList,
|
||||
pub private_actions: FfiPrivateActionList,
|
||||
pub block_validity_window: [u64; 2],
|
||||
pub timestamp_validity_window: [u64; 2],
|
||||
}
|
||||
@ -244,18 +272,15 @@ pub struct FfiPrivacyPreservingMessage {
|
||||
impl From<PrivacyPreservingMessage> for FfiPrivacyPreservingMessage {
|
||||
fn from(value: PrivacyPreservingMessage) -> Self {
|
||||
let PrivacyPreservingMessage {
|
||||
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
|
||||
public_actions: public_actions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
@ -265,25 +290,7 @@ impl From<PrivacyPreservingMessage> for FfiPrivacyPreservingMessage {
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
public_post_states: public_post_states
|
||||
.into_iter()
|
||||
.map(|acc_ind| -> lee::Account {
|
||||
acc_ind.try_into().expect("Source is in blocks, must fit")
|
||||
})
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
encrypted_private_post_states: encrypted_private_post_states
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
new_commitments: new_commitments
|
||||
.into_iter()
|
||||
.map(|comm| FfiBytes32 { data: comm.0 })
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
new_nullifiers: new_nullifiers
|
||||
private_actions: private_actions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
@ -294,21 +301,6 @@ impl From<PrivacyPreservingMessage> for FfiPrivacyPreservingMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiNullifierCommitmentSet {
|
||||
pub nullifier: FfiBytes32,
|
||||
pub commitment_set_digest: FfiBytes32,
|
||||
}
|
||||
|
||||
impl From<(Nullifier, CommitmentSetDigest)> for FfiNullifierCommitmentSet {
|
||||
fn from(value: (Nullifier, CommitmentSetDigest)) -> Self {
|
||||
Self {
|
||||
nullifier: FfiBytes32 { data: value.0.0 },
|
||||
commitment_set_digest: FfiBytes32 { data: value.1.0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FfiEncryptedAccountData {
|
||||
pub ciphertext: FfiVecU8,
|
||||
|
||||
@ -1,19 +1,12 @@
|
||||
use crate::api::types::{
|
||||
FfiAccountId, FfiBytes32, FfiNonce, FfiVec,
|
||||
account::FfiAccount,
|
||||
transaction::{
|
||||
FfiEncryptedAccountData, FfiNullifierCommitmentSet, FfiSignaturePubKeyEntry, FfiTransaction,
|
||||
},
|
||||
FfiAccountId, FfiNonce, FfiVec,
|
||||
transaction::{FfiPrivateAction, FfiPublicAction, FfiSignaturePubKeyEntry, FfiTransaction},
|
||||
};
|
||||
|
||||
pub type FfiVecU8 = FfiVec<u8>;
|
||||
|
||||
pub type FfiAccountList = FfiVec<FfiAccount>;
|
||||
|
||||
pub type FfiAccountIdList = FfiVec<FfiAccountId>;
|
||||
|
||||
pub type FfiVecBytes32 = FfiVec<FfiBytes32>;
|
||||
|
||||
pub type FfiBlockBody = FfiVec<FfiTransaction>;
|
||||
|
||||
pub type FfiNonceList = FfiVec<FfiNonce>;
|
||||
@ -26,6 +19,6 @@ pub type FfiProof = FfiVecU8;
|
||||
|
||||
pub type FfiProgramDeploymentMessage = FfiVecU8;
|
||||
|
||||
pub type FfiEncryptedAccountDataList = FfiVec<FfiEncryptedAccountData>;
|
||||
pub type FfiPublicActionList = FfiVec<FfiPublicAction>;
|
||||
|
||||
pub type FfiNullifierCommitmentSetList = FfiVec<FfiNullifierCommitmentSet>;
|
||||
pub type FfiPrivateActionList = FfiVec<FfiPrivateAction>;
|
||||
|
||||
@ -6,9 +6,9 @@ use crate::{
|
||||
Account, AccountId, BedrockStatus, Block, BlockBody, BlockHeader, BlockIngestError, Ciphertext,
|
||||
Commitment, CommitmentSetDigest, Data, EncryptedAccountData, EphemeralPublicKey, HashType,
|
||||
IndexerStatus, IndexerSyncState, Nullifier, PrivacyPreservingMessage,
|
||||
PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction,
|
||||
ProgramId, Proof, PublicKey, PublicMessage, PublicTransaction, Signature, StallReason,
|
||||
Transaction, ValidityWindow, WitnessSet,
|
||||
PrivacyPreservingTransaction, PrivateAction, ProgramDeploymentMessage,
|
||||
ProgramDeploymentTransaction, ProgramId, Proof, PublicActionWithID, PublicKey, PublicMessage,
|
||||
PublicTransaction, Signature, StallReason, Transaction, ValidityWindow, WitnessSet,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -279,71 +279,97 @@ impl From<PublicMessage> for lee::public_transaction::Message {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lee::privacy_preserving_transaction::message::PublicActionWithID> for PublicActionWithID {
|
||||
fn from(value: lee::privacy_preserving_transaction::message::PublicActionWithID) -> Self {
|
||||
Self {
|
||||
account_id: value.account_id.into(),
|
||||
post_state: value.post_state.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lee_core::PrivateAction> for PrivateAction {
|
||||
fn from(value: lee_core::PrivateAction) -> Self {
|
||||
Self {
|
||||
nullifier: value.nullifier.into(),
|
||||
root: value.root.into(),
|
||||
commitment: value.commitment.into(),
|
||||
encrypted_post_state: value.encrypted_post_state.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_actions: public_actions.into_iter().map(Into::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)
|
||||
.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: private_actions.into_iter().map(Into::into).collect(),
|
||||
block_validity_window: block_validity_window.into(),
|
||||
timestamp_validity_window: timestamp_validity_window.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PublicActionWithID>
|
||||
for lee::privacy_preserving_transaction::message::PublicActionWithID
|
||||
{
|
||||
type Error = lee::error::LeeError;
|
||||
|
||||
fn try_from(value: PublicActionWithID) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
account_id: value.account_id.into(),
|
||||
post_state: value
|
||||
.post_state
|
||||
.try_into()
|
||||
.map_err(|e| lee::error::LeeError::InvalidInput(format!("{e}")))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateAction> for lee_core::PrivateAction {
|
||||
fn from(value: PrivateAction) -> Self {
|
||||
Self {
|
||||
nullifier: value.nullifier.into(),
|
||||
root: value.root.into(),
|
||||
commitment: value.commitment.into(),
|
||||
encrypted_post_state: value.encrypted_post_state.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PrivacyPreservingMessage> for lee::privacy_preserving_transaction::message::Message {
|
||||
type Error = lee::error::LeeError;
|
||||
|
||||
fn try_from(value: PrivacyPreservingMessage) -> Result<Self, Self::Error> {
|
||||
let PrivacyPreservingMessage {
|
||||
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;
|
||||
|
||||
let public_actions = public_actions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let private_actions = private_actions.into_iter().map(Into::into).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}")))?,
|
||||
|
||||
@ -226,14 +226,25 @@ pub struct PublicMessage {
|
||||
|
||||
pub type InstructionData = Vec<u32>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PublicActionWithID {
|
||||
pub account_id: AccountId,
|
||||
pub post_state: Account,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PrivateAction {
|
||||
pub nullifier: Nullifier,
|
||||
pub root: CommitmentSetDigest,
|
||||
pub commitment: Commitment,
|
||||
pub encrypted_post_state: EncryptedAccountData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PrivacyPreservingMessage {
|
||||
pub public_account_ids: Vec<AccountId>,
|
||||
pub public_actions: Vec<PublicActionWithID>,
|
||||
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: ValidityWindow,
|
||||
pub timestamp_validity_window: ValidityWindow,
|
||||
}
|
||||
|
||||
@ -11,9 +11,9 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use indexer_service_protocol::{
|
||||
Account, AccountId, BedrockStatus, Block, BlockBody, BlockHeader, BlockId, Commitment,
|
||||
CommitmentSetDigest, Data, EncryptedAccountData, HashType, IndexerStatus, IndexerSyncState,
|
||||
PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage,
|
||||
ProgramDeploymentTransaction, ProgramId, PublicMessage, PublicTransaction, Signature,
|
||||
Transaction, ValidityWindow, WitnessSet,
|
||||
PrivacyPreservingMessage, PrivacyPreservingTransaction, PrivateAction,
|
||||
ProgramDeploymentMessage, ProgramDeploymentTransaction, ProgramId, PublicActionWithID,
|
||||
PublicMessage, PublicTransaction, Signature, Transaction, ValidityWindow, WitnessSet,
|
||||
};
|
||||
use jsonrpsee::{
|
||||
core::{SubscriptionResult, async_trait},
|
||||
@ -300,9 +300,11 @@ impl indexer_service_rpc::RpcServer for MockIndexerService {
|
||||
.values()
|
||||
.filter(|(tx, _)| match tx {
|
||||
Transaction::Public(pub_tx) => pub_tx.message.account_ids.contains(&account_id),
|
||||
Transaction::PrivacyPreserving(priv_tx) => {
|
||||
priv_tx.message.public_account_ids.contains(&account_id)
|
||||
}
|
||||
Transaction::PrivacyPreserving(priv_tx) => priv_tx
|
||||
.message
|
||||
.public_actions
|
||||
.iter()
|
||||
.any(|action| action.account_id == account_id),
|
||||
Transaction::ProgramDeployment(_) => false,
|
||||
})
|
||||
.cloned()
|
||||
@ -381,24 +383,26 @@ fn mock_privacy_preserving_tx(
|
||||
Transaction::PrivacyPreserving(PrivacyPreservingTransaction {
|
||||
hash: tx_hash,
|
||||
message: PrivacyPreservingMessage {
|
||||
public_account_ids: vec![account_ids[tx_idx as usize % account_ids.len()]],
|
||||
public_actions: vec![PublicActionWithID {
|
||||
account_id: account_ids[tx_idx as usize % account_ids.len()],
|
||||
post_state: Account {
|
||||
program_owner: ProgramId([1_u32; 8]),
|
||||
balance: 500,
|
||||
data: Data(vec![0xdd, 0xee]),
|
||||
nonce: block_id as u128,
|
||||
},
|
||||
}],
|
||||
nonces: vec![block_id as u128],
|
||||
public_post_states: vec![Account {
|
||||
program_owner: ProgramId([1_u32; 8]),
|
||||
balance: 500,
|
||||
data: Data(vec![0xdd, 0xee]),
|
||||
nonce: block_id as u128,
|
||||
private_actions: vec![PrivateAction {
|
||||
nullifier: indexer_service_protocol::Nullifier([tx_idx as u8; 32]),
|
||||
root: CommitmentSetDigest([0xff; 32]),
|
||||
commitment: Commitment([block_id as u8; 32]),
|
||||
encrypted_post_state: EncryptedAccountData {
|
||||
ciphertext: indexer_service_protocol::Ciphertext(vec![0x01, 0x02, 0x03, 0x04]),
|
||||
epk: indexer_service_protocol::EphemeralPublicKey(vec![0xaa; 32]),
|
||||
view_tag: 42,
|
||||
},
|
||||
}],
|
||||
encrypted_private_post_states: vec![EncryptedAccountData {
|
||||
ciphertext: indexer_service_protocol::Ciphertext(vec![0x01, 0x02, 0x03, 0x04]),
|
||||
epk: indexer_service_protocol::EphemeralPublicKey(vec![0xaa; 32]),
|
||||
view_tag: 42,
|
||||
}],
|
||||
new_commitments: vec![Commitment([block_id as u8; 32])],
|
||||
new_nullifiers: vec![(
|
||||
indexer_service_protocol::Nullifier([tx_idx as u8; 32]),
|
||||
CommitmentSetDigest([0xff; 32]),
|
||||
)],
|
||||
block_validity_window: ValidityWindow((None, None)),
|
||||
timestamp_validity_window: ValidityWindow((None, None)),
|
||||
},
|
||||
|
||||
@ -875,8 +875,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 =
|
||||
|
||||
@ -383,7 +383,6 @@ impl WalletCore {
|
||||
let LeeTransaction::PrivacyPreserving(pp_tx) = &tx else {
|
||||
continue;
|
||||
};
|
||||
pp_tx.message.validate_note_lengths()?;
|
||||
// Sync updates while watching only the init nullifier.
|
||||
self.storage
|
||||
.key_chain_mut()
|
||||
@ -550,7 +549,7 @@ impl WalletCore {
|
||||
tx: &lee::privacy_preserving_transaction::PrivacyPreservingTransaction,
|
||||
acc_decode_mask: &[AccDecodeData],
|
||||
) -> Result<()> {
|
||||
let note_count = tx.message.validate_note_lengths()?;
|
||||
let note_count = tx.message.private_actions.len();
|
||||
anyhow::ensure!(
|
||||
note_count >= acc_decode_mask.len(),
|
||||
"Decode mask has {} entries but the transaction has {note_count} notes",
|
||||
@ -655,12 +654,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
|
||||
@ -785,7 +782,6 @@ impl WalletCore {
|
||||
let LeeTransaction::PrivacyPreserving(pp_tx) = &tx else {
|
||||
continue;
|
||||
};
|
||||
pp_tx.message.validate_note_lengths()?;
|
||||
// Eagerly decrypt note updates using expected nullifiers.
|
||||
let handled = self
|
||||
.storage
|
||||
@ -824,18 +820,19 @@ impl WalletCore {
|
||||
&key_chain.viewing_public_key,
|
||||
);
|
||||
message
|
||||
.encrypted_private_post_states
|
||||
.private_actions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(move |(ciph_id, encrypted_data)| {
|
||||
.filter(move |(ciph_id, action)| {
|
||||
// If we have not decrypted the update using the nullifiers,
|
||||
// the note may be an initialized one, for which we should
|
||||
// scan.
|
||||
!handled.contains(ciph_id) && encrypted_data.view_tag == view_tag
|
||||
!handled.contains(ciph_id)
|
||||
&& action.encrypted_post_state.view_tag == view_tag
|
||||
})
|
||||
.filter_map(move |(ciph_id, encrypted_data)| {
|
||||
let shared_secret =
|
||||
key_chain.calculate_shared_secret_receiver(&encrypted_data.epk)?;
|
||||
.filter_map(move |(ciph_id, action)| {
|
||||
let shared_secret = key_chain
|
||||
.calculate_shared_secret_receiver(&action.encrypted_post_state.epk)?;
|
||||
|
||||
decrypt_note_at(message, ciph_id, &shared_secret).map(|(kind, res_acc)| {
|
||||
let npk = &key_chain.nullifier_public_key;
|
||||
@ -892,16 +889,14 @@ impl WalletCore {
|
||||
for (account_id, npk, vpk, vsk, nsk) in shared_keys {
|
||||
let view_tag = EncryptedAccountData::compute_view_tag(&npk, &vpk);
|
||||
|
||||
for (ciph_id, encrypted_data) in
|
||||
message.encrypted_private_post_states.iter().enumerate()
|
||||
{
|
||||
for (ciph_id, action) in message.private_actions.iter().enumerate() {
|
||||
// If already decrypted or the tag does not match, skip.
|
||||
if handled.contains(&ciph_id) || encrypted_data.view_tag != view_tag {
|
||||
if handled.contains(&ciph_id) || action.encrypted_post_state.view_tag != view_tag {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(shared_secret) =
|
||||
SharedSecretKey::decapsulate(&encrypted_data.epk, &vsk.d, &vsk.z)
|
||||
SharedSecretKey::decapsulate(&action.encrypted_post_state.epk, &vsk.d, &vsk.z)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@ -938,9 +933,9 @@ fn decrypt_note_at(
|
||||
secret: &SharedSecretKey,
|
||||
) -> Option<(lee_core::PrivateAccountKind, Account)> {
|
||||
lee_core::EncryptionScheme::decrypt(
|
||||
&message.encrypted_private_post_states[i].ciphertext,
|
||||
&message.private_actions[i].encrypted_post_state.ciphertext,
|
||||
secret,
|
||||
&message.new_nullifiers[i].0,
|
||||
&message.private_actions[i].nullifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -390,9 +390,9 @@ impl UserKeyChain {
|
||||
index: &mut NullifierIndex,
|
||||
) -> HashSet<usize> {
|
||||
let mut handled = HashSet::new();
|
||||
for (i, (old_nullifier, _)) in message.new_nullifiers.iter().enumerate() {
|
||||
for (i, action) in message.private_actions.iter().enumerate() {
|
||||
// Get the nullifier information if awaiting the nullifier.
|
||||
let Some(account_id) = index.account_for(old_nullifier) else {
|
||||
let Some(account_id) = index.account_for(&action.nullifier) else {
|
||||
continue;
|
||||
};
|
||||
// Try decrypting the commitment connected to the nullifier and get the next
|
||||
@ -400,7 +400,7 @@ impl UserKeyChain {
|
||||
if let Some(new_nullifier) = self.apply_nullifier_update(account_id, message, i) {
|
||||
// Update the index to await for the new state of the account, i.e.
|
||||
// the new nullifier.
|
||||
index.update(old_nullifier, new_nullifier, account_id);
|
||||
index.update(&action.nullifier, new_nullifier, account_id);
|
||||
// Record that this nullifier's position can be skipped for scanning.
|
||||
handled.insert(i);
|
||||
}
|
||||
@ -416,7 +416,7 @@ impl UserKeyChain {
|
||||
message: &Message,
|
||||
i: usize,
|
||||
) -> Option<Nullifier> {
|
||||
let encrypted = &message.encrypted_private_post_states[i];
|
||||
let encrypted = &message.private_actions[i].encrypted_post_state;
|
||||
|
||||
let (nsk, secret, is_shared) = if let Some(entry) = self.shared_private_account(account_id)
|
||||
{
|
||||
@ -460,10 +460,9 @@ impl UserKeyChain {
|
||||
&acc.key_chain.private_key_holder.nullifier_secret_key,
|
||||
)
|
||||
});
|
||||
message
|
||||
.new_nullifiers
|
||||
.iter()
|
||||
.position(|(nullifier, _)| *nullifier == init || Some(nullifier) == update.as_ref())
|
||||
message.private_actions.iter().position(|action| {
|
||||
action.nullifier == init || Some(&action.nullifier) == update.as_ref()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_imported_public_account(&mut self, private_key: lee::PrivateKey) {
|
||||
@ -876,7 +875,7 @@ impl Default for UserKeyChain {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lee_core::{EncryptionScheme, encryption::EncryptedAccountData};
|
||||
use lee_core::{EncryptionScheme, PrivateAction, encryption::EncryptedAccountData};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -921,9 +920,12 @@ mod tests {
|
||||
);
|
||||
|
||||
let message = Message {
|
||||
encrypted_private_post_states: vec![note],
|
||||
new_commitments: vec![new_commitment],
|
||||
new_nullifiers: vec![(old_nullifier, [0; 32])],
|
||||
private_actions: vec![PrivateAction {
|
||||
nullifier: old_nullifier,
|
||||
commitment: new_commitment,
|
||||
encrypted_post_state: note,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -985,9 +987,12 @@ mod tests {
|
||||
);
|
||||
let note = EncryptedAccountData::new(ciphertext, &npk, &vpk, epk);
|
||||
let message = Message {
|
||||
encrypted_private_post_states: vec![note],
|
||||
new_commitments: vec![new_commitment],
|
||||
new_nullifiers: vec![(old_nullifier, [0; 32])],
|
||||
private_actions: vec![PrivateAction {
|
||||
nullifier: old_nullifier,
|
||||
commitment: new_commitment,
|
||||
encrypted_post_state: note,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -1047,9 +1052,12 @@ mod tests {
|
||||
);
|
||||
let note = EncryptedAccountData::new(ciphertext, &npk, &vpk, epk);
|
||||
Message {
|
||||
encrypted_private_post_states: vec![note],
|
||||
new_commitments: vec![commitment],
|
||||
new_nullifiers: vec![(spent, [0; 32])],
|
||||
private_actions: vec![PrivateAction {
|
||||
nullifier: spent,
|
||||
commitment,
|
||||
encrypted_post_state: note,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
@ -1110,7 +1118,10 @@ mod tests {
|
||||
&[9; 32],
|
||||
);
|
||||
let message = Message {
|
||||
new_nullifiers: vec![(unindexed, [0; 32])],
|
||||
private_actions: vec![PrivateAction {
|
||||
nullifier: unindexed,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user