mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-18 06:59:49 +00:00
Merge pull request #523 from logos-blockchain/artem/bind-discovery-to-journal
fix: bind the EncryptedAccountData to instance
This commit is contained in:
commit
68062b33c7
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.
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.
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.
@ -11,7 +11,7 @@ use lee::{
|
||||
privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
|
||||
};
|
||||
use lee_core::{
|
||||
InputAccountIdentity, NullifierPublicKey,
|
||||
EncryptedAccountData, InputAccountIdentity, NullifierPublicKey,
|
||||
account::AccountWithMetadata,
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
@ -665,9 +665,9 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
|
||||
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
|
||||
let nsk: lee_core::NullifierSecretKey = [3; 32];
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let _vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
|
||||
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
|
||||
let ssk = SharedSecretKey([55_u8; 32]);
|
||||
let _epk = EphemeralPublicKey(vec![55_u8; 1088]);
|
||||
let epk = EphemeralPublicKey(vec![55_u8; 1088]);
|
||||
let attacker_vault_id = {
|
||||
let seed = vault_core::compute_vault_seed(attacker_id);
|
||||
AccountId::for_private_pda(&vault_program_id, &seed, &npk, 1337)
|
||||
@ -712,6 +712,8 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &vpk),
|
||||
npk,
|
||||
ssk,
|
||||
identifier: 1337,
|
||||
|
||||
@ -150,7 +150,6 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
let message = privacy_preserving_transaction::Message::try_from_circuit_output(
|
||||
vec![bridge_account_id, recipient_vault_id],
|
||||
vec![bridge_pre.account.nonce, vault_pre.account.nonce],
|
||||
vec![],
|
||||
output,
|
||||
)
|
||||
.context("Failed to build privacy-preserving bridge deposit message")?;
|
||||
|
||||
@ -23,7 +23,7 @@ use lee::{
|
||||
program::Program,
|
||||
};
|
||||
use lee_core::{
|
||||
InputAccountIdentity, NullifierPublicKey,
|
||||
EncryptedAccountData, InputAccountIdentity, NullifierPublicKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::ViewingPublicKey,
|
||||
program::PdaSeed,
|
||||
@ -74,6 +74,8 @@ async fn fund_private_pda(
|
||||
let account_identities = vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &vpk),
|
||||
npk,
|
||||
ssk,
|
||||
identifier,
|
||||
@ -89,13 +91,9 @@ 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],
|
||||
vec![(npk, vpk, epk)],
|
||||
output,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("message build 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 witness_set = WitnessSet::for_message(&message, proof, &[sender_sk]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
@ -23,7 +23,7 @@ use lee::{
|
||||
public_transaction as putx,
|
||||
};
|
||||
use lee_core::{
|
||||
InputAccountIdentity, MembershipProof, NullifierPublicKey,
|
||||
EncryptedAccountData, InputAccountIdentity, MembershipProof, NullifierPublicKey,
|
||||
account::{AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::ViewingPublicKey,
|
||||
};
|
||||
@ -301,12 +301,16 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: sender_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&sender_npk, &sender_vpk),
|
||||
ssk: sender_ss,
|
||||
nsk: sender_nsk,
|
||||
membership_proof: proof,
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: recipient_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&recipient_npk, &recipient_vpk),
|
||||
npk: recipient_npk,
|
||||
ssk: recipient_ss,
|
||||
identifier: 0,
|
||||
@ -315,16 +319,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
&program.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = pptx::message::Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
(sender_npk, sender_vpk, sender_epk),
|
||||
(recipient_npk, recipient_vpk, recipient_epk),
|
||||
],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = pptx::message::Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
let witness_set = pptx::witness_set::WitnessSet::for_message(&message, proof, &[]);
|
||||
pptx::PrivacyPreservingTransaction::new(message, witness_set)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::{
|
||||
Commitment, CommitmentSetDigest, Identifier, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::Ciphertext,
|
||||
encryption::{EncryptedAccountData, EphemeralPublicKey, ViewTag},
|
||||
program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow},
|
||||
};
|
||||
|
||||
@ -33,6 +33,8 @@ pub enum InputAccountIdentity {
|
||||
/// `AccountId::for_regular_private_account(&NullifierPublicKey::from(nsk), identifier)` and
|
||||
/// matched against `pre_state.account_id`.
|
||||
PrivateAuthorizedInit {
|
||||
epk: EphemeralPublicKey,
|
||||
view_tag: ViewTag,
|
||||
ssk: SharedSecretKey,
|
||||
nsk: NullifierSecretKey,
|
||||
identifier: Identifier,
|
||||
@ -40,6 +42,8 @@ pub enum InputAccountIdentity {
|
||||
/// Update of an authorized standalone private account: existing on-chain commitment, with
|
||||
/// membership proof.
|
||||
PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey,
|
||||
view_tag: ViewTag,
|
||||
ssk: SharedSecretKey,
|
||||
nsk: NullifierSecretKey,
|
||||
membership_proof: MembershipProof,
|
||||
@ -48,6 +52,8 @@ pub enum InputAccountIdentity {
|
||||
/// Init of a standalone private account the caller does not own (e.g. a recipient who
|
||||
/// doesn't yet exist on chain). No `nsk`, no membership proof.
|
||||
PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey,
|
||||
view_tag: ViewTag,
|
||||
npk: NullifierPublicKey,
|
||||
ssk: SharedSecretKey,
|
||||
identifier: Identifier,
|
||||
@ -57,6 +63,8 @@ pub enum InputAccountIdentity {
|
||||
/// PDA within the `(program_id, seed, npk)` family: `AccountId::for_private_pda` uses it
|
||||
/// as the 4th input.
|
||||
PrivatePdaInit {
|
||||
epk: EphemeralPublicKey,
|
||||
view_tag: ViewTag,
|
||||
npk: NullifierPublicKey,
|
||||
ssk: SharedSecretKey,
|
||||
identifier: Identifier,
|
||||
@ -72,6 +80,8 @@ pub enum InputAccountIdentity {
|
||||
/// from `nsk`. Authorization may be established upstream by a caller `pda_seeds` match or a
|
||||
/// previously-seen authorization in a chained call.
|
||||
PrivatePdaUpdate {
|
||||
epk: EphemeralPublicKey,
|
||||
view_tag: ViewTag,
|
||||
ssk: SharedSecretKey,
|
||||
nsk: NullifierSecretKey,
|
||||
membership_proof: MembershipProof,
|
||||
@ -123,7 +133,7 @@ impl InputAccountIdentity {
|
||||
pub struct PrivacyPreservingCircuitOutput {
|
||||
pub public_pre_states: Vec<AccountWithMetadata>,
|
||||
pub public_post_states: Vec<Account>,
|
||||
pub ciphertexts: Vec<Ciphertext>,
|
||||
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
|
||||
pub new_commitments: Vec<Commitment>,
|
||||
pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>,
|
||||
pub block_validity_window: BlockValidityWindow,
|
||||
@ -148,6 +158,7 @@ mod tests {
|
||||
use crate::{
|
||||
Commitment, Nullifier,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||
encryption::Ciphertext,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -181,7 +192,11 @@ mod tests {
|
||||
data: b"post state data".to_vec().try_into().unwrap(),
|
||||
nonce: Nonce(0xFFFF_FFFF_FFFF_FFFF),
|
||||
}],
|
||||
ciphertexts: vec![Ciphertext(vec![255, 255, 1, 1, 2, 2])],
|
||||
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(),
|
||||
|
||||
@ -6,7 +6,7 @@ use chacha20::{
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "host")]
|
||||
pub use shared_key_derivation::{EphemeralPublicKey, MlKem768EncapsulationKey, ViewingPublicKey};
|
||||
pub use shared_key_derivation::{MlKem768EncapsulationKey, ViewingPublicKey};
|
||||
|
||||
use crate::{Commitment, account::Account, program::PrivateAccountKind};
|
||||
#[cfg(feature = "host")]
|
||||
@ -17,6 +17,11 @@ pub type Scalar = [u8; 32];
|
||||
#[derive(Serialize, Deserialize, Clone, Copy)]
|
||||
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 1088 bytes for ML-KEM-768.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct EphemeralPublicKey(pub Vec<u8>);
|
||||
|
||||
pub struct EncryptionScheme;
|
||||
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
@ -36,6 +41,45 @@ impl std::fmt::Debug for Ciphertext {
|
||||
}
|
||||
}
|
||||
|
||||
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))]
|
||||
pub struct EncryptedAccountData {
|
||||
pub ciphertext: Ciphertext,
|
||||
pub epk: EphemeralPublicKey,
|
||||
pub view_tag: ViewTag,
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl EncryptedAccountData {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
ciphertext: Ciphertext,
|
||||
npk: &crate::NullifierPublicKey,
|
||||
vpk: &ViewingPublicKey,
|
||||
epk: EphemeralPublicKey,
|
||||
) -> Self {
|
||||
let view_tag = Self::compute_view_tag(npk, vpk);
|
||||
Self {
|
||||
ciphertext,
|
||||
epk,
|
||||
view_tag,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || npk || vpk).
|
||||
#[must_use]
|
||||
pub fn compute_view_tag(npk: &crate::NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(b"/LEE/v0.3/ViewTag/");
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
bytes.extend_from_slice(vpk.to_bytes());
|
||||
Impl::hash_bytes(&bytes).as_bytes()[0]
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptionScheme {
|
||||
#[must_use]
|
||||
pub fn encrypt(
|
||||
|
||||
@ -2,12 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use ml_kem::{Decapsulate as _, Encapsulate as _, KeyExport as _, Seed};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::SharedSecretKey;
|
||||
|
||||
/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the
|
||||
/// former ECDH ephemeral public key. Always 1088 bytes for ML-KEM-768.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct EphemeralPublicKey(pub Vec<u8>);
|
||||
use crate::{EphemeralPublicKey, SharedSecretKey};
|
||||
|
||||
/// ML-KEM-768 encapsulation key bytes (1184 bytes, opaque to this crate).
|
||||
#[derive(
|
||||
|
||||
@ -10,7 +10,9 @@ pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
compute_digest_for_path,
|
||||
};
|
||||
pub use encryption::{EncryptionScheme, SharedSecretKey};
|
||||
pub use encryption::{
|
||||
EncryptedAccountData, EncryptionScheme, EphemeralPublicKey, SharedSecretKey, ViewTag,
|
||||
};
|
||||
pub use nullifier::{Identifier, Nullifier, NullifierPublicKey, NullifierSecretKey};
|
||||
pub use program::PrivateAccountKind;
|
||||
|
||||
|
||||
@ -178,8 +178,8 @@ mod tests {
|
||||
#![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")]
|
||||
|
||||
use lee_core::{
|
||||
Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier,
|
||||
PrivacyPreservingCircuitOutput, SharedSecretKey,
|
||||
Commitment, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme,
|
||||
EphemeralPublicKey, Nullifier, PrivacyPreservingCircuitOutput, SharedSecretKey,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
program::{PdaSeed, PrivateAccountKind},
|
||||
};
|
||||
@ -201,7 +201,7 @@ mod tests {
|
||||
idx: usize,
|
||||
) -> PrivateAccountKind {
|
||||
let (kind, _) = EncryptionScheme::decrypt(
|
||||
&output.ciphertexts[idx],
|
||||
&output.encrypted_private_post_states[idx].ciphertext,
|
||||
ssk,
|
||||
&output.new_commitments[idx],
|
||||
u32::try_from(idx).expect("idx fits in u32"),
|
||||
@ -268,6 +268,11 @@ mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -285,10 +290,10 @@ mod tests {
|
||||
assert_eq!(sender_post, expected_sender_post);
|
||||
assert_eq!(output.new_commitments.len(), 1);
|
||||
assert_eq!(output.new_nullifiers.len(), 1);
|
||||
assert_eq!(output.ciphertexts.len(), 1);
|
||||
assert_eq!(output.encrypted_private_post_states.len(), 1);
|
||||
|
||||
let (_identifier, recipient_post) = EncryptionScheme::decrypt(
|
||||
&output.ciphertexts[0],
|
||||
&output.encrypted_private_post_states[0].ciphertext,
|
||||
&shared_secret,
|
||||
&output.new_commitments[0],
|
||||
0,
|
||||
@ -367,6 +372,11 @@ mod tests {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret_1,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: commitment_set
|
||||
@ -375,6 +385,11 @@ mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: shared_secret_2,
|
||||
identifier: 0,
|
||||
@ -389,10 +404,10 @@ mod tests {
|
||||
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.ciphertexts.len(), 2);
|
||||
assert_eq!(output.encrypted_private_post_states.len(), 2);
|
||||
|
||||
let (_identifier, sender_post) = EncryptionScheme::decrypt(
|
||||
&output.ciphertexts[0],
|
||||
&output.encrypted_private_post_states[0].ciphertext,
|
||||
&shared_secret_1,
|
||||
&expected_new_commitments[0],
|
||||
0,
|
||||
@ -401,7 +416,7 @@ mod tests {
|
||||
assert_eq!(sender_post, expected_private_account_1);
|
||||
|
||||
let (_identifier, recipient_post) = EncryptionScheme::decrypt(
|
||||
&output.ciphertexts[1],
|
||||
&output.encrypted_private_post_states[1].ciphertext,
|
||||
&shared_secret_2,
|
||||
&expected_new_commitments[1],
|
||||
1,
|
||||
@ -443,6 +458,11 @@ mod tests {
|
||||
vec![pre],
|
||||
instruction,
|
||||
vec![InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&account_keys.npk(),
|
||||
&account_keys.vpk(),
|
||||
),
|
||||
npk: account_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -472,6 +492,8 @@ mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction(seed).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier,
|
||||
@ -519,6 +541,8 @@ mod tests {
|
||||
vec![pda_pre],
|
||||
instruction,
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret_pda,
|
||||
identifier: 0,
|
||||
@ -572,6 +596,8 @@ mod tests {
|
||||
instruction,
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret_pda,
|
||||
identifier: 0,
|
||||
@ -629,6 +655,11 @@ mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&shared_npk,
|
||||
&shared_keys.vpk(),
|
||||
),
|
||||
npk: shared_npk,
|
||||
ssk: shared_secret,
|
||||
identifier: shared_identifier,
|
||||
@ -658,6 +689,8 @@ mod tests {
|
||||
Program::serialize_instruction(authenticated_transfer_core::Instruction::Initialize)
|
||||
.unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()),
|
||||
ssk,
|
||||
nsk: keys.nsk,
|
||||
identifier,
|
||||
@ -702,6 +735,8 @@ mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()),
|
||||
npk: keys.npk(),
|
||||
ssk,
|
||||
identifier,
|
||||
@ -746,6 +781,8 @@ mod tests {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&keys.npk(), &keys.vpk()),
|
||||
ssk,
|
||||
nsk: keys.nsk,
|
||||
membership_proof: commitment_set.get_proof_for(&commitment).unwrap(),
|
||||
@ -800,6 +837,8 @@ mod tests {
|
||||
Program::serialize_instruction((seed, 1_u128, auth_transfer_id, false)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
ssk,
|
||||
nsk: keys.nsk,
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
@ -838,6 +877,8 @@ mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction(seed).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: 99,
|
||||
@ -881,6 +922,8 @@ mod tests {
|
||||
Program::serialize_instruction((seed, 1_u128, auth_transfer_id, false)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
ssk,
|
||||
nsk: keys.nsk,
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
|
||||
@ -1,52 +1,16 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitOutput,
|
||||
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, Nonce},
|
||||
encryption::{Ciphertext, EphemeralPublicKey, ViewingPublicKey},
|
||||
program::{BlockValidityWindow, TimestampValidityWindow},
|
||||
};
|
||||
pub use lee_core::{EncryptedAccountData, ViewTag};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
|
||||
use crate::{AccountId, error::LeeError};
|
||||
|
||||
const PREFIX: &[u8; 32] = b"/LEE/v0.3/Message/Privacy/\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
pub type ViewTag = u8;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct EncryptedAccountData {
|
||||
pub ciphertext: Ciphertext,
|
||||
pub epk: EphemeralPublicKey,
|
||||
pub view_tag: ViewTag,
|
||||
}
|
||||
|
||||
impl EncryptedAccountData {
|
||||
fn new(
|
||||
ciphertext: Ciphertext,
|
||||
npk: &NullifierPublicKey,
|
||||
vpk: &ViewingPublicKey,
|
||||
epk: EphemeralPublicKey,
|
||||
) -> Self {
|
||||
let view_tag = Self::compute_view_tag(npk, vpk);
|
||||
Self {
|
||||
ciphertext,
|
||||
epk,
|
||||
view_tag,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || Npk || vpk).
|
||||
#[must_use]
|
||||
pub fn compute_view_tag(npk: &NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"/LEE/v0.3/ViewTag/");
|
||||
hasher.update(npk.to_byte_array());
|
||||
hasher.update(vpk.to_bytes());
|
||||
let digest: [u8; 32] = hasher.finalize().into();
|
||||
digest[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Message {
|
||||
pub public_account_ids: Vec<AccountId>,
|
||||
@ -92,28 +56,13 @@ impl Message {
|
||||
pub fn try_from_circuit_output(
|
||||
public_account_ids: Vec<AccountId>,
|
||||
nonces: Vec<Nonce>,
|
||||
public_keys: Vec<(NullifierPublicKey, ViewingPublicKey, EphemeralPublicKey)>,
|
||||
output: PrivacyPreservingCircuitOutput,
|
||||
) -> Result<Self, LeeError> {
|
||||
if public_keys.len() != output.ciphertexts.len() {
|
||||
return Err(LeeError::InvalidInput(
|
||||
"Ephemeral public keys and ciphertexts length mismatch".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let encrypted_private_post_states = output
|
||||
.ciphertexts
|
||||
.into_iter()
|
||||
.zip(public_keys)
|
||||
.map(|(ciphertext, (npk, vpk, epk))| {
|
||||
EncryptedAccountData::new(ciphertext, &npk, &vpk, epk)
|
||||
})
|
||||
.collect();
|
||||
Ok(Self {
|
||||
public_account_ids,
|
||||
nonces,
|
||||
public_post_states: output.public_post_states,
|
||||
encrypted_private_post_states,
|
||||
encrypted_private_post_states: output.encrypted_private_post_states,
|
||||
new_commitments: output.new_commitments,
|
||||
new_nullifiers: output.new_nullifiers,
|
||||
block_validity_window: output.block_validity_window,
|
||||
|
||||
@ -418,8 +418,8 @@ pub mod tests {
|
||||
|
||||
use authenticated_transfer_core::Instruction as AuthTransferInstruction;
|
||||
use lee_core::{
|
||||
BlockId, Commitment, InputAccountIdentity, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, SharedSecretKey, Timestamp,
|
||||
BlockId, Commitment, EncryptedAccountData, InputAccountIdentity, Nullifier,
|
||||
NullifierPublicKey, NullifierSecretKey, SharedSecretKey, Timestamp,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
program::{
|
||||
@ -1418,6 +1418,11 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -1430,7 +1435,6 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![sender_keys.account_id()],
|
||||
vec![sender_nonce],
|
||||
vec![(recipient_keys.npk(), recipient_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -1471,6 +1475,11 @@ pub mod tests {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: epk_1,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret_1,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -1479,6 +1488,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: epk_2,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: shared_secret_2,
|
||||
identifier: 0,
|
||||
@ -1488,16 +1502,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
(sender_keys.npk(), sender_keys.vpk(), epk_1),
|
||||
(recipient_keys.npk(), recipient_keys.vpk(), epk_2),
|
||||
],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
|
||||
@ -1536,6 +1541,11 @@ pub mod tests {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -1549,13 +1559,8 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![*recipient_account_id],
|
||||
vec![],
|
||||
vec![(sender_keys.npk(), sender_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![*recipient_account_id], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
|
||||
@ -1672,6 +1677,79 @@ pub mod tests {
|
||||
assert!(state.private_state.1.contains(&expected_new_nullifier));
|
||||
}
|
||||
|
||||
fn valid_private_transfer_tx_and_state() -> (V03State, PrivacyPreservingTransaction) {
|
||||
let sender_keys = test_private_account_keys_1();
|
||||
let sender_private_account = Account {
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
balance: 100,
|
||||
nonce: Nonce(0xdead_beef),
|
||||
..Account::default()
|
||||
};
|
||||
let recipient_keys = test_private_account_keys_2();
|
||||
let state = V03State::new_with_genesis_accounts(&[], vec![], 0)
|
||||
.with_private_account(&sender_keys, &sender_private_account);
|
||||
let tx = private_balance_transfer_for_tests(
|
||||
&sender_keys,
|
||||
&sender_private_account,
|
||||
&recipient_keys,
|
||||
37,
|
||||
&state,
|
||||
);
|
||||
(state, tx)
|
||||
}
|
||||
|
||||
/// After a valid fully-private tx is proven, tampering with a note's epk should
|
||||
/// make the shielding proof invalid.
|
||||
#[test]
|
||||
fn privacy_tampered_epk_is_rejected() {
|
||||
use crate::validated_state_diff::ValidatedStateDiff;
|
||||
|
||||
let (state, mut tx) = valid_private_transfer_tx_and_state();
|
||||
|
||||
// Baseline: the untampered tx verifies
|
||||
assert!(
|
||||
ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0).is_ok(),
|
||||
"the unmodified private transfer must verify"
|
||||
);
|
||||
|
||||
// Flip a byte of the first note's epk
|
||||
tx.message.encrypted_private_post_states[0].epk.0[0] ^= 0xFF;
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0),
|
||||
Err(LeeError::InvalidPrivacyPreservingProof)
|
||||
),
|
||||
"a tampered epk must be rejected by proof verification"
|
||||
);
|
||||
}
|
||||
|
||||
/// After a valid fully-private tx is proven, tampering with a note's view tag should
|
||||
/// make the shielding proof invalid.
|
||||
#[test]
|
||||
fn privacy_tampered_view_tag_is_rejected() {
|
||||
use crate::validated_state_diff::ValidatedStateDiff;
|
||||
|
||||
let (state, mut tx) = valid_private_transfer_tx_and_state();
|
||||
|
||||
// Baseline: the untampered tx verifies.
|
||||
assert!(
|
||||
ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0).is_ok(),
|
||||
"the unmodified private transfer must verify"
|
||||
);
|
||||
|
||||
// Flip the first note's view_tag
|
||||
tx.message.encrypted_private_post_states[0].view_tag ^= 0xFF;
|
||||
|
||||
assert!(
|
||||
matches!(
|
||||
ValidatedStateDiff::from_privacy_preserving_transaction(&tx, &state, 1, 0),
|
||||
Err(LeeError::InvalidPrivacyPreservingProof)
|
||||
),
|
||||
"a tampered view_tag must be rejected by proof verification"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_from_privacy_preserving_transaction_deshielded() {
|
||||
let sender_keys = test_private_account_keys_1();
|
||||
@ -2034,6 +2112,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2045,6 +2128,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2090,6 +2178,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2101,6 +2194,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2146,6 +2244,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2157,6 +2260,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2202,6 +2310,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2213,6 +2326,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2258,6 +2376,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2269,6 +2392,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2312,6 +2440,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
@ -2323,6 +2456,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
@ -2368,6 +2506,8 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2401,6 +2541,8 @@ pub mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction(seed).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2412,7 +2554,7 @@ pub mod tests {
|
||||
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.ciphertexts.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());
|
||||
}
|
||||
@ -2442,6 +2584,8 @@ pub mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction(seed).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk_b, &keys_b.vpk()),
|
||||
npk: npk_b,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2479,6 +2623,8 @@ pub mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction((seed, seed, callee_id)).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2519,6 +2665,8 @@ pub mod tests {
|
||||
vec![pre_state],
|
||||
Program::serialize_instruction((claim_seed, wrong_delegated_seed, callee_id)).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2558,12 +2706,16 @@ pub mod tests {
|
||||
Program::serialize_instruction(seed).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&keys_a.npk(), &keys_a.vpk()),
|
||||
npk: keys_a.npk(),
|
||||
ssk: shared_a,
|
||||
identifier: u128::MAX,
|
||||
seed: None,
|
||||
},
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&keys_b.npk(), &keys_b.vpk()),
|
||||
npk: keys_b.npk(),
|
||||
ssk: shared_b,
|
||||
identifier: u128::MAX,
|
||||
@ -2606,6 +2758,8 @@ pub mod tests {
|
||||
vec![owned_pre_state],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::PrivatePdaInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&npk, &keys.vpk()),
|
||||
npk,
|
||||
ssk: shared_secret,
|
||||
identifier: u128::MAX,
|
||||
@ -2694,12 +2848,22 @@ pub mod tests {
|
||||
Program::serialize_instruction(100_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (1, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (1, vec![]),
|
||||
@ -3045,6 +3209,11 @@ pub mod tests {
|
||||
.unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -3058,13 +3227,9 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_account_id],
|
||||
vec![Nonce(0)],
|
||||
vec![(sender_keys.npk(), sender_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![recipient_account_id], vec![Nonce(0)], output)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_private_key]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
@ -3171,6 +3336,11 @@ pub mod tests {
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: to_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&to_keys.npk(),
|
||||
&to_keys.vpk(),
|
||||
),
|
||||
ssk: to_ss,
|
||||
nsk: from_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -3179,6 +3349,11 @@ pub mod tests {
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: from_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&from_keys.npk(),
|
||||
&from_keys.vpk(),
|
||||
),
|
||||
ssk: from_ss,
|
||||
nsk: to_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -3191,16 +3366,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![
|
||||
(to_keys.npk(), to_keys.vpk(), to_epk),
|
||||
(from_keys.npk(), from_keys.vpk(), from_epk),
|
||||
],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
let transaction = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
|
||||
@ -3448,6 +3614,11 @@ pub mod tests {
|
||||
vec![authorized_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&private_keys.npk(),
|
||||
&private_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: private_keys.nsk,
|
||||
identifier: 0,
|
||||
@ -3457,13 +3628,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
|
||||
// Create message from circuit output
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![(private_keys.npk(), private_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
|
||||
@ -3496,6 +3661,11 @@ pub mod tests {
|
||||
vec![unauthorized_account],
|
||||
Program::serialize_instruction(0_u128).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateUnauthorized {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&private_keys.npk(),
|
||||
&private_keys.vpk(),
|
||||
),
|
||||
npk: private_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -3504,13 +3674,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![(private_keys.npk(), private_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
@ -3548,6 +3712,11 @@ pub mod tests {
|
||||
vec![authorized_account.clone()],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&private_keys.npk(),
|
||||
&private_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret,
|
||||
nsk: private_keys.nsk,
|
||||
identifier: 0,
|
||||
@ -3556,13 +3725,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![(private_keys.npk(), private_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
let tx = PrivacyPreservingTransaction::new(message, witness_set);
|
||||
@ -3595,6 +3758,11 @@ pub mod tests {
|
||||
vec![account_metadata],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&private_keys.npk(),
|
||||
&private_keys.vpk(),
|
||||
),
|
||||
ssk: shared_secret2,
|
||||
nsk: private_keys.nsk,
|
||||
identifier: 0,
|
||||
@ -3672,6 +3840,11 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
@ -3699,6 +3872,11 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&sender_keys.npk(),
|
||||
&sender_keys.vpk(),
|
||||
),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
@ -3760,6 +3938,11 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: EphemeralPublicKey(Vec::new()),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&recipient_keys.npk(),
|
||||
&recipient_keys.vpk(),
|
||||
),
|
||||
ssk: recipient,
|
||||
nsk: recipient_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -3914,6 +4097,11 @@ pub mod tests {
|
||||
vec![pre],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateUnauthorized {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&account_keys.npk(),
|
||||
&account_keys.vpk(),
|
||||
),
|
||||
npk: account_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -3922,13 +4110,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![(account_keys.npk(), account_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
PrivacyPreservingTransaction::new(message, witness_set)
|
||||
@ -3983,6 +4165,11 @@ pub mod tests {
|
||||
vec![pre],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateUnauthorized {
|
||||
epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&account_keys.npk(),
|
||||
&account_keys.vpk(),
|
||||
),
|
||||
npk: account_keys.npk(),
|
||||
ssk: shared_secret,
|
||||
identifier: 0,
|
||||
@ -3991,13 +4178,7 @@ pub mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![],
|
||||
vec![],
|
||||
vec![(account_keys.npk(), account_keys.vpk(), epk)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(vec![], vec![], output).unwrap();
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
PrivacyPreservingTransaction::new(message, witness_set)
|
||||
@ -4546,6 +4727,11 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: alice_epk_0.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&alice_npk,
|
||||
&alice_keys.vpk(),
|
||||
),
|
||||
npk: alice_npk,
|
||||
ssk: alice_shared_0,
|
||||
identifier: 0,
|
||||
@ -4555,13 +4741,9 @@ pub mod tests {
|
||||
&auth_transfer.clone().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![funder_id],
|
||||
vec![funder_nonce],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0.clone())],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output)
|
||||
.unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
@ -4586,6 +4768,11 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk: alice_epk_1.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&alice_npk,
|
||||
&alice_keys.vpk(),
|
||||
),
|
||||
npk: alice_npk,
|
||||
ssk: alice_shared_1,
|
||||
identifier: 1,
|
||||
@ -4595,13 +4782,9 @@ pub mod tests {
|
||||
&auth_transfer.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![funder_id],
|
||||
vec![funder_nonce],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1.clone())],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![funder_id], vec![funder_nonce], output)
|
||||
.unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&funder_keys.signing_key]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
@ -4629,6 +4812,11 @@ pub mod tests {
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: alice_epk_0,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&alice_npk,
|
||||
&alice_keys.vpk(),
|
||||
),
|
||||
ssk: alice_shared_0,
|
||||
nsk: alice_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -4642,13 +4830,9 @@ pub mod tests {
|
||||
&spend_with_deps,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![Nonce(0)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![recipient_id], vec![Nonce(0)], output)
|
||||
.unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
@ -4670,6 +4854,11 @@ pub mod tests {
|
||||
Program::serialize_instruction((seed, amount, auth_transfer_id)).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: alice_epk_1,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&alice_npk,
|
||||
&alice_keys.vpk(),
|
||||
),
|
||||
ssk: alice_shared_1,
|
||||
nsk: alice_keys.nsk,
|
||||
membership_proof: state
|
||||
@ -4683,13 +4872,8 @@ pub mod tests {
|
||||
&spend_with_deps,
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![recipient_id], vec![], output).unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
@ -4732,6 +4916,11 @@ pub mod tests {
|
||||
vec![
|
||||
InputAccountIdentity::Public,
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: EphemeralPublicKey(vec![12_u8; 1088]),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&alice_npk,
|
||||
&alice_keys.vpk(),
|
||||
),
|
||||
nsk: alice_keys.nsk,
|
||||
ssk: alice_shared_1_refund,
|
||||
membership_proof: state
|
||||
@ -4744,17 +4933,9 @@ pub mod tests {
|
||||
&Program::authenticated_transfer_program().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![recipient_nonce],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey(vec![12_u8; 1088]),
|
||||
)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
let message =
|
||||
Message::try_from_circuit_output(vec![recipient_id], vec![recipient_nonce], output)
|
||||
.unwrap();
|
||||
let witness_set = WitnessSet::for_message(&message, proof, &[&recipient_signing_key]);
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(
|
||||
|
||||
@ -492,12 +492,7 @@ fn check_privacy_preserving_circuit_proof_is_valid(
|
||||
let output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states: public_pre_states.to_vec(),
|
||||
public_post_states: message.public_post_states.clone(),
|
||||
ciphertexts: message
|
||||
.encrypted_private_post_states
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|value| value.ciphertext)
|
||||
.collect(),
|
||||
encrypted_private_post_states: message.encrypted_private_post_states.clone(),
|
||||
new_commitments: message.new_commitments.clone(),
|
||||
new_nullifiers: message.new_nullifiers.clone(),
|
||||
block_validity_window: message.block_validity_window,
|
||||
@ -580,7 +575,7 @@ mod tests {
|
||||
#[test]
|
||||
fn privacy_malicious_programs_cannot_drain_public_victim() {
|
||||
use lee_core::{
|
||||
Commitment, InputAccountIdentity, SharedSecretKey,
|
||||
Commitment, EncryptedAccountData, InputAccountIdentity, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
};
|
||||
|
||||
@ -664,6 +659,11 @@ mod tests {
|
||||
// [2] recipient — first seen in authenticated_transfer's program_output.pre_states
|
||||
let account_identities = vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: attacker_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&attacker_keys.npk(),
|
||||
&attacker_keys.vpk(),
|
||||
),
|
||||
ssk: attacker_ssk,
|
||||
nsk: attacker_keys.nsk,
|
||||
membership_proof,
|
||||
@ -688,7 +688,6 @@ mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![victim_id, recipient_id],
|
||||
vec![], // no public signers, no nonces
|
||||
vec![(attacker_keys.npk(), attacker_keys.vpk(), attacker_epk)],
|
||||
circuit_output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -728,7 +727,7 @@ mod tests {
|
||||
#[test]
|
||||
fn privacy_malicious_programs_cannot_drain_private_victim() {
|
||||
use lee_core::{
|
||||
Commitment, InputAccountIdentity, SharedSecretKey,
|
||||
Commitment, EncryptedAccountData, InputAccountIdentity, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
};
|
||||
|
||||
@ -820,6 +819,11 @@ mod tests {
|
||||
// so PrivateAuthorizedUpdate is not an option.
|
||||
let account_identities = vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: attacker_epk,
|
||||
view_tag: EncryptedAccountData::compute_view_tag(
|
||||
&attacker_keys.npk(),
|
||||
&attacker_keys.vpk(),
|
||||
),
|
||||
ssk: attacker_ssk,
|
||||
nsk: attacker_keys.nsk,
|
||||
membership_proof,
|
||||
@ -845,7 +849,6 @@ mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![victim_id, recipient_id],
|
||||
vec![], // no public signers, no nonces
|
||||
vec![(attacker_keys.npk(), attacker_keys.vpk(), attacker_epk)],
|
||||
circuit_output,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -6,7 +6,7 @@ use lee_core::{
|
||||
Identifier, InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey,
|
||||
SharedSecretKey,
|
||||
account::{AccountWithMetadata, Nonce},
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
encryption::{EncryptedAccountData, EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
|
||||
use crate::{ExecutionFailureKind, WalletCore};
|
||||
@ -100,10 +100,7 @@ impl AccountIdentity {
|
||||
}
|
||||
|
||||
pub struct PrivateAccountKeys {
|
||||
pub npk: NullifierPublicKey,
|
||||
pub ssk: SharedSecretKey,
|
||||
pub vpk: ViewingPublicKey,
|
||||
pub epk: EphemeralPublicKey,
|
||||
}
|
||||
|
||||
enum State {
|
||||
@ -307,12 +304,7 @@ impl AccountManager {
|
||||
self.states
|
||||
.iter()
|
||||
.filter_map(|state| match state {
|
||||
State::Private(pre) => Some(PrivateAccountKeys {
|
||||
npk: pre.npk,
|
||||
ssk: pre.ssk,
|
||||
vpk: pre.vpk.clone(),
|
||||
epk: pre.epk.clone(),
|
||||
}),
|
||||
State::Private(pre) => Some(PrivateAccountKeys { ssk: pre.ssk }),
|
||||
State::Public { .. } | State::PublicKeycard { .. } => None,
|
||||
})
|
||||
.collect()
|
||||
@ -329,6 +321,8 @@ impl AccountManager {
|
||||
State::Public { .. } | State::PublicKeycard { .. } => InputAccountIdentity::Public,
|
||||
State::Private(pre) if pre.is_pda => match (pre.nsk, pre.proof.clone()) {
|
||||
(Some(nsk), Some(membership_proof)) => InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk: pre.epk.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk),
|
||||
ssk: pre.ssk,
|
||||
nsk,
|
||||
membership_proof,
|
||||
@ -336,6 +330,8 @@ impl AccountManager {
|
||||
seed: None,
|
||||
},
|
||||
_ => InputAccountIdentity::PrivatePdaInit {
|
||||
epk: pre.epk.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk),
|
||||
npk: pre.npk,
|
||||
ssk: pre.ssk,
|
||||
identifier: pre.identifier,
|
||||
@ -345,6 +341,8 @@ impl AccountManager {
|
||||
State::Private(pre) => match (pre.nsk, pre.proof.clone()) {
|
||||
(Some(nsk), Some(membership_proof)) => {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk: pre.epk.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk),
|
||||
ssk: pre.ssk,
|
||||
nsk,
|
||||
membership_proof,
|
||||
@ -352,11 +350,15 @@ impl AccountManager {
|
||||
}
|
||||
}
|
||||
(Some(nsk), None) => InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk: pre.epk.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk),
|
||||
ssk: pre.ssk,
|
||||
nsk,
|
||||
identifier: pre.identifier,
|
||||
},
|
||||
(None, _) => InputAccountIdentity::PrivateUnauthorized {
|
||||
epk: pre.epk.clone(),
|
||||
view_tag: EncryptedAccountData::compute_view_tag(&pre.npk, &pre.vpk),
|
||||
npk: pre.npk,
|
||||
ssk: pre.ssk,
|
||||
identifier: pre.identifier,
|
||||
|
||||
@ -587,10 +587,6 @@ impl WalletCore {
|
||||
lee::privacy_preserving_transaction::message::Message::try_from_circuit_output(
|
||||
acc_manager.public_account_ids(),
|
||||
acc_manager.public_account_nonces(),
|
||||
private_account_keys
|
||||
.iter()
|
||||
.map(|keys| (keys.npk, keys.vpk.clone(), keys.epk.clone()))
|
||||
.collect(),
|
||||
output,
|
||||
)?;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptionScheme, InputAccountIdentity,
|
||||
MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey,
|
||||
PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme,
|
||||
EphemeralPublicKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
|
||||
account::{Account, AccountId, Nonce},
|
||||
compute_digest_for_path,
|
||||
};
|
||||
@ -17,7 +17,7 @@ pub fn compute_circuit_output(
|
||||
let mut output = PrivacyPreservingCircuitOutput {
|
||||
public_pre_states: Vec::new(),
|
||||
public_post_states: Vec::new(),
|
||||
ciphertexts: Vec::new(),
|
||||
encrypted_private_post_states: Vec::new(),
|
||||
new_commitments: Vec::new(),
|
||||
new_nullifiers: Vec::new(),
|
||||
block_validity_window,
|
||||
@ -40,6 +40,8 @@ pub fn compute_circuit_output(
|
||||
output.public_post_states.push(post_state);
|
||||
}
|
||||
InputAccountIdentity::PrivateAuthorizedInit {
|
||||
epk,
|
||||
view_tag,
|
||||
ssk,
|
||||
nsk,
|
||||
identifier,
|
||||
@ -71,11 +73,15 @@ pub fn compute_circuit_output(
|
||||
&account_id,
|
||||
&PrivateAccountKind::Regular(*identifier),
|
||||
ssk,
|
||||
epk,
|
||||
*view_tag,
|
||||
new_nullifier,
|
||||
new_nonce,
|
||||
);
|
||||
}
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
epk,
|
||||
view_tag,
|
||||
ssk,
|
||||
nsk,
|
||||
membership_proof,
|
||||
@ -105,11 +111,15 @@ pub fn compute_circuit_output(
|
||||
&account_id,
|
||||
&PrivateAccountKind::Regular(*identifier),
|
||||
ssk,
|
||||
epk,
|
||||
*view_tag,
|
||||
new_nullifier,
|
||||
new_nonce,
|
||||
);
|
||||
}
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
epk,
|
||||
view_tag,
|
||||
npk,
|
||||
ssk,
|
||||
identifier,
|
||||
@ -140,11 +150,15 @@ pub fn compute_circuit_output(
|
||||
&account_id,
|
||||
&PrivateAccountKind::Regular(*identifier),
|
||||
ssk,
|
||||
epk,
|
||||
*view_tag,
|
||||
new_nullifier,
|
||||
new_nonce,
|
||||
);
|
||||
}
|
||||
InputAccountIdentity::PrivatePdaInit {
|
||||
epk,
|
||||
view_tag,
|
||||
npk: _,
|
||||
ssk,
|
||||
identifier,
|
||||
@ -187,11 +201,15 @@ pub fn compute_circuit_output(
|
||||
identifier: *identifier,
|
||||
},
|
||||
ssk,
|
||||
epk,
|
||||
*view_tag,
|
||||
new_nullifier,
|
||||
new_nonce,
|
||||
);
|
||||
}
|
||||
InputAccountIdentity::PrivatePdaUpdate {
|
||||
epk,
|
||||
view_tag,
|
||||
ssk,
|
||||
nsk,
|
||||
membership_proof,
|
||||
@ -231,6 +249,8 @@ pub fn compute_circuit_output(
|
||||
identifier: *identifier,
|
||||
},
|
||||
ssk,
|
||||
epk,
|
||||
*view_tag,
|
||||
new_nullifier,
|
||||
new_nonce,
|
||||
);
|
||||
@ -243,7 +263,7 @@ pub fn compute_circuit_output(
|
||||
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
reason = "All seven inputs are distinct concerns from the variant arms; bundling would be artificial"
|
||||
reason = "Inputs are distinct concerns from the variant arms; bundling would be artificial"
|
||||
)]
|
||||
fn emit_private_output(
|
||||
output: &mut PrivacyPreservingCircuitOutput,
|
||||
@ -252,6 +272,8 @@ fn emit_private_output(
|
||||
account_id: &AccountId,
|
||||
kind: &PrivateAccountKind,
|
||||
shared_secret: &SharedSecretKey,
|
||||
epk: &EphemeralPublicKey,
|
||||
view_tag: u8,
|
||||
new_nullifier: (Nullifier, CommitmentSetDigest),
|
||||
new_nonce: Nonce,
|
||||
) {
|
||||
@ -270,7 +292,13 @@ fn emit_private_output(
|
||||
);
|
||||
|
||||
output.new_commitments.push(commitment_post);
|
||||
output.ciphertexts.push(encrypted_account);
|
||||
output
|
||||
.encrypted_private_post_states
|
||||
.push(EncryptedAccountData {
|
||||
ciphertext: encrypted_account,
|
||||
epk: epk.clone(),
|
||||
view_tag,
|
||||
});
|
||||
*output_index = output_index
|
||||
.checked_add(1)
|
||||
.unwrap_or_else(|| panic!("Too many private accounts, output index overflow"));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user