update nullifiers and tags

This commit is contained in:
jonesmarvin8 2026-04-02 13:09:20 -04:00
parent d7adc391a4
commit 15a41375e4
8 changed files with 48 additions and 41 deletions

View File

@ -212,6 +212,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
let sender_vsk = [99; 32];
let sender_vpk = ViewingPublicKey::from_scalar(sender_vsk);
let sender_npk = NullifierPublicKey::from(&sender_nsk);
let sender_id = AccountId::account_id_without_identifier(&sender_npk);
let sender_pre = AccountWithMetadata::new(
Account {
balance: 100,
@ -226,6 +227,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
let recipient_vsk = [99; 32];
let recipient_vpk = ViewingPublicKey::from_scalar(recipient_vsk);
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
let recipient_id = AccountId::account_id_without_identifier(&recipient_npk);
let recipient_pre =
AccountWithMetadata::new(Account::default(), false, AccountId::account_id_without_identifier(&recipient_npk));
@ -250,8 +252,8 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
Program::serialize_instruction(balance_to_move).unwrap(),
vec![1, 2],
vec![
(sender_npk.clone(), sender_ss),
(recipient_npk.clone(), recipient_ss),
(sender_npk, sender_ss),
(recipient_npk, recipient_ss),
],
vec![sender_nsk],
vec![Some(proof)],
@ -262,8 +264,8 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
vec![],
vec![],
vec![
(sender_npk, sender_vpk, sender_epk),
(recipient_npk, recipient_vpk, recipient_epk),
(sender_id, sender_vpk, sender_epk),
(recipient_id, recipient_vpk, recipient_epk),
],
output,
)

View File

@ -27,7 +27,6 @@ pub type ViewingSecretKey = Scalar;
#[derive(Serialize, Deserialize, Debug, Clone)]
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
/// for recepient.
#[expect(clippy::partial_pub_fields, reason = "TODO: fix later")]
pub struct PrivateKeyHolder {
pub nullifier_secret_key: NullifierSecretKey,
pub viewing_secret_key: ViewingSecretKey,

View File

@ -2,7 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
use risc0_zkvm::sha::{Impl, Sha256 as _};
use serde::{Deserialize, Serialize};
use crate::{Commitment};
use crate::{Commitment, account::AccountId};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(any(feature = "host", test), derive(Clone, Hash))]
@ -68,10 +68,10 @@ impl Nullifier {
/// Computes a nullifier for an account initialization.
#[must_use]
pub fn for_account_initialization(npk: &NullifierPublicKey) -> Self {
pub fn for_account_initialization(account_id: &AccountId) -> Self {
const INIT_PREFIX: &[u8; 32] = b"/LEE/v0.3/Nullifier/Initialize/\x00";
let mut bytes = INIT_PREFIX.to_vec();
bytes.extend_from_slice(&npk.to_byte_array());
bytes.extend_from_slice(account_id.value());
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
}
}
@ -98,11 +98,11 @@ mod tests {
112, 188, 193, 129, 150, 55, 228, 67, 88, 168, 29, 151, 5, 92, 23, 190, 17, 162, 164,
255, 29, 105, 42, 186, 43, 11, 157, 168, 132, 225, 17, 163,
]);
let expected_nullifier = Nullifier([
149, 59, 95, 181, 2, 194, 20, 143, 72, 233, 104, 243, 59, 70, 67, 243, 110, 77, 109,
132, 139, 111, 51, 125, 128, 92, 107, 46, 252, 4, 20, 149,
]);
let nullifier = Nullifier::for_account_initialization(&npk);
let account_id = AccountId::account_id_with_identifier(&npk, 0_u128);
let expected_nullifier = Nullifier([63, 58, 51, 159, 15, 100, 240, 243, 60, 143, 151, 108, 116, 144, 101, 6, 134, 72, 198, 249, 108, 80, 237, 194, 143, 66, 225, 191, 111, 49, 66, 54]);
let nullifier = Nullifier::for_account_initialization(&account_id);
assert_eq!(nullifier, expected_nullifier);
}

View File

