This commit is contained in:
agureev 2026-06-29 22:34:25 +04:00
parent 5a23b54a6f
commit e4e90104a5
27 changed files with 449 additions and 396 deletions

View File

@ -156,11 +156,12 @@ async fn import_private_account() -> Result<()> {
let mut ctx = TestContext::new().await?; let mut ctx = TestContext::new().await?;
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id = lee::AccountId::from(( let account_id = lee::PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
0, 0,
)); )
.account_id();
let account = lee::Account { let account = lee::Account {
program_owner: Program::authenticated_transfer_program().id(), program_owner: Program::authenticated_transfer_program().id(),
balance: 777, balance: 777,
@ -217,11 +218,12 @@ async fn import_private_account_second_time_overrides_account_data() -> Result<(
let mut ctx = TestContext::new().await?; let mut ctx = TestContext::new().await?;
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id = lee::AccountId::from(( let account_id = lee::PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
0, 0,
)); )
.account_id();
let key_chain_json = let key_chain_json =
serde_json::to_string(&key_chain).context("Failed to serialize key chain")?; serde_json::to_string(&key_chain).context("Failed to serialize key chain")?;

View File

@ -7,8 +7,8 @@ use integration_tests::{
public_mention, verify_commitment_is_in_state, public_mention, verify_commitment_is_in_state,
}; };
use lee::{ use lee::{
AccountId, execute_and_prove, privacy_preserving_transaction::circuit::ProgramWithDependencies, AccountId, PrivateAddressPlaintext, execute_and_prove,
program::Program, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
}; };
use lee_core::{ use lee_core::{
InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata, InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata,
@ -599,14 +599,14 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
.await?; .await?;
// Both accounts must be discovered with the correct balances. // Both accounts must be discovered with the correct balances.
let account_id_1 = AccountId::for_regular_private_account(&npk, &vpk, identifier_1); let account_id_1 = PrivateAddressPlaintext::new(npk, vpk.clone(), identifier_1).account_id();
let acc_1 = ctx let acc_1 = ctx
.wallet() .wallet()
.get_account_private(account_id_1) .get_account_private(account_id_1)
.context("account for identifier 1 not found after sync")?; .context("account for identifier 1 not found after sync")?;
assert_eq!(acc_1.balance, 100); assert_eq!(acc_1.balance, 100);
let account_id_2 = AccountId::for_regular_private_account(&npk, &vpk, identifier_2); let account_id_2 = PrivateAddressPlaintext::new(npk, vpk.clone(), identifier_2).account_id();
let acc_2 = ctx let acc_2 = ctx
.wallet() .wallet()
.get_account_private(account_id_2) .get_account_private(account_id_2)
@ -667,7 +667,8 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap(); let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
let attacker_vault_id = { let attacker_vault_id = {
let seed = vault_core::compute_vault_seed(attacker_id); let seed = vault_core::compute_vault_seed(attacker_id);
AccountId::for_private_pda(&vault_program_id, &seed, &npk, &vpk, 1337) PrivateAddressPlaintext::new(npk, vpk.clone(), 1337)
.pda_account_id(&vault_program_id, &seed)
}; };
let amount: u128 = 1; let amount: u128 = 1;

View File

@ -13,7 +13,7 @@ use integration_tests::{
verify_commitment_is_in_state, verify_commitment_is_in_state,
}; };
use lee::{ use lee::{
AccountId, PrivacyPreservingTransaction, ProgramId, AccountId, PrivacyPreservingTransaction, PrivateAddressPlaintext, ProgramId,
privacy_preserving_transaction::{ privacy_preserving_transaction::{
circuit::{ProgramWithDependencies, execute_and_prove}, circuit::{ProgramWithDependencies, execute_and_prove},
message::Message, message::Message,
@ -51,8 +51,8 @@ async fn fund_private_pda(
amount: u128, amount: u128,
auth_transfer: &ProgramWithDependencies, auth_transfer: &ProgramWithDependencies,
) -> Result<()> { ) -> Result<()> {
let pda_account_id = let pda_account_id = PrivateAddressPlaintext::new(npk, vpk.clone(), identifier)
AccountId::for_private_pda(&authority_program_id, &seed, &npk, &vpk, identifier); .pda_account_id(&authority_program_id, &seed);
let sender_account = wallet let sender_account = wallet
.get_account_public(sender) .get_account_public(sender)
.await .await
@ -177,8 +177,10 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
let spend_program = let spend_program =
ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer)].into()); ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer)].into());
let alice_pda_0_id = AccountId::for_private_pda(&proxy_id, &seed, &alice_npk, &alice_vpk, 0); let alice_pda_0_id = PrivateAddressPlaintext::new(alice_npk, alice_vpk.clone(), 0)
let alice_pda_1_id = AccountId::for_private_pda(&proxy_id, &seed, &alice_npk, &alice_vpk, 1); .pda_account_id(&proxy_id, &seed);
let alice_pda_1_id = PrivateAddressPlaintext::new(alice_npk, alice_vpk.clone(), 1)
.pda_account_id(&proxy_id, &seed);
// Use two different public senders to avoid nonce conflicts between the back-to-back txs. // Use two different public senders to avoid nonce conflicts between the back-to-back txs.
let senders = ctx.existing_public_accounts(); let senders = ctx.existing_public_accounts();

View File

@ -16,7 +16,8 @@ use bytesize::ByteSize;
use common::transaction::LeeTransaction; use common::transaction::LeeTransaction;
use integration_tests::{TestContext, config::SequencerPartialConfig}; use integration_tests::{TestContext, config::SequencerPartialConfig};
use lee::{ use lee::{
Account, AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey, PublicTransaction, Account, AccountId, PrivacyPreservingTransaction, PrivateAddressPlaintext, PrivateKey,
PublicKey, PublicTransaction,
privacy_preserving_transaction::{self as pptx, circuit}, privacy_preserving_transaction::{self as pptx, circuit},
program::Program, program::Program,
public_transaction as putx, public_transaction as putx,
@ -265,7 +266,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
data: Data::default(), data: Data::default(),
}, },
true, true,
AccountId::for_regular_private_account(&sender_npk, &sender_vpk, 0), PrivateAddressPlaintext::new(sender_npk, sender_vpk.clone(), 0).account_id(),
); );
let recipient_nsk = [2; 32]; let recipient_nsk = [2; 32];
let recipient_vpk = ViewingPublicKey::from_seed(&[101_u8; 32], &[102_u8; 32]); let recipient_vpk = ViewingPublicKey::from_seed(&[101_u8; 32], &[102_u8; 32]);
@ -273,7 +274,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
let recipient_pre = AccountWithMetadata::new( let recipient_pre = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
AccountId::for_regular_private_account(&recipient_npk, &recipient_vpk, 0), PrivateAddressPlaintext::new(recipient_npk, recipient_vpk.clone(), 0).account_id(),
); );
let balance_to_move: u128 = 1; let balance_to_move: u128 = 1;

View File

@ -907,11 +907,9 @@ fn test_wallet_ffi_transfer_shielded() -> Result<()> {
let (to, to_keys) = unsafe { let (to, to_keys) = unsafe {
let mut out_keys = FfiPrivateAccountKeys::default(); let mut out_keys = FfiPrivateAccountKeys::default();
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys).unwrap(); wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys).unwrap();
let account_id = lee::AccountId::for_regular_private_account( let account_id =
&out_keys.npk(), lee::PrivateAddressPlaintext::new(out_keys.npk(), out_keys.vpk().unwrap(), 0_u128)
&out_keys.vpk().unwrap(), .account_id();
0_u128,
);
let to: FfiBytes32 = account_id.into(); let to: FfiBytes32 = account_id.into();
(to, out_keys) (to, out_keys)
}; };
@ -1048,11 +1046,9 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
let (to, to_keys) = unsafe { let (to, to_keys) = unsafe {
let mut out_keys = FfiPrivateAccountKeys::default(); let mut out_keys = FfiPrivateAccountKeys::default();
wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys).unwrap(); wallet_ffi_create_private_accounts_key(wallet_ffi_handle, &raw mut out_keys).unwrap();
let account_id = lee::AccountId::for_regular_private_account( let account_id =
&out_keys.npk(), lee::PrivateAddressPlaintext::new(out_keys.npk(), out_keys.vpk().unwrap(), 0_u128)
&out_keys.vpk().unwrap(), .account_id();
0_u128,
);
let to: FfiBytes32 = account_id.into(); let to: FfiBytes32 = account_id.into();
(to, out_keys) (to, out_keys)
}; };

View File

@ -323,11 +323,11 @@ mod tests {
/// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change /// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change
/// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` nsk/npk chain, or the /// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` nsk/npk chain, or the
/// `AccountId::for_private_pda` formula breaks this test. Mirrors the pinned-value /// `PrivateAddressPlaintext::pda_account_id` formula breaks this test. Mirrors the pinned-value
/// pattern from `for_private_pda_matches_pinned_value` in `lee_core`. /// pattern from `for_private_pda_matches_pinned_value` in `lee_core`.
#[test] #[test]
fn pinned_end_to_end_derivation_for_private_pda() { fn pinned_end_to_end_derivation_for_private_pda() {
use lee_core::{account::AccountId, program::ProgramId}; use lee_core::{account::PrivateAddressPlaintext, program::ProgramId};
let gms = [42_u8; 32]; let gms = [42_u8; 32];
let seed = PdaSeed::new([1; 32]); let seed = PdaSeed::new([1; 32]);
@ -337,7 +337,8 @@ mod tests {
let keys = holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed); let keys = holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed);
let npk = keys.generate_nullifier_public_key(); let npk = keys.generate_nullifier_public_key();
let vpk = keys.generate_viewing_public_key(); let vpk = keys.generate_viewing_public_key();
let account_id = AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX); let account_id = PrivateAddressPlaintext::new(npk, vpk.clone(), u128::MAX)
.pda_account_id(&program_id, &seed);
let expected_npk = NullifierPublicKey([ let expected_npk = NullifierPublicKey([
136, 176, 234, 71, 208, 8, 143, 142, 126, 155, 132, 18, 71, 27, 88, 56, 100, 90, 79, 136, 176, 234, 71, 208, 8, 143, 142, 126, 155, 132, 18, 71, 27, 88, 56, 100, 90, 79,
@ -345,8 +346,8 @@ mod tests {
]); ]);
// AccountId is derived from (program_id, seed, npk), so it changes when npk changes. // AccountId is derived from (program_id, seed, npk), so it changes when npk changes.
// We verify npk is pinned, and AccountId is deterministically derived from it. // We verify npk is pinned, and AccountId is deterministically derived from it.
let expected_account_id = let expected_account_id = PrivateAddressPlaintext::new(expected_npk, vpk, u128::MAX)
AccountId::for_private_pda(&program_id, &seed, &expected_npk, &vpk, u128::MAX); .pda_account_id(&program_id, &seed);
assert_eq!(npk, expected_npk); assert_eq!(npk, expected_npk);
assert_eq!(account_id, expected_account_id); assert_eq!(account_id, expected_account_id);
@ -524,7 +525,7 @@ mod tests {
/// Full lifecycle: create group, distribute GMS via seal/unseal, verify key agreement. /// Full lifecycle: create group, distribute GMS via seal/unseal, verify key agreement.
#[test] #[test]
fn group_pda_lifecycle() { fn group_pda_lifecycle() {
use lee_core::account::AccountId; use lee_core::account::PrivateAddressPlaintext;
let alice_holder = GroupKeyHolder::new(); let alice_holder = GroupKeyHolder::new();
let pda_seed = PdaSeed::new([42_u8; 32]); let pda_seed = PdaSeed::new([42_u8; 32]);
@ -549,10 +550,10 @@ mod tests {
let alice_vpk = alice_keys.generate_viewing_public_key(); let alice_vpk = alice_keys.generate_viewing_public_key();
let bob_group_vpk = bob_group_keys.generate_viewing_public_key(); let bob_group_vpk = bob_group_keys.generate_viewing_public_key();
let alice_account_id = let alice_account_id = PrivateAddressPlaintext::new(alice_npk, alice_vpk, 0)
AccountId::for_private_pda(&program_id, &pda_seed, &alice_npk, &alice_vpk, 0); .pda_account_id(&program_id, &pda_seed);
let bob_account_id = let bob_account_id = PrivateAddressPlaintext::new(bob_npk, bob_group_vpk, 0)
AccountId::for_private_pda(&program_id, &pda_seed, &bob_npk, &bob_group_vpk, 0); .pda_account_id(&program_id, &pda_seed);
assert_eq!(alice_account_id, bob_account_id); assert_eq!(alice_account_id, bob_account_id);
} }

View File

@ -275,11 +275,12 @@ impl KeyTree<ChildKeysPrivate> {
identifier: Identifier, identifier: Identifier,
) -> Option<lee::AccountId> { ) -> Option<lee::AccountId> {
let node = self.key_map.get(cci)?; let node = self.key_map.get(cci)?;
let account_id = lee::AccountId::for_regular_private_account( let account_id = lee::PrivateAddressPlaintext::new(
&node.value.0.nullifier_public_key, node.value.0.nullifier_public_key,
&node.value.0.viewing_public_key, node.value.0.viewing_public_key.clone(),
identifier, identifier,
); )
.account_id();
if self.account_id_map.contains_key(&account_id) { if self.account_id_map.contains_key(&account_id) {
return None; return None;
} }

View File

@ -10,7 +10,10 @@ use risc0_zkvm::sha::{Impl, Sha256 as _};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay}; use serde_with::{DeserializeFromStr, SerializeDisplay};
use crate::{NullifierSecretKey, program::ProgramId}; use crate::{
Identifier, NullifierPublicKey, NullifierSecretKey, encryption::ViewingPublicKey,
program::ProgramId,
};
pub mod data; pub mod data;
@ -179,6 +182,28 @@ impl AccountId {
} }
} }
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
pub struct PrivateAddressPlaintext {
pub npk: NullifierPublicKey,
pub vpk: ViewingPublicKey,
pub identifier: Identifier,
}
impl PrivateAddressPlaintext {
#[must_use]
pub const fn new(
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
identifier: Identifier,
) -> Self {
Self {
npk,
vpk,
identifier,
}
}
}
impl AsRef<[u8]> for AccountId { impl AsRef<[u8]> for AccountId {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
&self.value &self.value

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
Commitment, CommitmentSetDigest, Identifier, MembershipProof, Nullifier, NullifierPublicKey, Commitment, CommitmentSetDigest, Identifier, MembershipProof, Nullifier, NullifierPublicKey,
NullifierSecretKey, NullifierSecretKey,
account::{Account, AccountWithMetadata}, account::{Account, AccountWithMetadata, PrivateAddressPlaintext},
encryption::{EncryptedAccountData, ViewingPublicKey}, encryption::{EncryptedAccountData, ViewingPublicKey},
program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow}, program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow},
}; };
@ -28,7 +28,7 @@ pub enum InputAccountIdentity {
Public, Public,
/// Init of an authorized standalone private account: no membership proof. The `pre_state` /// Init of an authorized standalone private account: no membership proof. The `pre_state`
/// must be `Account::default()`. The `account_id` is derived as /// must be `Account::default()`. The `account_id` is derived as
/// `AccountId::for_regular_private_account(&NullifierPublicKey::from(nsk), vpk, identifier)` /// `PrivateAddressPlaintext::new(NullifierPublicKey::from(nsk), vpk, identifier).account_id()`
/// and matched against `pre_state.account_id`. /// and matched against `pre_state.account_id`.
PrivateAuthorizedInit { PrivateAuthorizedInit {
vpk: ViewingPublicKey, vpk: ViewingPublicKey,
@ -55,8 +55,7 @@ pub enum InputAccountIdentity {
}, },
/// Init of a private PDA, unauthorized. The npk-to-account_id binding is proven upstream /// Init of a private PDA, unauthorized. The npk-to-account_id binding is proven upstream
/// via `Claim::Pda(seed)` or a caller's `pda_seeds` match. The identifier diversifies the /// via `Claim::Pda(seed)` or a caller's `pda_seeds` match. The identifier diversifies the
/// PDA within the `(program_id, seed, npk)` family: `AccountId::for_private_pda` uses it /// PDA within the `(program_id, seed, npk)` family.
/// as the 4th input.
PrivatePdaInit { PrivatePdaInit {
vpk: ViewingPublicKey, vpk: ViewingPublicKey,
random_seed: [u8; 32], random_seed: [u8; 32],
@ -64,10 +63,10 @@ pub enum InputAccountIdentity {
identifier: Identifier, identifier: Identifier,
/// When `Some((seed, authority_program_id))`, the circuit binds this position via the /// When `Some((seed, authority_program_id))`, the circuit binds this position via the
/// external derivation check /// external derivation check
/// `AccountId::for_private_pda(authority_program_id, seed, npk, vpk, identifier) == /// `PrivateAddressPlaintext::new(npk, vpk,
/// pre_state.account_id` rather than requiring a `Claim::Pda` or caller /// identifier).pda_account_id(authority_program_id, seed) == pre_state.account_id`
/// `pda_seeds` to establish the binding. The `pre_state` must have `is_authorized /// rather than requiring a `Claim::Pda` or caller `pda_seeds` to establish the
/// == false`. /// binding. The `pre_state` must have `is_authorized == false`.
seed: Option<(PdaSeed, ProgramId)>, seed: Option<(PdaSeed, ProgramId)>,
}, },
/// Update of an existing private PDA, with membership proof. `npk` is derived /// Update of an existing private PDA, with membership proof. `npk` is derived
@ -81,9 +80,10 @@ pub enum InputAccountIdentity {
identifier: Identifier, identifier: Identifier,
/// When `Some((seed, authority_program_id))`, the circuit binds this position via the /// When `Some((seed, authority_program_id))`, the circuit binds this position via the
/// external derivation check /// external derivation check
/// `AccountId::for_private_pda(authority_program_id, seed, npk, vpk, identifier) == /// `PrivateAddressPlaintext::new(npk, vpk,
/// pre_state.account_id` rather than requiring a caller `pda_seeds` to establish /// identifier).pda_account_id(authority_program_id, seed) == pre_state.account_id`
/// the binding. The `pre_state` must have `is_authorized == false`. /// rather than requiring a caller `pda_seeds` to establish the binding. The
/// `pre_state` must have `is_authorized == false`.
seed: Option<(PdaSeed, ProgramId)>, seed: Option<(PdaSeed, ProgramId)>,
}, },
} }
@ -103,22 +103,24 @@ impl InputAccountIdentity {
} }
#[must_use] #[must_use]
pub fn npk_vpk_if_private_pda( pub fn private_pda_address(&self) -> Option<PrivateAddressPlaintext> {
&self,
) -> Option<(NullifierPublicKey, ViewingPublicKey, Identifier)> {
match self { match self {
Self::PrivatePdaInit { Self::PrivatePdaInit {
npk, npk,
vpk, vpk,
identifier, identifier,
.. ..
} => Some((*npk, vpk.clone(), *identifier)), } => Some(PrivateAddressPlaintext::new(*npk, vpk.clone(), *identifier)),
Self::PrivatePdaUpdate { Self::PrivatePdaUpdate {
nsk, nsk,
vpk, vpk,
identifier, identifier,
.. ..
} => Some((NullifierPublicKey::from(nsk), vpk.clone(), *identifier)), } => Some(PrivateAddressPlaintext::new(
NullifierPublicKey::from(nsk),
vpk.clone(),
*identifier,
)),
Self::Public Self::Public
| Self::PrivateAuthorizedInit { .. } | Self::PrivateAuthorizedInit { .. }
| Self::PrivateAuthorizedUpdate { .. } | Self::PrivateAuthorizedUpdate { .. }

View File

@ -2,7 +2,11 @@ use borsh::{BorshDeserialize, BorshSerialize};
use risc0_zkvm::sha::{Impl, Sha256 as _}; use risc0_zkvm::sha::{Impl, Sha256 as _};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Commitment, account::AccountId, encryption::ViewingPublicKey}; use crate::{
Commitment,
account::{AccountId, PrivateAddressPlaintext},
encryption::ViewingPublicKey,
};
const PRIVATE_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/Private/\x00\x00\x00\x00"; const PRIVATE_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/Private/\x00\x00\x00\x00";
@ -12,22 +16,18 @@ pub type Identifier = u128;
#[cfg_attr(any(feature = "host", test), derive(Hash))] #[cfg_attr(any(feature = "host", test), derive(Hash))]
pub struct NullifierPublicKey(pub [u8; 32]); pub struct NullifierPublicKey(pub [u8; 32]);
impl AccountId { impl PrivateAddressPlaintext {
/// Derives an [`AccountId`] for a regular (non-PDA) private account from the nullifier public /// Derives an [`AccountId`] for a regular (non-PDA) private account from the nullifier public
/// key and identifier. /// key and identifier.
#[must_use] #[must_use]
pub fn for_regular_private_account( pub fn account_id(&self) -> AccountId {
npk: &NullifierPublicKey,
vpk: &ViewingPublicKey,
identifier: Identifier,
) -> Self {
let mut bytes = [0_u8; 32 + 32 + ViewingPublicKey::LEN + 16]; let mut bytes = [0_u8; 32 + 32 + ViewingPublicKey::LEN + 16];
bytes[0..32].copy_from_slice(PRIVATE_ACCOUNT_ID_PREFIX); bytes[0..32].copy_from_slice(PRIVATE_ACCOUNT_ID_PREFIX);
bytes[32..64].copy_from_slice(&npk.0); bytes[32..64].copy_from_slice(&self.npk.0);
bytes[64..64 + ViewingPublicKey::LEN].copy_from_slice(vpk.to_bytes()); bytes[64..64 + ViewingPublicKey::LEN].copy_from_slice(self.vpk.to_bytes());
bytes[64 + ViewingPublicKey::LEN..].copy_from_slice(&identifier.to_le_bytes()); bytes[64 + ViewingPublicKey::LEN..].copy_from_slice(&self.identifier.to_le_bytes());
Self::new( AccountId::new(
Impl::hash_bytes(&bytes) Impl::hash_bytes(&bytes)
.as_bytes() .as_bytes()
.try_into() .try_into()
@ -36,12 +36,6 @@ impl AccountId {
} }
} }
impl From<(&NullifierPublicKey, &ViewingPublicKey, Identifier)> for AccountId {
fn from((npk, vpk, identifier): (&NullifierPublicKey, &ViewingPublicKey, Identifier)) -> Self {
Self::for_regular_private_account(npk, vpk, identifier)
}
}
impl AsRef<[u8]> for NullifierPublicKey { impl AsRef<[u8]> for NullifierPublicKey {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
self.0.as_slice() self.0.as_slice()
@ -168,7 +162,7 @@ mod tests {
220, 68, 135, 10, 171, 182, 80, 54, 74, 228, 244, 236, 7, 220, 68, 135, 10, 171, 182, 80, 54, 74, 228, 244, 236, 7,
]); ]);
let account_id = AccountId::for_regular_private_account(&npk, &vpk, 0); let account_id = PrivateAddressPlaintext::new(npk, vpk, 0).account_id();
assert_eq!(account_id, expected_account_id); assert_eq!(account_id, expected_account_id);
} }
@ -186,7 +180,7 @@ mod tests {
189, 170, 32, 181, 255, 231, 19, 92, 235, 59, 153, 185, 172, 206, 189, 170, 32, 181, 255, 231, 19, 92, 235, 59, 153, 185, 172, 206,
]); ]);
let account_id = AccountId::for_regular_private_account(&npk, &vpk, 1); let account_id = PrivateAddressPlaintext::new(npk, vpk, 1).account_id();
assert_eq!(account_id, expected_account_id); assert_eq!(account_id, expected_account_id);
} }
@ -205,7 +199,7 @@ mod tests {
159, 112, 84, 100, 133, 244, 16, 34, 221, 35, 128, 131, 98, 159, 159, 112, 84, 100, 133, 244, 16, 34, 221, 35, 128, 131, 98, 159,
]); ]);
let account_id = AccountId::for_regular_private_account(&npk, &vpk, identifier); let account_id = PrivateAddressPlaintext::new(npk, vpk, identifier).account_id();
assert_eq!(account_id, expected_account_id); assert_eq!(account_id, expected_account_id);
} }

View File