@ -266,6 +266,7 @@ mod tests {
let program = Program::authenticated_transfer_program();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let recipient_id = AccountId::account_id_without_identifier(&test_private_account_keys_2().npk());
let sender_nonce = Nonce(0xdead_beef);
let sender_pre = AccountWithMetadata::new(
@ -296,7 +297,7 @@ mod tests {
commitment_set.digest(),
),
(
Nullifier::for_account_initialization(&recipient_keys.npk()),
Nullifier::for_account_initialization(&recipient_id),
DUMMY_COMMITMENT_HASH,
),
];

View File

@ -1,6 +1,6 @@
use borsh::{BorshDeserialize, BorshSerialize};
use nssa_core::{
Commitment, CommitmentSetDigest, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitOutput,
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput,
account::{Account, Nonce},
encryption::{Ciphertext, EphemeralPublicKey, ViewingPublicKey},
program::ValidityWindow,
@ -21,11 +21,11 @@ pub struct EncryptedAccountData {
impl EncryptedAccountData {
fn new(
ciphertext: Ciphertext,
npk: &NullifierPublicKey,
account_id: &AccountId,
vpk: &ViewingPublicKey,
epk: EphemeralPublicKey,
) -> Self {
let view_tag = Self::compute_view_tag(npk, vpk);
let view_tag = Self::compute_view_tag(account_id, vpk);
Self {
ciphertext,
epk,
@ -35,10 +35,13 @@ impl EncryptedAccountData {
/// Computes the tag as the first byte of SHA256("/LEE/v0.3/ViewTag/" || Npk || vpk).
#[must_use]
pub fn compute_view_tag(npk: &NullifierPublicKey, vpk: &ViewingPublicKey) -> ViewTag {
pub fn compute_view_tag(account_id: &AccountId, vpk: &ViewingPublicKey) -> ViewTag {
const VIEWTAG_PREFIX: &[u8; 32] =
b"/LEE/v0.3/ViewTag\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let mut hasher = Sha256::new();
hasher.update(b"/LEE/v0.3/ViewTag/");
hasher.update(npk.to_byte_array());
hasher.update(VIEWTAG_PREFIX);
hasher.update(account_id.to_bytes());
hasher.update(vpk.to_bytes());
let digest: [u8; 32] = hasher.finalize().into();
digest[0]
@ -88,7 +91,7 @@ impl Message {
pub fn try_from_circuit_output(
public_account_ids: Vec<AccountId>,
nonces: Vec<Nonce>,
public_keys: Vec<(NullifierPublicKey, ViewingPublicKey, EphemeralPublicKey)>,
public_keys: Vec<(AccountId, ViewingPublicKey, EphemeralPublicKey)>, //TODO: Rename `public_keys` to account for `account_id`.
output: PrivacyPreservingCircuitOutput,
) -> Result<Self, NssaError> {
if public_keys.len() != output.ciphertexts.len() {
@ -101,8 +104,8 @@ impl Message {
.ciphertexts
.into_iter()
.zip(public_keys)
.map(|(ciphertext, (npk, vpk, epk))| {
EncryptedAccountData::new(ciphertext, &npk, &vpk, epk)
.map(|(ciphertext, (account_id, vpk, epk))| {
EncryptedAccountData::new(ciphertext, &account_id, &vpk, epk)
})
.collect();
Ok(Self {
@ -183,7 +186,7 @@ pub mod tests {
let epk = EphemeralPublicKey::from_scalar(esk);
let ciphertext = EncryptionScheme::encrypt(&account, &shared_secret, &commitment, 2);
let encrypted_account_data =
EncryptedAccountData::new(ciphertext.clone(), &npk, &vpk, epk.clone());
EncryptedAccountData::new(ciphertext.clone(), &account_id, &vpk, epk.clone());
let expected_view_tag = {
let mut hasher = Sha256::new();
@ -198,7 +201,7 @@ pub mod tests {
assert_eq!(encrypted_account_data.epk, epk);
assert_eq!(
encrypted_account_data.view_tag,
EncryptedAccountData::compute_view_tag(&npk, &vpk)
EncryptedAccountData::compute_view_tag(&account_id, &vpk)
);
assert_eq!(encrypted_account_data.view_tag, expected_view_tag);
}

View File

@ -953,7 +953,7 @@ pub mod tests {
let message = Message::try_from_circuit_output(
vec![sender_keys.account_id()],
vec![sender_nonce],
vec![(recipient_keys.npk(), recipient_keys.vpk(), epk)],
vec![(recipient_id, recipient_keys.vpk(), epk)],
output,
)
.unwrap();
@ -1004,8 +1004,8 @@ pub mod tests {
vec![],
vec![],
vec![
(sender_keys.npk(), sender_keys.vpk(), epk_1),
(recipient_keys.npk(), recipient_keys.vpk(), epk_2),
(sender_id, sender_keys.vpk(), epk_1),
(recipient_id, recipient_keys.vpk(), epk_2),
],
output,
)
@ -1052,7 +1052,7 @@ pub mod tests {
let message = Message::try_from_circuit_output(
vec![*recipient_account_id],
vec![],
vec![(sender_keys.npk(), sender_keys.vpk(), epk)],
vec![(sender_id, sender_keys.vpk(), epk)],
output,
)
.unwrap();
@ -2557,8 +2557,8 @@ pub mod tests {
vec![],
vec![],
vec![
(to_keys.npk(), to_keys.vpk(), to_epk),
(from_keys.npk(), from_keys.vpk(), from_epk),
(to_account_id, to_keys.vpk(), to_epk),
(from_account_id, from_keys.vpk(), from_epk),
],
output,
)
@ -2791,7 +2791,7 @@ pub mod tests {
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![(private_keys.npk(), private_keys.vpk(), epk)],
vec![(account_id, private_keys.vpk(), epk)],
output,
)
.unwrap();
@ -2802,7 +2802,7 @@ pub mod tests {
let result = state.transition_from_privacy_preserving_transaction(&tx, 1);
assert!(result.is_ok());
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
let nullifier = Nullifier::for_account_initialization(&account_id);
assert!(state.private_state.1.contains(&nullifier));
}
@ -2842,7 +2842,7 @@ pub mod tests {
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![(private_keys.npk(), private_keys.vpk(), epk)],
vec![(account_id, private_keys.vpk(), epk)],
output,
)
.unwrap();
@ -2858,7 +2858,7 @@ pub mod tests {
);
// Verify the account is now initialized (nullifier exists)
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
let nullifier = Nullifier::for_account_initialization(&account_id);
assert!(state.private_state.1.contains(&nullifier));
// Prepare new state of account
@ -3111,7 +3111,8 @@ pub mod tests {
let validity_window: ValidityWindow = validity_window.try_into().unwrap();
let validity_window_program = Program::validity_window();
let account_keys = test_private_account_keys_1();
let pre = AccountWithMetadata::new(Account::default(), false, &account_keys.npk());
let account_id = AccountId::account_id_without_identifier(&account_keys.npk());
let pre = AccountWithMetadata::new(Account::default(), false, account_id);
let mut state = V03State::new_with_genesis_accounts(&[], &[]).with_test_programs();
let tx = {
let esk = [3; 32];
@ -3132,7 +3133,7 @@ pub mod tests {
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![(account_keys.npk(), account_keys.vpk(), epk)],
vec![(account_id, account_keys.vpk(), epk)],
output,
)
.unwrap();

View File

@ -317,7 +317,8 @@ fn compute_circuit_output(
"Membership proof must be None for unauthorized accounts"
);
let nullifier = Nullifier::for_account_initialization(npk);
let account_id = AccountId::account_id_without_identifier(npk);
let nullifier = Nullifier::for_account_initialization(&account_id);
let new_nonce = Nonce::private_account_nonce_init(npk);
@ -386,7 +387,7 @@ fn compute_nullifier_and_set_digest(
);
// Compute initialization nullifier
let nullifier = Nullifier::for_account_initialization(npk);
let nullifier = Nullifier::for_account_initialization(&account_id);
(nullifier, DUMMY_COMMITMENT_HASH)
},
|membership_proof| {

View File

@ -393,7 +393,7 @@ impl WalletCore {
Vec::from_iter(acc_manager.public_account_nonces()),
private_account_keys
.iter()
.map(|keys| (keys.npk.clone(), keys.vpk.clone(), keys.epk.clone()))
.map(|keys| (AccountId::account_id_without_identifier(&keys.npk.clone()), keys.vpk.clone(), keys.epk.clone()))
.collect(),
output,
)
@ -484,7 +484,7 @@ impl WalletCore {
let affected_accounts = private_account_key_chains
.flat_map(|(acc_account_id, key_chain, index)| {
let view_tag = EncryptedAccountData::compute_view_tag(
&key_chain.nullifier_public_key,
&AccountId::account_id_without_identifier(&key_chain.nullifier_public_key),
&key_chain.viewing_public_key,
);