@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
BlockId, Identifier, NullifierPublicKey, Timestamp, BlockId, Identifier, NullifierPublicKey, Timestamp,
account::{Account, AccountId, AccountWithMetadata}, account::{Account, AccountId, AccountWithMetadata, PrivateAddressPlaintext},
encryption::ViewingPublicKey, encryption::ViewingPublicKey,
}; };
@ -143,41 +143,6 @@ impl AccountId {
) )
} }
/// Derives an [`AccountId`] for a private PDA from the program ID, seed, nullifier public
/// key, and identifier.
///
/// Unlike public PDAs ([`AccountId::for_public_pda`]), this includes the `npk` in the
/// derivation, making the address unique per group of controllers sharing viewing keys.
/// The `identifier` further diversifies the address, so a single `(program_id, seed, npk)`
/// tuple controls a family of 2^128 addresses.
#[must_use]
pub fn for_private_pda(
program_id: &ProgramId,
seed: &PdaSeed,
npk: &NullifierPublicKey,
vpk: &ViewingPublicKey,
identifier: Identifier,
) -> Self {
use risc0_zkvm::sha::{Impl, Sha256 as _};
const PRIVATE_PDA_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/PrivatePDA/\x00";
let mut bytes = [0_u8; 32 + 32 + 32 + 32 + ViewingPublicKey::LEN + 16];
bytes[0..32].copy_from_slice(PRIVATE_PDA_PREFIX);
let program_id_bytes: &[u8] =
bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]");
bytes[32..64].copy_from_slice(program_id_bytes);
bytes[64..96].copy_from_slice(&seed.0);
bytes[96..128].copy_from_slice(&npk.to_byte_array());
bytes[128..128 + ViewingPublicKey::LEN].copy_from_slice(vpk.to_bytes());
bytes[128 + ViewingPublicKey::LEN..].copy_from_slice(&identifier.to_le_bytes());
Self::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
}
/// Derives the [`AccountId`] for a private account from the nullifier public key and kind. /// Derives the [`AccountId`] for a private account from the nullifier public key and kind.
#[must_use] #[must_use]
pub fn for_private_account( pub fn for_private_account(
@ -187,17 +152,49 @@ impl AccountId {
) -> Self { ) -> Self {
match kind { match kind {
PrivateAccountKind::Regular(identifier) => { PrivateAccountKind::Regular(identifier) => {
Self::for_regular_private_account(npk, vpk, *identifier) PrivateAddressPlaintext::new(*npk, vpk.clone(), *identifier).account_id()
} }
PrivateAccountKind::Pda { PrivateAccountKind::Pda {
program_id, program_id,
seed, seed,
identifier, identifier,
} => Self::for_private_pda(program_id, seed, npk, vpk, *identifier), } => PrivateAddressPlaintext::new(*npk, vpk.clone(), *identifier)
.pda_account_id(program_id, seed),
} }
} }
} }
impl PrivateAddressPlaintext {
/// Derives an [`AccountId`] for a private PDA from the program ID, seed, nullifier public
/// key, and identifier.
///
/// Unlike public PDAs ([`AccountId::for_public_pda`]), this includes the `npk` in the
/// derivation, making the address unique per group of controllers sharing viewing keys.
/// The `identifier` further diversifies the address, so a single `(program_id, seed, npk)`
/// tuple controls a family of 2^128 addresses.
#[must_use]
pub fn pda_account_id(&self, program_id: &ProgramId, seed: &PdaSeed) -> AccountId {
use risc0_zkvm::sha::{Impl, Sha256 as _};
const PRIVATE_PDA_PREFIX: &[u8; 32] = b"/LEE/v0.3/AccountId/PrivatePDA/\x00";
let mut bytes = [0_u8; 32 + 32 + 32 + 32 + ViewingPublicKey::LEN + 16];
bytes[0..32].copy_from_slice(PRIVATE_PDA_PREFIX);
let program_id_bytes: &[u8] =
bytemuck::try_cast_slice(program_id).expect("ProgramId should be castable to &[u8]");
bytes[32..64].copy_from_slice(program_id_bytes);
bytes[64..96].copy_from_slice(&seed.0);
bytes[96..128].copy_from_slice(&self.npk.to_byte_array());
bytes[128..128 + ViewingPublicKey::LEN].copy_from_slice(self.vpk.to_bytes());
bytes[128 + ViewingPublicKey::LEN..].copy_from_slice(&self.identifier.to_le_bytes());
AccountId::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ChainedCall { pub struct ChainedCall {
/// The program ID of the program to execute. /// The program ID of the program to execute.
@ -626,7 +623,7 @@ pub enum ExecutionValidationError {
/// ///
/// Returns only public-form derivations, suitable for contexts where all accounts are public /// Returns only public-form derivations, suitable for contexts where all accounts are public
/// (e.g. the public-execution path). The privacy circuit must additionally check each mask-3 /// (e.g. the public-execution path). The privacy circuit must additionally check each mask-3
/// `pre_state` against [`AccountId::for_private_pda`] with the supplied npk for that /// `pre_state` against [`PrivateAddressPlaintext::pda_account_id`] with the supplied npk for that
/// `pre_state`. /// `pre_state`.
#[must_use] #[must_use]
pub fn compute_public_authorized_pdas( pub fn compute_public_authorized_pdas(
@ -950,11 +947,11 @@ mod tests {
assert_eq!(account_post_state.account_mut(), &mut account); assert_eq!(account_post_state.account_mut(), &mut account);
} }
// ---- AccountId::for_private_pda tests ---- // ---- PrivateAddressPlaintext::pda_account_id tests ----
/// Pins `AccountId::for_private_pda` against a hardcoded expected output for a specific /// Pins `PrivateAddressPlaintext::pda_account_id` against a hardcoded expected output for a
/// `(program_id, seed, npk, identifier)` tuple. Any change to `PRIVATE_PDA_PREFIX`, byte /// specific `(program_id, seed, npk, identifier)` tuple. Any change to
/// ordering, or the underlying hash breaks this test. /// `PRIVATE_PDA_PREFIX`, byte ordering, or the underlying hash breaks this test.
#[test] #[test]
fn for_private_pda_matches_pinned_value() { fn for_private_pda_matches_pinned_value() {
let program_id: ProgramId = [1; 8]; let program_id: ProgramId = [1; 8];
@ -967,7 +964,7 @@ mod tests {
156, 13, 55, 32, 139, 91, 222, 209, 83, 172, 148, 123, 179, 156, 13, 55, 32, 139, 91, 222, 209, 83, 172, 148, 123, 179,
]); ]);
assert_eq!( assert_eq!(
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, identifier), PrivateAddressPlaintext::new(npk, vpk, identifier).pda_account_id(&program_id, &seed),
expected expected
); );
} }
@ -981,8 +978,9 @@ mod tests {
let npk_b = NullifierPublicKey([4; 32]); let npk_b = NullifierPublicKey([4; 32]);
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
assert_ne!( assert_ne!(
AccountId::for_private_pda(&program_id, &seed, &npk_a, &vpk, u128::MAX), PrivateAddressPlaintext::new(npk_a, vpk.clone(), u128::MAX)
AccountId::for_private_pda(&program_id, &seed, &npk_b, &vpk, u128::MAX), .pda_account_id(&program_id, &seed),
PrivateAddressPlaintext::new(npk_b, vpk, u128::MAX).pda_account_id(&program_id, &seed),
); );
} }
@ -995,8 +993,9 @@ mod tests {
let npk = NullifierPublicKey([3; 32]); let npk = NullifierPublicKey([3; 32]);
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
assert_ne!( assert_ne!(
AccountId::for_private_pda(&program_id, &seed_a, &npk, &vpk, u128::MAX), PrivateAddressPlaintext::new(npk, vpk.clone(), u128::MAX)
AccountId::for_private_pda(&program_id, &seed_b, &npk, &vpk, u128::MAX), .pda_account_id(&program_id, &seed_a),
PrivateAddressPlaintext::new(npk, vpk, u128::MAX).pda_account_id(&program_id, &seed_b),
); );
} }
@ -1009,8 +1008,9 @@ mod tests {
let npk = NullifierPublicKey([3; 32]); let npk = NullifierPublicKey([3; 32]);
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
assert_ne!( assert_ne!(
AccountId::for_private_pda(&program_id_a, &seed, &npk, &vpk, u128::MAX), PrivateAddressPlaintext::new(npk, vpk.clone(), u128::MAX)
AccountId::for_private_pda(&program_id_b, &seed, &npk, &vpk, u128::MAX), .pda_account_id(&program_id_a, &seed),
PrivateAddressPlaintext::new(npk, vpk, u128::MAX).pda_account_id(&program_id_b, &seed),
); );
} }
@ -1023,12 +1023,12 @@ mod tests {
let npk = NullifierPublicKey([3; 32]); let npk = NullifierPublicKey([3; 32]);
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
assert_ne!( assert_ne!(
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 0), PrivateAddressPlaintext::new(npk, vpk.clone(), 0).pda_account_id(&program_id, &seed),
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 1), PrivateAddressPlaintext::new(npk, vpk.clone(), 1).pda_account_id(&program_id, &seed),
); );
assert_ne!( assert_ne!(
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 0), PrivateAddressPlaintext::new(npk, vpk.clone(), 0).pda_account_id(&program_id, &seed),
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX), PrivateAddressPlaintext::new(npk, vpk, u128::MAX).pda_account_id(&program_id, &seed),
); );
} }
@ -1040,7 +1040,8 @@ mod tests {
let seed = PdaSeed::new([2; 32]); let seed = PdaSeed::new([2; 32]);
let npk = NullifierPublicKey([3; 32]); let npk = NullifierPublicKey([3; 32]);
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
let private_id = AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, u128::MAX); let private_id =
PrivateAddressPlaintext::new(npk, vpk, u128::MAX).pda_account_id(&program_id, &seed);
let public_id = AccountId::for_public_pda(&program_id, &seed); let public_id = AccountId::for_public_pda(&program_id, &seed);
assert_ne!(private_id, public_id); assert_ne!(private_id, public_id);
} }
@ -1082,7 +1083,7 @@ mod tests {
assert_eq!( assert_eq!(
AccountId::for_private_account(&npk, &vpk, &PrivateAccountKind::Regular(identifier)), AccountId::for_private_account(&npk, &vpk, &PrivateAccountKind::Regular(identifier)),
AccountId::for_regular_private_account(&npk, &vpk, identifier), PrivateAddressPlaintext::new(npk, vpk.clone(), identifier).account_id(),
); );
assert_eq!( assert_eq!(
AccountId::for_private_account( AccountId::for_private_account(
@ -1094,7 +1095,8 @@ mod tests {
identifier identifier
} }
), ),
AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, identifier), PrivateAddressPlaintext::new(npk, vpk.clone(), identifier)
.pda_account_id(&program_id, &seed),
); );
} }

View File

@ -5,7 +5,7 @@
pub use lee_core::{ pub use lee_core::{
GENESIS_BLOCK_ID, SharedSecretKey, GENESIS_BLOCK_ID, SharedSecretKey,
account::{Account, AccountId, Data}, account::{Account, AccountId, Data, PrivateAddressPlaintext},
encryption::EphemeralPublicKey, encryption::EphemeralPublicKey,
program::ProgramId, program::ProgramId,
}; };

View File

@ -180,7 +180,9 @@ mod tests {
use lee_core::{ use lee_core::{
Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, EphemeralSecretKey, Nullifier, Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, EphemeralSecretKey, Nullifier,
PrivacyPreservingCircuitOutput, SharedSecretKey, PrivacyPreservingCircuitOutput, SharedSecretKey,
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data}, account::{
Account, AccountId, AccountWithMetadata, Nonce, PrivateAddressPlaintext, data::Data,
},
program::{PdaSeed, PrivateAccountKind}, program::{PdaSeed, PrivateAccountKind},
}; };
@ -236,7 +238,8 @@ mod tests {
); );
let recipient_account_id = let recipient_account_id =
AccountId::for_regular_private_account(&recipient_keys.npk(), &recipient_keys.vpk(), 0); PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id();
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id); let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id);
let balance_to_move: u128 = 37; let balance_to_move: u128 = 37;
@ -316,14 +319,15 @@ mod tests {
data: Data::default(), data: Data::default(),
}, },
true, true,
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let commitment_sender = Commitment::new(&sender_account_id, &sender_pre.account); let commitment_sender = Commitment::new(&sender_account_id, &sender_pre.account);
let recipient_account_id = let recipient_account_id =
AccountId::for_regular_private_account(&recipient_keys.npk(), &recipient_keys.vpk(), 0); PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id();
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id); let recipient = AccountWithMetadata::new(Account::default(), false, recipient_account_id);
let balance_to_move: u128 = 37; let balance_to_move: u128 = 37;
@ -429,7 +433,7 @@ mod tests {
let pre = AccountWithMetadata::new( let pre = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
AccountId::for_regular_private_account(&account_keys.npk(), &account_keys.vpk(), 0), PrivateAddressPlaintext::new(account_keys.npk(), account_keys.vpk(), 0).account_id(),
); );
let validity_window_chain_caller = Program::validity_window_chain_caller(); let validity_window_chain_caller = Program::validity_window_chain_caller();
@ -473,8 +477,8 @@ mod tests {
let npk = keys.npk(); let npk = keys.npk();
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
let identifier: u128 = 99; let identifier: u128 = 99;
let account_id = let account_id = PrivateAddressPlaintext::new(npk, keys.vpk(), identifier)
AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), identifier); .pda_account_id(&program.id(), &seed);
let init_nonce = Nonce::private_account_nonce_init(&account_id); let init_nonce = Nonce::private_account_nonce_init(&account_id);
let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &init_nonce); let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &init_nonce);
let shared_secret = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let shared_secret = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
@ -517,7 +521,8 @@ mod tests {
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
// PDA (new, private PDA) // PDA (new, private PDA)
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 0); let pda_id =
PrivateAddressPlaintext::new(npk, keys.vpk(), 0).pda_account_id(&program.id(), &seed);
let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id); let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id);
let auth_id = auth_transfer.id(); let auth_id = auth_transfer.id();
@ -556,7 +561,8 @@ mod tests {
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
// PDA (new, private PDA) // PDA (new, private PDA)
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 0); let pda_id =
PrivateAddressPlaintext::new(npk, keys.vpk(), 0).pda_account_id(&program.id(), &seed);
let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id); let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_id);
// Recipient (public) // Recipient (public)
@ -624,7 +630,8 @@ mod tests {
// Recipient: shared private account (new, unauthorized) // Recipient: shared private account (new, unauthorized)
let shared_account_id = let shared_account_id =
AccountId::from((&shared_npk, &shared_keys.vpk(), shared_identifier)); PrivateAddressPlaintext::new(shared_npk, shared_keys.vpk(), shared_identifier)
.account_id();
let recipient = AccountWithMetadata::new(Account::default(), false, shared_account_id); let recipient = AccountWithMetadata::new(Account::default(), false, shared_account_id);
let balance_to_move: u128 = 100; let balance_to_move: u128 = 100;
@ -662,7 +669,7 @@ mod tests {
let keys = test_private_account_keys_1(); let keys = test_private_account_keys_1();
let identifier: u128 = 99; let identifier: u128 = 99;
let account_id = let account_id =
AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), identifier).account_id();
let nonce = Nonce::private_account_nonce_init(&account_id); let nonce = Nonce::private_account_nonce_init(&account_id);
let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &nonce); let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &nonce);
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
@ -696,7 +703,7 @@ mod tests {
let keys = test_private_account_keys_1(); let keys = test_private_account_keys_1();
let identifier: u128 = 99; let identifier: u128 = 99;
let account_id = let account_id =
AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), identifier).account_id();
let pre = AccountWithMetadata::new(Account::default(), true, account_id); let pre = AccountWithMetadata::new(Account::default(), true, account_id);
let (output, _) = execute_and_prove( let (output, _) = execute_and_prove(
@ -742,7 +749,7 @@ mod tests {
let identifier: u128 = 99; let identifier: u128 = 99;
// create an account id with one set of viewing keys // create an account id with one set of viewing keys
let account_id = let account_id =
AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), identifier).account_id();
let pre = AccountWithMetadata::new(Account::default(), true, account_id); let pre = AccountWithMetadata::new(Account::default(), true, account_id);
let result = execute_and_prove( let result = execute_and_prove(
@ -771,7 +778,7 @@ mod tests {
let keys = test_private_account_keys_1(); let keys = test_private_account_keys_1();
let identifier: u128 = 99; let identifier: u128 = 99;
let recipient_id = let recipient_id =
AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), identifier).account_id();
let init_nonce = Nonce::private_account_nonce_init(&recipient_id); let init_nonce = Nonce::private_account_nonce_init(&recipient_id);
let esk = EphemeralSecretKey::new(&recipient_id, &[0; 32], &init_nonce); let esk = EphemeralSecretKey::new(&recipient_id, &[0; 32], &init_nonce);
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
@ -820,7 +827,7 @@ mod tests {
let keys = test_private_account_keys_1(); let keys = test_private_account_keys_1();
let identifier: u128 = 99; let identifier: u128 = 99;
let account_id = let account_id =
AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier); PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), identifier).account_id();
let update_nonce = Nonce::default().private_account_nonce_increment(&keys.nsk); let update_nonce = Nonce::default().private_account_nonce_increment(&keys.nsk);
let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &update_nonce); let esk = EphemeralSecretKey::new(&account_id, &[0; 32], &update_nonce);
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
@ -873,8 +880,8 @@ mod tests {
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
let identifier: u128 = 99; let identifier: u128 = 99;
let auth_transfer_id = auth_transfer.id(); let auth_transfer_id = auth_transfer.id();
let pda_id = let pda_id = PrivateAddressPlaintext::new(npk, keys.vpk(), identifier)
AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), identifier); .pda_account_id(&program.id(), &seed);
let update_nonce = Nonce::default().private_account_nonce_increment(&keys.nsk); let update_nonce = Nonce::default().private_account_nonce_increment(&keys.nsk);
let esk = EphemeralSecretKey::new(&pda_id, &[0; 32], &update_nonce); let esk = EphemeralSecretKey::new(&pda_id, &[0; 32], &update_nonce);
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0; let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
@ -930,7 +937,8 @@ mod tests {
let keys = test_private_account_keys_1(); let keys = test_private_account_keys_1();
let npk = keys.npk(); let npk = keys.npk();
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 5); let account_id =
PrivateAddressPlaintext::new(npk, keys.vpk(), 5).pda_account_id(&program.id(), &seed);
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
let result = execute_and_prove( let result = execute_and_prove(
@ -957,7 +965,8 @@ mod tests {
let npk = keys.npk(); let npk = keys.npk();
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
let auth_transfer_id = auth_transfer.id(); let auth_transfer_id = auth_transfer.id();
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), 5); let pda_id =
PrivateAddressPlaintext::new(npk, keys.vpk(), 5).pda_account_id(&program.id(), &seed);
let pda_account = Account { let pda_account = Account {
program_owner: auth_transfer_id, program_owner: auth_transfer_id,
balance: 1, balance: 1,

View File

@ -119,10 +119,12 @@ pub mod tests {
let encrypted_private_post_states = Vec::new(); let encrypted_private_post_states = Vec::new();
let account_id2 = lee_core::account::AccountId::for_regular_private_account(&npk2, &vpk, 0); let account_id2 =
lee_core::account::PrivateAddressPlaintext::new(npk2, vpk.clone(), 0).account_id();
let new_commitments = vec![Commitment::new(&account_id2, &account2)]; let new_commitments = vec![Commitment::new(&account_id2, &account2)];
let account_id1 = lee_core::account::AccountId::for_regular_private_account(&npk1, &vpk, 0); let account_id1 =
lee_core::account::PrivateAddressPlaintext::new(npk1, vpk, 0).account_id();
let old_commitment = Commitment::new(&account_id1, &account1); let old_commitment = Commitment::new(&account_id1, &account1);
let new_nullifiers = vec![( let new_nullifiers = vec![(
Nullifier::for_account_update(&old_commitment, &nsk1), Nullifier::for_account_update(&old_commitment, &nsk1),
@ -198,7 +200,8 @@ pub mod tests {
let npk = NullifierPublicKey::from(&[1; 32]); let npk = NullifierPublicKey::from(&[1; 32]);
let vpk = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]); let vpk = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]);
let account = Account::default(); let account = Account::default();
let account_id = lee_core::account::AccountId::for_regular_private_account(&npk, &vpk, 0); let account_id =
lee_core::account::PrivateAddressPlaintext::new(npk, vpk.clone(), 0).account_id();
let commitment = Commitment::new(&account_id, &account); let commitment = Commitment::new(&account_id, &account);
let (shared_secret, epk) = let (shared_secret, epk) =
SharedSecretKey::encapsulate_deterministic(&vpk, &EphemeralSecretKey([0_u8; 32])); SharedSecretKey::encapsulate_deterministic(&vpk, &EphemeralSecretKey([0_u8; 32]));

View File

@ -420,7 +420,9 @@ pub mod tests {
use lee_core::{ use lee_core::{
BlockId, Commitment, InputAccountIdentity, Nullifier, NullifierPublicKey, BlockId, Commitment, InputAccountIdentity, Nullifier, NullifierPublicKey,
NullifierSecretKey, Timestamp, NullifierSecretKey, Timestamp,
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data}, account::{
Account, AccountId, AccountWithMetadata, Nonce, PrivateAddressPlaintext, data::Data,
},
encryption::ViewingPublicKey, encryption::ViewingPublicKey,
program::{ program::{
BlockValidityWindow, ExecutionValidationError, PdaSeed, ProgramId, BlockValidityWindow, ExecutionValidationError, PdaSeed, ProgramId,
@ -517,7 +519,7 @@ pub mod tests {
#[must_use] #[must_use]
pub fn with_private_account(mut self, keys: &TestPrivateKeys, account: &Account) -> Self { pub fn with_private_account(mut self, keys: &TestPrivateKeys, account: &Account) -> Self {
let account_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), 0); let account_id = PrivateAddressPlaintext::new(keys.npk(), keys.vpk(), 0).account_id();
let commitment = Commitment::new(&account_id, account); let commitment = Commitment::new(&account_id, account);
self.private_state.0.extend(&[commitment]); self.private_state.0.extend(&[commitment]);
self self
@ -729,8 +731,8 @@ pub mod tests {
..Account::default() ..Account::default()
}; };
let account_id1 = AccountId::for_regular_private_account(&keys1.npk(), &keys1.vpk(), 0); let account_id1 = PrivateAddressPlaintext::new(keys1.npk(), keys1.vpk(), 0).account_id();
let account_id2 = AccountId::for_regular_private_account(&keys2.npk(), &keys2.vpk(), 0); let account_id2 = PrivateAddressPlaintext::new(keys2.npk(), keys2.vpk(), 0).account_id();
let init_commitment1 = Commitment::new(&account_id1, &account); let init_commitment1 = Commitment::new(&account_id1, &account);
let init_commitment2 = Commitment::new(&account_id2, &account); let init_commitment2 = Commitment::new(&account_id2, &account);
@ -1406,7 +1408,8 @@ pub mod tests {
let recipient = AccountWithMetadata::new( let recipient = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let (output, proof) = circuit::execute_and_prove( let (output, proof) = circuit::execute_and_prove(
@ -1448,17 +1451,18 @@ pub mod tests {
) -> PrivacyPreservingTransaction { ) -> PrivacyPreservingTransaction {
let program = Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let sender_commitment = Commitment::new(&sender_account_id, sender_private_account); let sender_commitment = Commitment::new(&sender_account_id, sender_private_account);
let sender_pre = AccountWithMetadata::new( let sender_pre = AccountWithMetadata::new(
sender_private_account.clone(), sender_private_account.clone(),
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let recipient_pre = AccountWithMetadata::new( let recipient_pre = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let (output, proof) = circuit::execute_and_prove( let (output, proof) = circuit::execute_and_prove(
@ -1504,12 +1508,12 @@ pub mod tests {
) -> PrivacyPreservingTransaction { ) -> PrivacyPreservingTransaction {
let program = Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let sender_commitment = Commitment::new(&sender_account_id, sender_private_account); let sender_commitment = Commitment::new(&sender_account_id, sender_private_account);
let sender_pre = AccountWithMetadata::new( let sender_pre = AccountWithMetadata::new(
sender_private_account.clone(), sender_private_account.clone(),
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let recipient_pre = AccountWithMetadata::new( let recipient_pre = AccountWithMetadata::new(
state.get_account_by_id(*recipient_account_id), state.get_account_by_id(*recipient_account_id),
@ -1615,9 +1619,10 @@ pub mod tests {
); );
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let recipient_account_id = let recipient_account_id =
AccountId::for_regular_private_account(&recipient_keys.npk(), &recipient_keys.vpk(), 0); PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id();
let expected_new_commitment_1 = Commitment::new( let expected_new_commitment_1 = Commitment::new(
&sender_account_id, &sender_account_id,
&Account { &Account {
@ -1769,7 +1774,7 @@ pub mod tests {
); );
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let expected_new_commitment = Commitment::new( let expected_new_commitment = Commitment::new(
&sender_account_id, &sender_account_id,
&Account { &Account {
@ -2081,12 +2086,13 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
// Setting the recipient nsk to authorize the sender. // Setting the recipient nsk to authorize the sender.
@ -2129,7 +2135,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account { Account {
@ -2138,7 +2144,8 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -2177,7 +2184,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account { Account {
@ -2186,7 +2193,8 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -2225,7 +2233,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account { Account {
@ -2234,7 +2242,8 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -2273,7 +2282,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account { Account {
@ -2282,7 +2291,8 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
false, false,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -2322,13 +2332,14 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let private_account_2 = AccountWithMetadata::new( let private_account_2 = AccountWithMetadata::new(
Account::default(), Account::default(),
// This should be set to false in normal circumstances // This should be set to false in normal circumstances
true, true,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -2397,9 +2408,10 @@ pub mod tests {
/// Happy path: a program claims a new private PDA via `Claim::Pda(seed)`. The circuit /// Happy path: a program claims a new private PDA via `Claim::Pda(seed)`. The circuit
/// reads the npk for that `pre_state` from `private_account_keys` at the `pre_state`'s /// reads the npk for that `pre_state` from `private_account_keys` at the `pre_state`'s
/// position, derives `AccountId` via `AccountId::for_private_pda(program_id, seed, npk)`, and /// position, derives `AccountId` via `PrivateAddressPlaintext::new(npk,
/// asserts it equals the `pre_state`'s `account_id`. The equality both validates the claim /// ..).pda_account_id(program_id, seed)`, and asserts it equals the `pre_state`'s
/// and binds the supplied npk to the `account_id`. /// `account_id`. The equality both validates the claim and binds the supplied npk to the
/// `account_id`.
#[test] #[test]
fn private_pda_claim_succeeds() { fn private_pda_claim_succeeds() {
let program = Program::pda_claimer(); let program = Program::pda_claimer();
@ -2407,8 +2419,8 @@ pub mod tests {
let npk = keys.npk(); let npk = keys.npk();
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
let account_id = let account_id = PrivateAddressPlaintext::new(npk, keys.vpk(), u128::MAX)
AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), u128::MAX); .pda_account_id(&program.id(), &seed);
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
let result = execute_and_prove( let result = execute_and_prove(
@ -2433,7 +2445,8 @@ pub mod tests {
} }
/// An npk is supplied that does not match the `pre_state`'s `account_id` under /// An npk is supplied that does not match the `pre_state`'s `account_id` under
/// `AccountId::for_private_pda(program, claim_seed, npk)`. The claim equality check rejects. /// `PrivateAddressPlaintext::new(npk, ..).pda_account_id(program, claim_seed)`. The claim
/// equality check rejects.
#[test] #[test]
fn private_pda_npk_mismatch_fails() { fn private_pda_npk_mismatch_fails() {
// `keys_a` produces the `pre_state`'s `account_id` (the registered pair), `keys_b` is // `keys_a` produces the `pre_state`'s `account_id` (the registered pair), `keys_b` is
@ -2446,10 +2459,10 @@ pub mod tests {
let seed = PdaSeed::new([42; 32]); let seed = PdaSeed::new([42; 32]);
// `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state. // `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state.
// `AccountId::for_private_pda(program, seed, npk_b) != account_id`, so the claim check in // `PrivateAddressPlaintext::new(npk_b, ..).pda_account_id(program, seed) != account_id`, so
// the circuit must reject. // the claim check in the circuit must reject.
let account_id = let account_id = PrivateAddressPlaintext::new(npk_a, keys_a.vpk(), u128::MAX)
AccountId::for_private_pda(&program.id(), &seed, &npk_a, &keys_a.vpk(), u128::MAX); .pda_account_id(&program.id(), &seed);
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
let result = execute_and_prove( let result = execute_and_prove(
@ -2472,7 +2485,7 @@ pub mod tests {
/// private PDA via `Claim::Pda(seed)`, then chains to a callee (`noop`) delegating the same /// private PDA via `Claim::Pda(seed)`, then chains to a callee (`noop`) delegating the same
/// seed via `ChainedCall.pda_seeds`. In the callee's step, the `pre_state`'s authorization /// seed via `ChainedCall.pda_seeds`. In the callee's step, the `pre_state`'s authorization
/// is established via the private derivation /// is established via the private derivation
/// `AccountId::for_private_pda(delegator, seed, npk) == pre.account_id`. /// `PrivateAddressPlaintext::new(npk, ..).pda_account_id(delegator, seed) == pre.account_id`.
#[test] #[test]
fn caller_pda_seeds_authorize_private_pda_for_callee() { fn caller_pda_seeds_authorize_private_pda_for_callee() {
let delegator = Program::private_pda_delegator(); let delegator = Program::private_pda_delegator();
@ -2481,8 +2494,8 @@ pub mod tests {
let npk = keys.npk(); let npk = keys.npk();
let seed = PdaSeed::new([77; 32]); let seed = PdaSeed::new([77; 32]);
let account_id = let account_id = PrivateAddressPlaintext::new(npk, keys.vpk(), u128::MAX)
AccountId::for_private_pda(&delegator.id(), &seed, &npk, &keys.vpk(), u128::MAX); .pda_account_id(&delegator.id(), &seed);
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
let callee_id = callee.id(); let callee_id = callee.id();
@ -2521,8 +2534,8 @@ pub mod tests {
let claim_seed = PdaSeed::new([77; 32]); let claim_seed = PdaSeed::new([77; 32]);
let wrong_delegated_seed = PdaSeed::new([88; 32]); let wrong_delegated_seed = PdaSeed::new([88; 32]);
let account_id = let account_id = PrivateAddressPlaintext::new(npk, keys.vpk(), u128::MAX)
AccountId::for_private_pda(&delegator.id(), &claim_seed, &npk, &keys.vpk(), u128::MAX); .pda_account_id(&delegator.id(), &claim_seed);
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id); let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
let callee_id = callee.id(); let callee_id = callee.id();
@ -2560,20 +2573,10 @@ pub mod tests {
let keys_b = test_private_account_keys_2(); let keys_b = test_private_account_keys_2();
let seed = PdaSeed::new([55; 32]); let seed = PdaSeed::new([55; 32]);
let account_a = AccountId::for_private_pda( let account_a = PrivateAddressPlaintext::new(keys_a.npk(), keys_a.vpk(), u128::MAX)
&program.id(), .pda_account_id(&program.id(), &seed);
&seed, let account_b = PrivateAddressPlaintext::new(keys_b.npk(), keys_b.vpk(), u128::MAX)
&keys_a.npk(), .pda_account_id(&program.id(), &seed);
&keys_a.vpk(),
u128::MAX,
);
let account_b = AccountId::for_private_pda(
&program.id(),
&seed,
&keys_b.npk(),
&keys_b.vpk(),
u128::MAX,
);
let pre_a = AccountWithMetadata::new(Account::default(), false, account_a); let pre_a = AccountWithMetadata::new(Account::default(), false, account_a);
let pre_b = AccountWithMetadata::new(Account::default(), false, account_b); let pre_b = AccountWithMetadata::new(Account::default(), false, account_b);
@ -2617,8 +2620,8 @@ pub mod tests {
// Simulate a previously-claimed private PDA: program_owner != DEFAULT, is_authorized = // Simulate a previously-claimed private PDA: program_owner != DEFAULT, is_authorized =
// true, account_id derived via the private formula. // true, account_id derived via the private formula.
let account_id = let account_id = PrivateAddressPlaintext::new(npk, keys.vpk(), u128::MAX)
AccountId::for_private_pda(&program.id(), &seed, &npk, &keys.vpk(), u128::MAX); .pda_account_id(&program.id(), &seed);
let owned_pre_state = AccountWithMetadata::new( let owned_pre_state = AccountWithMetadata::new(
Account { Account {
program_owner: program.id(), program_owner: program.id(),
@ -2711,7 +2714,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let result = execute_and_prove( let result = execute_and_prove(
@ -3045,7 +3048,7 @@ pub mod tests {
..Account::default() ..Account::default()
}; };
let sender_account_id = let sender_account_id =
AccountId::for_regular_private_account(&sender_keys.npk(), &sender_keys.vpk(), 0); PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id();
let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account); let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account);
let sender_init_nullifier = Nullifier::for_account_initialization(&sender_account_id); let sender_init_nullifier = Nullifier::for_account_initialization(&sender_account_id);
let mut state = V03State::new_with_genesis_accounts( let mut state = V03State::new_with_genesis_accounts(
@ -3056,7 +3059,7 @@ pub mod tests {
let sender_pre = AccountWithMetadata::new( let sender_pre = AccountWithMetadata::new(
sender_private_account, sender_private_account,
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
let recipient_private_key = PrivateKey::try_new([2; 32]).unwrap(); let recipient_private_key = PrivateKey::try_new([2; 32]).unwrap();
let recipient_account_id = let recipient_account_id =
@ -3129,7 +3132,7 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&from_keys.npk(), &from_keys.vpk(), 0), PrivateAddressPlaintext::new(from_keys.npk(), from_keys.vpk(), 0).account_id(),
); );
let to_account = AccountWithMetadata::new( let to_account = AccountWithMetadata::new(
Account { Account {
@ -3137,13 +3140,13 @@ pub mod tests {
..Account::default() ..Account::default()
}, },
true, true,
(&to_keys.npk(), &to_keys.vpk(), 0), PrivateAddressPlaintext::new(to_keys.npk(), to_keys.vpk(), 0).account_id(),
); );
let from_account_id = let from_account_id =
AccountId::for_regular_private_account(&from_keys.npk(), &from_keys.vpk(), 0); PrivateAddressPlaintext::new(from_keys.npk(), from_keys.vpk(), 0).account_id();
let to_account_id = let to_account_id =
AccountId::for_regular_private_account(&to_keys.npk(), &to_keys.vpk(), 0); PrivateAddressPlaintext::new(to_keys.npk(), to_keys.vpk(), 0).account_id();
let from_commitment = Commitment::new(&from_account_id, &from_account.account); let from_commitment = Commitment::new(&from_account_id, &from_account.account);
let to_commitment = Commitment::new(&to_account_id, &to_account.account); let to_commitment = Commitment::new(&to_account_id, &to_account.account);
let from_init_nullifier = Nullifier::for_account_initialization(&from_account_id); let from_init_nullifier = Nullifier::for_account_initialization(&from_account_id);
@ -3450,7 +3453,7 @@ pub mod tests {
let authorized_account = AccountWithMetadata::new( let authorized_account = AccountWithMetadata::new(
Account::default(), Account::default(),
true, true,
(&private_keys.npk(), &private_keys.vpk(), 0), PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id(),
); );
let program = Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
@ -3483,7 +3486,7 @@ pub mod tests {
assert!(result.is_ok()); assert!(result.is_ok());
let account_id = let account_id =
AccountId::for_regular_private_account(&private_keys.npk(), &private_keys.vpk(), 0); PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id();
let nullifier = Nullifier::for_account_initialization(&account_id); let nullifier = Nullifier::for_account_initialization(&account_id);
assert!(state.private_state.1.contains(&nullifier)); assert!(state.private_state.1.contains(&nullifier));
} }
@ -3500,7 +3503,7 @@ pub mod tests {
let unauthorized_account = AccountWithMetadata::new( let unauthorized_account = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&private_keys.npk(), &private_keys.vpk(), 0), PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id(),
); );
let program = Program::claimer(); let program = Program::claimer();
@ -3528,7 +3531,7 @@ pub mod tests {
.unwrap(); .unwrap();
let account_id = let account_id =
AccountId::for_regular_private_account(&private_keys.npk(), &private_keys.vpk(), 0); PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id();
let nullifier = Nullifier::for_account_initialization(&account_id); let nullifier = Nullifier::for_account_initialization(&account_id);
assert!(state.private_state.1.contains(&nullifier)); assert!(state.private_state.1.contains(&nullifier));
} }
@ -3544,7 +3547,7 @@ pub mod tests {
let authorized_account = AccountWithMetadata::new( let authorized_account = AccountWithMetadata::new(
Account::default(), Account::default(),
true, true,
(&private_keys.npk(), &private_keys.vpk(), 0), PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id(),
); );
let claimer_program = Program::claimer(); let claimer_program = Program::claimer();
@ -3581,7 +3584,7 @@ pub mod tests {
// Verify the account is now initialized (nullifier exists) // Verify the account is now initialized (nullifier exists)
let account_id = let account_id =
AccountId::for_regular_private_account(&private_keys.npk(), &private_keys.vpk(), 0); PrivateAddressPlaintext::new(private_keys.npk(), private_keys.vpk(), 0).account_id();
let nullifier = Nullifier::for_account_initialization(&account_id); let nullifier = Nullifier::for_account_initialization(&account_id);
assert!(state.private_state.1.contains(&nullifier)); assert!(state.private_state.1.contains(&nullifier));
@ -3671,7 +3674,7 @@ pub mod tests {
let private_account = AccountWithMetadata::new( let private_account = AccountWithMetadata::new(
Account::default(), Account::default(),
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
// Don't change data (None) and don't claim (false) // Don't change data (None) and don't claim (false)
let instruction: (Option<Vec<u8>>, bool) = (None, false); let instruction: (Option<Vec<u8>>, bool) = (None, false);
@ -3700,7 +3703,7 @@ pub mod tests {
let private_account = AccountWithMetadata::new( let private_account = AccountWithMetadata::new(
Account::default(), Account::default(),
true, true,
(&sender_keys.npk(), &sender_keys.vpk(), 0), PrivateAddressPlaintext::new(sender_keys.npk(), sender_keys.vpk(), 0).account_id(),
); );
// Change data but don't claim (false) - should fail // Change data but don't claim (false) - should fail
let new_data = vec![1, 2, 3, 4, 5]; let new_data = vec![1, 2, 3, 4, 5];
@ -3743,11 +3746,13 @@ pub mod tests {
let recipient_account = AccountWithMetadata::new( let recipient_account = AccountWithMetadata::new(
Account::default(), Account::default(),
true, true,
(&recipient_keys.npk(), &recipient_keys.vpk(), 0), PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id(),
); );
let recipient_account_id = let recipient_account_id =
AccountId::for_regular_private_account(&recipient_keys.npk(), &recipient_keys.vpk(), 0); PrivateAddressPlaintext::new(recipient_keys.npk(), recipient_keys.vpk(), 0)
.account_id();
let recipient_commitment = let recipient_commitment =
Commitment::new(&recipient_account_id, &recipient_account.account); Commitment::new(&recipient_account_id, &recipient_account.account);
let recipient_init_nullifier = Nullifier::for_account_initialization(&recipient_account_id); let recipient_init_nullifier = Nullifier::for_account_initialization(&recipient_account_id);
@ -3916,7 +3921,7 @@ pub mod tests {
let pre = AccountWithMetadata::new( let pre = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&account_keys.npk(), &account_keys.vpk(), 0), PrivateAddressPlaintext::new(account_keys.npk(), account_keys.vpk(), 0).account_id(),
); );
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs(); let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
let tx = { let tx = {
@ -3981,7 +3986,7 @@ pub mod tests {
let pre = AccountWithMetadata::new( let pre = AccountWithMetadata::new(
Account::default(), Account::default(),
false, false,
(&account_keys.npk(), &account_keys.vpk(), 0), PrivateAddressPlaintext::new(account_keys.npk(), account_keys.vpk(), 0).account_id(),
); );
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs(); let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
let tx = { let tx = {
@ -4512,10 +4517,10 @@ pub mod tests {
ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer.clone())].into()); ProgramWithDependencies::new(proxy, [(auth_transfer_id, auth_transfer.clone())].into());
let funder_id = funder_keys.account_id(); let funder_id = funder_keys.account_id();
let alice_pda_0_id = let alice_pda_0_id = PrivateAddressPlaintext::new(alice_npk, alice_keys.vpk(), 0)
AccountId::for_private_pda(&proxy_id, &seed, &alice_npk, &alice_keys.vpk(), 0); .pda_account_id(&proxy_id, &seed);
let alice_pda_1_id = let alice_pda_1_id = PrivateAddressPlaintext::new(alice_npk, alice_keys.vpk(), 1)
AccountId::for_private_pda(&proxy_id, &seed, &alice_npk, &alice_keys.vpk(), 1); .pda_account_id(&proxy_id, &seed);
let recipient_id = test_public_account_keys_2().account_id(); let recipient_id = test_public_account_keys_2().account_id();
let recipient_signing_key = test_public_account_keys_2().signing_key; let recipient_signing_key = test_public_account_keys_2().signing_key;

View File

@ -511,7 +511,7 @@ fn n_unique<T: Eq + Hash>(data: &[T]) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use lee_core::account::{AccountId, Nonce}; use lee_core::account::{AccountId, Nonce, PrivateAddressPlaintext};
use crate::{ use crate::{
PrivateKey, PublicKey, V03State, PrivateKey, PublicKey, V03State,
@ -603,7 +603,7 @@ mod tests {
// Attacker controls a private account. // Attacker controls a private account.
let attacker_keys = test_private_account_keys_1(); let attacker_keys = test_private_account_keys_1();
let attacker_id = let attacker_id =
AccountId::for_regular_private_account(&attacker_keys.npk(), &attacker_keys.vpk(), 0); PrivateAddressPlaintext::new(attacker_keys.npk(), attacker_keys.vpk(), 0).account_id();
let victim_id = AccountId::new([20_u8; 32]); let victim_id = AccountId::new([20_u8; 32]);
let recipient_id = AccountId::new([42_u8; 32]); let recipient_id = AccountId::new([42_u8; 32]);
@ -709,9 +709,10 @@ mod tests {
/// There are two routes, both closed: /// There are two routes, both closed:
/// ///
/// - **mask=1 (`PrivateAuthorizedUpdate`)**: the circuit derives `account_id = /// - **mask=1 (`PrivateAuthorizedUpdate`)**: the circuit derives `account_id =
/// AccountId::for_regular_private_account(&npk_from(nsk), identifier)` and asserts it matches /// PrivateAddressPlaintext::new(npk_from(nsk), vpk, identifier).account_id()` and asserts it
/// `pre_state.account_id`. Passing this check requires the victim's `nsk`, which the attacker /// matches `pre_state.account_id`. Passing this check requires the victim's `nsk`, which the
/// does not have. `execute_and_prove` panics inside the ZKVM and no proof is produced. /// attacker does not have. `execute_and_prove` panics inside the ZKVM and no proof is
/// produced.
/// ///
/// - **mask=0 (`Public`)**: the circuit places the account in `public_pre_states` and /// - **mask=0 (`Public`)**: the circuit places the account in `public_pre_states` and
/// `execute_and_prove` succeeds. The host-side validator then reconstructs /// `execute_and_prove` succeeds. The host-side validator then reconstructs
@ -754,12 +755,12 @@ mod tests {
// Attacker controls a private account. // Attacker controls a private account.
let attacker_keys = test_private_account_keys_1(); let attacker_keys = test_private_account_keys_1();
let attacker_id = let attacker_id =
AccountId::for_regular_private_account(&attacker_keys.npk(), &attacker_keys.vpk(), 0); PrivateAddressPlaintext::new(attacker_keys.npk(), attacker_keys.vpk(), 0).account_id();
// Victim is a private account — not registered in public chain state. // Victim is a private account — not registered in public chain state.
let victim_keys = test_private_account_keys_2(); let victim_keys = test_private_account_keys_2();
let victim_id = let victim_id =
AccountId::for_regular_private_account(&victim_keys.npk(), &victim_keys.vpk(), 0); PrivateAddressPlaintext::new(victim_keys.npk(), victim_keys.vpk(), 0).account_id();
let victim_balance = 5_000_u128; let victim_balance = 5_000_u128;
let recipient_id = AccountId::new([42_u8; 32]); let recipient_id = AccountId::new([42_u8; 32]);

View File

@ -819,7 +819,7 @@ mod tests {
}; };
use key_protocol::key_management::KeyChain; use key_protocol::key_management::KeyChain;
use lee::{ use lee::{
Account, AccountId, Data, PrivacyPreservingTransaction, V03State, Account, Data, PrivacyPreservingTransaction, PrivateAddressPlaintext, V03State,
error::LeeError, error::LeeError,
execute_and_prove, execute_and_prove,
privacy_preserving_transaction::{Message, circuit::ProgramWithDependencies}, privacy_preserving_transaction::{Message, circuit::ProgramWithDependencies},
@ -1604,11 +1604,12 @@ mod tests {
#[test] #[test]
fn private_bridge_withdraw_invocation_is_dropped() { fn private_bridge_withdraw_invocation_is_dropped() {
let sender_keys = KeyChain::new_os_random(); let sender_keys = KeyChain::new_os_random();
let sender_account_id = AccountId::for_regular_private_account( let sender_account_id = PrivateAddressPlaintext::new(
&sender_keys.nullifier_public_key, sender_keys.nullifier_public_key,
&sender_keys.viewing_public_key, sender_keys.viewing_public_key.clone(),
0, 0,
); )
.account_id();
let sender_private_account = Account { let sender_private_account = Account {
program_owner: Program::authenticated_transfer_program().id(), program_owner: Program::authenticated_transfer_program().id(),
balance: 100, balance: 100,
@ -1631,11 +1632,12 @@ mod tests {
let sender_pre = AccountWithMetadata::new( let sender_pre = AccountWithMetadata::new(
sender_private_account, sender_private_account,
true, true,
( PrivateAddressPlaintext::new(
&sender_keys.nullifier_public_key, sender_keys.nullifier_public_key,
&sender_keys.viewing_public_key, sender_keys.viewing_public_key.clone(),
0, 0,
), )
.account_id(),
); );
let bridge_pre = AccountWithMetadata::new( let bridge_pre = AccountWithMetadata::new(
state.get_account_by_id(bridge_account_id), state.get_account_by_id(bridge_account_id),

View File

@ -106,11 +106,12 @@ pub struct PrivateAccountPrivateInitialData {
impl PrivateAccountPrivateInitialData { impl PrivateAccountPrivateInitialData {
#[must_use] #[must_use]
pub fn account_id(&self) -> lee::AccountId { pub fn account_id(&self) -> lee::AccountId {
lee::AccountId::for_regular_private_account( lee::PrivateAddressPlaintext::new(
&self.key_chain.nullifier_public_key, self.key_chain.nullifier_public_key,
&self.key_chain.viewing_public_key, self.key_chain.viewing_public_key.clone(),
self.identifier, self.identifier,
) )
.account_id()
} }
} }
@ -219,7 +220,8 @@ pub fn initial_state() -> V03State {
.map(|init_comm_data| { .map(|init_comm_data| {
let npk = &init_comm_data.npk; let npk = &init_comm_data.npk;
let account_id = let account_id =
lee::AccountId::for_regular_private_account(npk, &init_comm_data.vpk, 0); lee::PrivateAddressPlaintext::new(*npk, init_comm_data.vpk.clone(), 0)
.account_id();
let mut acc = init_comm_data.account.clone(); let mut acc = init_comm_data.account.clone();

View File

@ -2,7 +2,7 @@ use core::fmt;
use anyhow::Result; use anyhow::Result;
use keycard_wallet::{KeycardWallet, python_path}; use keycard_wallet::{KeycardWallet, python_path};
use lee::{AccountId, PrivateKey, PublicKey, Signature}; use lee::{AccountId, PrivateAddressPlaintext, PrivateKey, PublicKey, Signature};
use lee_core::{ use lee_core::{
Identifier, InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey, Identifier, InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey,
SharedSecretKey, SharedSecretKey,
@ -30,7 +30,7 @@ pub enum AccountIdentity {
identifier: Identifier, identifier: Identifier,
}, },
/// An owned private PDA: wallet holds the nsk/npk; `account_id` was derived via /// An owned private PDA: wallet holds the nsk/npk; `account_id` was derived via
/// [`AccountId::for_private_pda`]. /// [`PrivateAddressPlaintext::pda_account_id`].
PrivatePdaOwned(AccountId), PrivatePdaOwned(AccountId),
/// A foreign private PDA: wallet knows the recipient's npk/vpk but not their nsk. /// A foreign private PDA: wallet knows the recipient's npk/vpk but not their nsk.
/// Uses a default (uninitialised) account. /// Uses a default (uninitialised) account.
@ -50,7 +50,7 @@ pub enum AccountIdentity {
identifier: Identifier, identifier: Identifier,
}, },
/// A shared private PDA with externally-provided keys (e.g. from GMS). /// A shared private PDA with externally-provided keys (e.g. from GMS).
/// `account_id` was derived via [`AccountId::for_private_pda`]. /// `account_id` was derived via [`PrivateAddressPlaintext::pda_account_id`].
PrivatePdaShared { PrivatePdaShared {
account_id: AccountId, account_id: AccountId,
nsk: NullifierSecretKey, nsk: NullifierSecretKey,
@ -261,7 +261,11 @@ impl AccountManager {
identifier, identifier,
} => { } => {
let acc = lee_core::account::Account::default(); let acc = lee_core::account::Account::default();
let auth_acc = AccountWithMetadata::new(acc, false, (&npk, &vpk, identifier)); let auth_acc = AccountWithMetadata::new(
acc,
false,
PrivateAddressPlaintext::new(npk, vpk.clone(), identifier).account_id(),
);
let mut random_seed: [u8; 32] = [0; 32]; let mut random_seed: [u8; 32] = [0; 32];
OsRng.fill_bytes(&mut random_seed); OsRng.fill_bytes(&mut random_seed);
let pre = AccountPreparedData { let pre = AccountPreparedData {
@ -309,7 +313,9 @@ impl AccountManager {
vpk, vpk,
identifier, identifier,
} => { } => {
let account_id = lee::AccountId::from((&npk, &vpk, identifier)); let account_id =
lee::PrivateAddressPlaintext::new(npk, vpk.clone(), identifier)
.account_id();
let pre = private_shared_acc_preparation( let pre = private_shared_acc_preparation(
wallet, account_id, nsk, npk, vpk, identifier, false, wallet, account_id, nsk, npk, vpk, identifier, false,
) )

View File

@ -525,11 +525,12 @@ impl WalletSubcommand for ImportSubcommand {
let key_chain: KeyChain = serde_json::from_str(&key_chain_json) let key_chain: KeyChain = serde_json::from_str(&key_chain_json)
.map_err(|err| anyhow::anyhow!("Invalid key chain JSON: {err}"))?; .map_err(|err| anyhow::anyhow!("Invalid key chain JSON: {err}"))?;
let account = lee::Account::from(account_state); let account = lee::Account::from(account_state);
let account_id = lee::AccountId::from(( let account_id = lee::PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
identifier, identifier,
)); )
.account_id();
wallet_core wallet_core
.storage_mut() .storage_mut()

View File

@ -17,7 +17,7 @@ use common::{HashType, transaction::LeeTransaction};
use config::WalletConfig; use config::WalletConfig;
use key_protocol::key_management::key_tree::chain_index::ChainIndex; use key_protocol::key_management::key_tree::chain_index::ChainIndex;
use lee::{ use lee::{
Account, AccountId, PrivacyPreservingTransaction, Account, AccountId, PrivacyPreservingTransaction, PrivateAddressPlaintext,
privacy_preserving_transaction::{ privacy_preserving_transaction::{
circuit::ProgramWithDependencies, message::EncryptedAccountData, circuit::ProgramWithDependencies, message::EncryptedAccountData,
}, },
@ -377,7 +377,8 @@ impl WalletCore {
let keys = holder.derive_keys_for_pda(&program_id, &pda_seed); let keys = holder.derive_keys_for_pda(&program_id, &pda_seed);
let npk = keys.generate_nullifier_public_key(); let npk = keys.generate_nullifier_public_key();
let vpk = keys.generate_viewing_public_key(); let vpk = keys.generate_viewing_public_key();
let account_id = AccountId::for_private_pda(&program_id, &pda_seed, &npk, &vpk, identifier); let account_id = PrivateAddressPlaintext::new(npk, vpk.clone(), identifier)
.pda_account_id(&program_id, &pda_seed);
self.register_shared_account( self.register_shared_account(
account_id, account_id,
@ -419,7 +420,7 @@ impl WalletCore {
let keys = holder.derive_keys_for_shared_account(&derivation_seed); let keys = holder.derive_keys_for_shared_account(&derivation_seed);
let npk = keys.generate_nullifier_public_key(); let npk = keys.generate_nullifier_public_key();
let vpk = keys.generate_viewing_public_key(); let vpk = keys.generate_viewing_public_key();
let account_id = AccountId::from((&npk, &vpk, identifier)); let account_id = PrivateAddressPlaintext::new(npk, vpk.clone(), identifier).account_id();
self.register_shared_account(account_id, group_name, identifier, None, None); self.register_shared_account(account_id, group_name, identifier, None, None);

View File

@ -696,6 +696,8 @@ impl Default for UserKeyChain {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use lee::PrivateAddressPlaintext;
use super::*; use super::*;
#[test] #[test]
@ -734,11 +736,12 @@ mod tests {
let mut user_data = UserKeyChain::default(); let mut user_data = UserKeyChain::default();
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id = AccountId::from(( let account_id = PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
0, 0,
)); )
.account_id();
let account = lee_core::account::Account::default(); let account = lee_core::account::Account::default();
user_data.add_imported_private_account(key_chain, None, 0, account); user_data.add_imported_private_account(key_chain, None, 0, account);
@ -753,11 +756,12 @@ mod tests {
let mut user_data = UserKeyChain::default(); let mut user_data = UserKeyChain::default();
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id = AccountId::from(( let account_id = PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
0, 0,
)); )
.account_id();
let account = lee_core::account::Account::default(); let account = lee_core::account::Account::default();
user_data.add_imported_private_account(key_chain, None, 0, account.clone()); user_data.add_imported_private_account(key_chain, None, 0, account.clone());
@ -802,11 +806,12 @@ mod tests {
let mut user_data = UserKeyChain::default(); let mut user_data = UserKeyChain::default();
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id = AccountId::from(( let account_id = PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key,
0, 0,
)); )
.account_id();
let new_account = lee_core::account::Account { let new_account = lee_core::account::Account {
balance: 100, balance: 100,
@ -827,11 +832,12 @@ mod tests {
let mut user_data = UserKeyChain::default(); let mut user_data = UserKeyChain::default();
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let account_id1 = AccountId::from(( let account_id1 = PrivateAddressPlaintext::new(
&key_chain.nullifier_public_key, key_chain.nullifier_public_key,
&key_chain.viewing_public_key, key_chain.viewing_public_key.clone(),
0, 0,
)); )
.account_id();
let account = lee_core::account::Account::default(); let account = lee_core::account::Account::default();
user_data.add_imported_private_account(key_chain, None, 0, account); user_data.add_imported_private_account(key_chain, None, 0, account);

View File

@ -4,9 +4,8 @@ use std::{
}; };
use lee_core::{ use lee_core::{
Identifier, InputAccountIdentity, NullifierPublicKey, InputAccountIdentity,
account::{Account, AccountId, AccountWithMetadata}, account::{Account, AccountId, AccountWithMetadata, PrivateAddressPlaintext},
encryption::ViewingPublicKey,
program::{ program::{
AccountPostState, BlockValidityWindow, ChainedCall, Claim, DEFAULT_PROGRAM_ID, AccountPostState, BlockValidityWindow, ChainedCall, Claim, DEFAULT_PROGRAM_ID,
MAX_NUMBER_CHAINED_CALLS, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow, MAX_NUMBER_CHAINED_CALLS, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow,
@ -22,15 +21,15 @@ pub struct ExecutionState {
block_validity_window: BlockValidityWindow, block_validity_window: BlockValidityWindow,
timestamp_validity_window: TimestampValidityWindow, timestamp_validity_window: TimestampValidityWindow,
/// Positions (in `pre_states`) of private-PDA accounts whose supplied npk has been bound to /// Positions (in `pre_states`) of private-PDA accounts whose supplied npk has been bound to
/// their `AccountId` via a proven `AccountId::for_private_pda(program_id, seed, npk, vpk, /// their `AccountId` via a proven
/// identifier)` check. /// `PrivateAddressPlaintext::new(npk, vpk, identifier).pda_account_id(program_id, seed)`
/// Two proof paths populate this set: a `Claim::Pda(seed)` in a program's `post_state` on /// check. Two proof paths populate this set: a `Claim::Pda(seed)` in a program's
/// that `pre_state`, or a caller's `ChainedCall.pda_seeds` entry matching that `pre_state` /// `post_state` on that `pre_state`, or a caller's `ChainedCall.pda_seeds` entry matching
/// under the private derivation. Binding is an idempotent property, not an event: the same /// that `pre_state` under the private derivation. Binding is an idempotent property, not
/// position can legitimately be bound through both paths in the same tx (e.g. a program /// an event: the same position can legitimately be bound through both paths in the same tx
/// claims a private PDA and then delegates it to a callee), and the map uses `contains_key`, /// (e.g. a program claims a private PDA and then delegates it to a callee), and the map
/// not `assert!(insert)`. After the main loop, every private-PDA position must appear in this /// uses `contains_key`, not `assert!(insert)`. After the main loop, every private-PDA
/// map; otherwise the npk is unbound and the circuit rejects. /// position must appear in this map; otherwise the npk is unbound and the circuit rejects.
/// The stored `(ProgramId, PdaSeed)` is the owner program and seed, used in /// The stored `(ProgramId, PdaSeed)` is the owner program and seed, used in
/// `compute_circuit_output` to construct `PrivateAccountKind::Pda { program_id, seed, /// `compute_circuit_output` to construct `PrivateAccountKind::Pda { program_id, seed,
/// identifier }`. /// identifier }`.
@ -44,13 +43,12 @@ pub struct ExecutionState {
/// `AccountId` entry or as an equality check against the existing one, making the rule: one /// `AccountId` entry or as an equality check against the existing one, making the rule: one
/// `(program, seed)` → one account per tx. /// `(program, seed)` → one account per tx.
pda_family_binding: HashMap<(ProgramId, PdaSeed), AccountId>, pda_family_binding: HashMap<(ProgramId, PdaSeed), AccountId>,
/// Map from a private-PDA `pre_state`'s position in `account_identities` to the (npk, vpk, /// Map from a private-PDA `pre_state`'s position in `account_identities` to the
/// identifier) supplied for that position. Built once in `derive_from_outputs` by walking /// `PrivateAddressPlaintext` supplied for that position. Built once in `derive_from_outputs`
/// `account_identities` and consulting `npk_vpk_if_private_pda`. Used later by the claim and /// by walking `account_identities` and consulting `private_pda_address`. Used later by the
/// caller-seeds authorization paths to verify /// claim and caller-seeds authorization paths to verify
/// `AccountId::for_private_pda(program_id, seed, npk, vpk, identifier) == /// `address.pda_account_id(program_id, seed) == pre_state.account_id`.
/// pre_state.account_id`. private_pda_by_position: HashMap<usize, PrivateAddressPlaintext>,
private_pda_by_position: HashMap<usize, (NullifierPublicKey, ViewingPublicKey, Identifier)>,
authorized_accounts: HashSet<AccountId>, authorized_accounts: HashSet<AccountId>,
} }
@ -61,17 +59,14 @@ impl ExecutionState {
program_id: ProgramId, program_id: ProgramId,
program_outputs: Vec<ProgramOutput>, program_outputs: Vec<ProgramOutput>,
) -> Self { ) -> Self {
// Build position → (npk, identifier) map for private-PDA pre_states, indexed by position // Build position → `PrivateAddressPlaintext` map for private-PDA pre_states, indexed by
// in `account_identities`. The vec is documented as 1:1 with the program's pre_state // position in `account_identities`. The vec is documented as 1:1 with the program's
// order, so position here matches `pre_state_position` used downstream in // pre_state order, so position here matches `pre_state_position` used downstream in
// `validate_and_sync_states`. // `validate_and_sync_states`.
let mut private_pda_by_position: HashMap< let mut private_pda_by_position: HashMap<usize, PrivateAddressPlaintext> = HashMap::new();
usize,
(NullifierPublicKey, ViewingPublicKey, Identifier),
> = HashMap::new();
for (pos, account_identity) in account_identities.iter().enumerate() { for (pos, account_identity) in account_identities.iter().enumerate() {
if let Some((npk, vpk, identifier)) = account_identity.npk_vpk_if_private_pda() { if let Some(address) = account_identity.private_pda_address() {
private_pda_by_position.insert(pos, (npk, vpk, identifier)); private_pda_by_position.insert(pos, address);
} }
} }
@ -312,19 +307,16 @@ impl ExecutionState {
let pre_state_position = self.pre_states.len(); let pre_state_position = self.pre_states.len();
let external_seed = match account_identities.get(pre_state_position) { let external_seed = match account_identities.get(pre_state_position) {
Some(InputAccountIdentity::PrivatePdaInit { Some(InputAccountIdentity::PrivatePdaInit {
npk,
vpk,
identifier,
seed: Some((seed, authority_program_id)), seed: Some((seed, authority_program_id)),
.. ..
}) => { }) => {
let expected = AccountId::for_private_pda( let expected = self
authority_program_id, .private_pda_by_position
seed, .get(&pre_state_position)
npk, .expect(
vpk, "private PDA pre_state must have an address in the position map",
*identifier, )
); .pda_account_id(authority_program_id, seed);
assert_eq!( assert_eq!(
pre_account_id, expected, pre_account_id, expected,
"External seed mismatch for PrivatePdaInit at position {pre_state_position}" "External seed mismatch for PrivatePdaInit at position {pre_state_position}"
@ -332,20 +324,16 @@ impl ExecutionState {
Some((*seed, *authority_program_id)) Some((*seed, *authority_program_id))
} }
Some(InputAccountIdentity::PrivatePdaUpdate { Some(InputAccountIdentity::PrivatePdaUpdate {
nsk,
vpk,
identifier,
seed: Some((seed, authority_program_id)), seed: Some((seed, authority_program_id)),
.. ..
}) => { }) => {
let npk = NullifierPublicKey::from(nsk); let expected = self
let expected = AccountId::for_private_pda( .private_pda_by_position
authority_program_id, .get(&pre_state_position)
seed, .expect(
&npk, "private PDA pre_state must have an address in the position map",
vpk, )
*identifier, .pda_account_id(authority_program_id, seed);
);
assert_eq!( assert_eq!(
pre_account_id, expected, pre_account_id, expected,
"External seed mismatch for PrivatePdaUpdate at position {pre_state_position}" "External seed mismatch for PrivatePdaUpdate at position {pre_state_position}"
@ -424,19 +412,13 @@ impl ExecutionState {
match claim { match claim {
Claim::Authorized => {} Claim::Authorized => {}
Claim::Pda(seed) => { Claim::Pda(seed) => {
let (npk, vpk, identifier) = self let pda = self
.private_pda_by_position .private_pda_by_position
.get(&pre_state_position) .get(&pre_state_position)
.expect( .expect(
"private PDA pre_state must have an npk in the position map", "private PDA pre_state must have an address in the position map",
); )
let pda = AccountId::for_private_pda( .pda_account_id(&program_id, &seed);
&program_id,
&seed,
npk,
vpk,
*identifier,
);
assert_eq!( assert_eq!(
pre_account_id, pda, pre_account_id, pda,
"Invalid private PDA claim for account {pre_account_id}" "Invalid private PDA claim for account {pre_account_id}"
@ -561,7 +543,7 @@ fn bind_private_pda_position(
fn resolve_authorization_and_record_bindings( fn resolve_authorization_and_record_bindings(
pda_family_binding: &mut HashMap<(ProgramId, PdaSeed), AccountId>, pda_family_binding: &mut HashMap<(ProgramId, PdaSeed), AccountId>,
private_pda_bound_positions: &mut HashMap<usize, (ProgramId, PdaSeed)>, private_pda_bound_positions: &mut HashMap<usize, (ProgramId, PdaSeed)>,
private_pda_by_position: &HashMap<usize, (NullifierPublicKey, ViewingPublicKey, Identifier)>, private_pda_by_position: &HashMap<usize, PrivateAddressPlaintext>,
authorized_accounts: &mut HashSet<AccountId>, authorized_accounts: &mut HashSet<AccountId>,
pre_account_id: AccountId, pre_account_id: AccountId,
pre_state_position: usize, pre_state_position: usize,
@ -575,10 +557,8 @@ fn resolve_authorization_and_record_bindings(
if AccountId::for_public_pda(&caller, seed) == pre_account_id { if AccountId::for_public_pda(&caller, seed) == pre_account_id {
return Some((*seed, false, caller)); return Some((*seed, false, caller));
} }
if let Some((npk, vpk, identifier)) = if let Some(address) = private_pda_by_position.get(&pre_state_position)
private_pda_by_position.get(&pre_state_position) && address.pda_account_id(&caller, seed) == pre_account_id
&& AccountId::for_private_pda(&caller, seed, npk, vpk, *identifier)
== pre_account_id
{ {
return Some((*seed, true, caller)); return Some((*seed, true, caller));
} }

View File

@ -17,7 +17,7 @@ fn main() {
program_outputs, program_outputs,
); );
let output = output::compute_circuit_output(execution_state, &account_identities); let output = output::compute_circuit_output(execution_state, account_identities);
env::commit(&output); env::commit(&output);
} }

View File

@ -2,7 +2,7 @@ use lee_core::{
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme, Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptedAccountData, EncryptionScheme,
EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey, EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey,
NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey, NullifierSecretKey, PrivacyPreservingCircuitOutput, PrivateAccountKind, SharedSecretKey,
account::{Account, AccountId, Nonce}, account::{Account, AccountId, Nonce, PrivateAddressPlaintext},
compute_digest_for_path, compute_digest_for_path,
encryption::ViewingPublicKey, encryption::ViewingPublicKey,
}; };
@ -11,7 +11,7 @@ use crate::execution_state::ExecutionState;
pub fn compute_circuit_output( pub fn compute_circuit_output(
execution_state: ExecutionState, execution_state: ExecutionState,
account_identities: &[InputAccountIdentity], account_identities: Vec<InputAccountIdentity>,
) -> PrivacyPreservingCircuitOutput { ) -> PrivacyPreservingCircuitOutput {
let (block_validity_window, timestamp_validity_window, pda_seed_by_position, states_iter) = let (block_validity_window, timestamp_validity_window, pda_seed_by_position, states_iter) =
execution_state.into_parts(); execution_state.into_parts();
@ -33,7 +33,7 @@ pub fn compute_circuit_output(
let mut output_index = 0; let mut output_index = 0;
for (pos, (account_identity, (pre_state, post_state))) in for (pos, (account_identity, (pre_state, post_state))) in
account_identities.iter().zip(states_iter).enumerate() account_identities.into_iter().zip(states_iter).enumerate()
{ {
match account_identity { match account_identity {
InputAccountIdentity::Public => { InputAccountIdentity::Public => {
@ -46,8 +46,9 @@ pub fn compute_circuit_output(
nsk, nsk,
identifier, identifier,
} => { } => {
let npk = NullifierPublicKey::from(nsk); let address =
let account_id = AccountId::for_regular_private_account(&npk, vpk, *identifier); PrivateAddressPlaintext::new(NullifierPublicKey::from(&nsk), vpk, identifier);
let account_id = address.account_id();
assert_eq!(account_id, pre_state.account_id, "AccountId mismatch"); assert_eq!(account_id, pre_state.account_id, "AccountId mismatch");
assert!( assert!(
@ -71,10 +72,10 @@ pub fn compute_circuit_output(
&mut output_index, &mut output_index,
post_state, post_state,
&account_id, &account_id,
&PrivateAccountKind::Regular(*identifier), &PrivateAccountKind::Regular(address.identifier),
&npk, &address.npk,
vpk, &address.vpk,
random_seed, &random_seed,
new_nullifier, new_nullifier,
new_nonce, new_nonce,
); );
@ -86,8 +87,9 @@ pub fn compute_circuit_output(
membership_proof, membership_proof,
identifier, identifier,
} => { } => {
let npk = NullifierPublicKey::from(nsk); let address =
let account_id = AccountId::for_regular_private_account(&npk, vpk, *identifier); PrivateAddressPlaintext::new(NullifierPublicKey::from(&nsk), vpk, identifier);
let account_id = address.account_id();
assert_eq!(account_id, pre_state.account_id, "AccountId mismatch"); assert_eq!(account_id, pre_state.account_id, "AccountId mismatch");
assert!( assert!(
@ -96,22 +98,25 @@ pub fn compute_circuit_output(
); );
let new_nullifier = compute_update_nullifier_and_set_digest( let new_nullifier = compute_update_nullifier_and_set_digest(
membership_proof, &membership_proof,
&pre_state.account, &pre_state.account,
&account_id, &account_id,
nsk, &nsk,
); );
let new_nonce = pre_state.account.nonce.private_account_nonce_increment(nsk); let new_nonce = pre_state
.account
.nonce
.private_account_nonce_increment(&nsk);
emit_private_output( emit_private_output(
&mut output, &mut output,
&mut output_index, &mut output_index,
post_state, post_state,
&account_id, &account_id,
&PrivateAccountKind::Regular(*identifier), &PrivateAccountKind::Regular(address.identifier),
&npk, &address.npk,
vpk, &address.vpk,
random_seed, &random_seed,
new_nullifier, new_nullifier,
new_nonce, new_nonce,
); );
@ -122,7 +127,8 @@ pub fn compute_circuit_output(
npk, npk,
identifier, identifier,
} => { } => {
let account_id = AccountId::for_regular_private_account(npk, vpk, *identifier); let address = PrivateAddressPlaintext::new(npk, vpk, identifier);
let account_id = address.account_id();
assert_eq!(account_id, pre_state.account_id, "AccountId mismatch"); assert_eq!(account_id, pre_state.account_id, "AccountId mismatch");
assert_eq!( assert_eq!(
@ -146,10 +152,10 @@ pub fn compute_circuit_output(
&mut output_index, &mut output_index,
post_state, post_state,
&account_id, &account_id,
&PrivateAccountKind::Regular(*identifier), &PrivateAccountKind::Regular(address.identifier),
npk, &address.npk,
vpk, &address.vpk,
random_seed, &random_seed,
new_nullifier, new_nullifier,
new_nonce, new_nonce,
); );
@ -195,11 +201,11 @@ pub fn compute_circuit_output(
&PrivateAccountKind::Pda { &PrivateAccountKind::Pda {
program_id: *authority_program_id, program_id: *authority_program_id,
seed: *seed, seed: *seed,
identifier: *identifier, identifier,
}, },
npk, &npk,
vpk, &vpk,
random_seed, &random_seed,
new_nullifier, new_nullifier,
new_nonce, new_nonce,
); );
@ -223,15 +229,18 @@ pub fn compute_circuit_output(
); );
let new_nullifier = compute_update_nullifier_and_set_digest( let new_nullifier = compute_update_nullifier_and_set_digest(
membership_proof, &membership_proof,
&pre_state.account, &pre_state.account,
&pre_state.account_id, &pre_state.account_id,
nsk, &nsk,
); );
let new_nonce = pre_state.account.nonce.private_account_nonce_increment(nsk); let new_nonce = pre_state
.account
.nonce
.private_account_nonce_increment(&nsk);
let account_id = pre_state.account_id; let account_id = pre_state.account_id;
let npk = NullifierPublicKey::from(nsk); let npk = NullifierPublicKey::from(&nsk);
let (authority_program_id, seed) = pda_seed_by_position let (authority_program_id, seed) = pda_seed_by_position
.get(&pos) .get(&pos)
.expect("PrivatePdaUpdate position must be in pda_seed_by_position"); .expect("PrivatePdaUpdate position must be in pda_seed_by_position");
@ -243,11 +252,11 @@ pub fn compute_circuit_output(
&PrivateAccountKind::Pda { &PrivateAccountKind::Pda {
program_id: *authority_program_id, program_id: *authority_program_id,
seed: *seed, seed: *seed,
identifier: *identifier, identifier,
}, },
&npk, &npk,
vpk, &vpk,
random_seed, &random_seed,
new_nullifier, new_nullifier,
new_nonce, new_nonce,
); );

View File

@ -4,7 +4,7 @@ use anyhow::{Context as _, Result};
use bytesize::ByteSize; use bytesize::ByteSize;
use indexer_service::{ChannelId, ClientConfig, IndexerConfig}; use indexer_service::{ChannelId, ClientConfig, IndexerConfig};
use key_protocol::key_management::KeyChain; use key_protocol::key_management::KeyChain;
use lee::{AccountId, PrivateKey, PublicKey}; use lee::{AccountId, PrivateAddressPlaintext, PrivateKey, PublicKey};
use lee_core::Identifier; use lee_core::Identifier;
use sequencer_core::config::{BedrockConfig, GenesisAction, SequencerConfig}; use sequencer_core::config::{BedrockConfig, GenesisAction, SequencerConfig};
use url::Url; use url::Url;
@ -23,11 +23,12 @@ pub struct InitialPrivateAccountForWallet {
impl InitialPrivateAccountForWallet { impl InitialPrivateAccountForWallet {
#[must_use] #[must_use]
pub fn account_id(&self) -> AccountId { pub fn account_id(&self) -> AccountId {
AccountId::from(( PrivateAddressPlaintext::new(
&self.key_chain.nullifier_public_key, self.key_chain.nullifier_public_key,
&self.key_chain.viewing_public_key, self.key_chain.viewing_public_key.clone(),
self.identifier, self.identifier,
)) )
.account_id()
} }
} }

View File

@ -12,7 +12,7 @@ use criterion::{Criterion, criterion_group, criterion_main};
use key_protocol::key_management::KeyChain; use key_protocol::key_management::KeyChain;
use lee_core::{ use lee_core::{
Commitment, EncryptionScheme, SharedSecretKey, Commitment, EncryptionScheme, SharedSecretKey,
account::{Account, AccountId}, account::{Account, PrivateAddressPlaintext},
program::PrivateAccountKind, program::PrivateAccountKind,
}; };
@ -49,7 +49,7 @@ fn bench_encryption(c: &mut Criterion) {
let npk = recipient_kc.nullifier_public_key; let npk = recipient_kc.nullifier_public_key;
let account = Account::default(); let account = Account::default();
let account_id = let account_id =
AccountId::for_regular_private_account(&npk, &recipient_kc.viewing_public_key, 0); PrivateAddressPlaintext::new(npk, recipient_kc.viewing_public_key.clone(), 0).account_id();
let commitment = Commitment::new(&account_id, &account); let commitment = Commitment::new(&account_id, &account);
let (shared, _epk) = SharedSecretKey::encapsulate(&recipient_kc.viewing_public_key); let (shared, _epk) = SharedSecretKey::encapsulate(&recipient_kc.viewing_public_key);
let kind = PrivateAccountKind::Regular(0_u128); let kind = PrivateAccountKind::Regular(0_u128);