This commit is contained in:
Sergio Chouhy 2025-08-18 07:39:41 -03:00
parent 1a10dade25
commit b20a97e5a1
9 changed files with 242 additions and 15 deletions

View File

@ -1,2 +1,13 @@
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
use serde::{Deserialize, Serialize};
use crate::account::{Account, NullifierPublicKey};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Commitment([u8; 32]);
impl Commitment {
pub fn new(Npk: &NullifierPublicKey, account: &Account) -> Self {
todo!()
}
}

View File

@ -6,7 +6,7 @@ mod commitment;
mod nullifier;
pub use commitment::Commitment;
pub use nullifier::Nullifier;
pub use nullifier::{Nullifier, NullifierPublicKey, NullifierSecretKey};
pub type Nonce = u128;
type Data = Vec<u8>;

View File

@ -1,2 +1,23 @@
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
use serde::{Deserialize, Serialize};
use crate::account::Commitment;
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct NullifierPublicKey([u8; 32]);
impl From<&NullifierSecretKey> for NullifierPublicKey {
fn from(_value: &NullifierSecretKey) -> Self {
todo!()
}
}
pub type NullifierSecretKey = [u8; 32];
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Nullifier([u8; 32]);
impl Nullifier {
pub fn new(commitment: &Commitment, nsk: &NullifierSecretKey) -> Self {
todo!()
}
}

View File

@ -1,2 +1,79 @@
use serde::{Deserialize, Serialize};
use crate::{
account::{
Account, AccountWithMetadata, Commitment, Nonce, Nullifier, NullifierPublicKey,
NullifierSecretKey,
},
program::{ProgramId, ProgramOutput},
};
pub mod account;
pub mod program;
pub type CommitmentSetDigest = [u32; 8];
pub type MembershipProof = Vec<[u8; 32]>;
pub fn verify_membership_proof(
commitment: &Commitment,
proof: &MembershipProof,
digest: &CommitmentSetDigest,
) -> bool {
todo!()
}
pub type IncomingViewingPublicKey = [u8; 32];
pub type EphemeralSecretKey = [u8; 32];
pub struct EphemeralPublicKey;
impl From<&EphemeralSecretKey> for EphemeralPublicKey {
fn from(value: &EphemeralSecretKey) -> Self {
todo!()
}
}
pub struct Tag(u8);
impl Tag {
pub fn new(Npk: &NullifierPublicKey, Ipk: &IncomingViewingPublicKey) -> Self {
todo!()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct EncryptedAccountData;
impl EncryptedAccountData {
pub fn new(
account: &Account,
esk: &EphemeralSecretKey,
Npk: &NullifierPublicKey,
Ivk: &IncomingViewingPublicKey,
) -> Self {
// TODO: implement
Self
}
}
#[derive(Serialize, Deserialize)]
pub struct PrivacyPreservingCircuitInput {
pub program_output: ProgramOutput,
pub visibility_mask: Vec<u8>,
pub private_account_data: Vec<(
Nonce,
NullifierPublicKey,
IncomingViewingPublicKey,
EphemeralSecretKey,
)>,
pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>,
pub program_id: ProgramId,
pub commitment_set_digest: CommitmentSetDigest,
}
#[derive(Serialize, Deserialize)]
pub struct PrivacyPreservingCircuitOutput {
pub public_pre_states: Vec<AccountWithMetadata>,
pub public_post_states: Vec<Account>,
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
pub new_commitments: Vec<Commitment>,
pub new_nullifiers: Vec<Nullifier>,
pub commitment_set_digest: CommitmentSetDigest,
}

View File

@ -0,0 +1,118 @@
use risc0_zkvm::{guest::env, serde::to_vec};
use nssa_core::{
account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey},
program::{validate_execution, ProgramOutput},
verify_membership_proof, EncryptedAccountData, EphemeralPublicKey, EphemeralSecretKey,
IncomingViewingPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, Tag,
};
fn main() {
let PrivacyPreservingCircuitInput {
program_output,
visibility_mask,
private_account_data,
private_account_auth,
program_id,
commitment_set_digest,
} = env::read();
// TODO: Check that `program_execution_proof` is one of the allowed built-in programs
// assert!(BUILTIN_PROGRAM_IDS.contains(executing_program_id));
// Check that `program_output` is consistent with the execution of the corresponding program.
env::verify(program_id, &to_vec(&program_output).unwrap()).unwrap();
let ProgramOutput {
pre_states,
post_states,
} = program_output;
// Check that the program is well behaved.
// See the # Programs section for the definition of the `validate_execution` method.
validate_execution(&pre_states, &post_states, program_id);
let n_accounts = pre_states.len();
assert_eq!(visibility_mask.len(), n_accounts);
let n_private_accounts = visibility_mask.iter().filter(|&&flag| flag != 0).count();
assert_eq!(private_account_data.len(), n_private_accounts);
let n_auth_private_accounts = visibility_mask.iter().filter(|&&flag| flag == 1).count();
assert_eq!(private_account_auth.len(), n_auth_private_accounts);
// These lists will be the public outputs of this circuit
// and will be populated next.
let mut public_pre_states: Vec<AccountWithMetadata> = Vec::new();
let mut public_post_states: Vec<Account> = Vec::new();
let mut encrypted_private_post_states: Vec<EncryptedAccountData> = Vec::new();
let mut new_commitments: Vec<Commitment> = Vec::new();
let mut new_nullifiers: Vec<Nullifier> = Vec::new();
for i in 0..n_accounts {
// visibility_mask[i] equal to 0 means public
if visibility_mask[i] == 0 {
// If the account is marked as public, add the pre and post
// states to the corresponding lists.
public_pre_states.push(pre_states[i].clone());
public_post_states.push(post_states[i].clone());
} else {
let (new_nonce, Npk, Ipk, esk) = &private_account_data[i];
// Verify authentication
if visibility_mask[i] == 1 {
let (nsk, membership_proof) = &private_account_auth[i];
// 1. Compute Npk from the provided nsk and assert it is equal to the provided Npk
let expected_Npk = NullifierPublicKey::from(nsk);
assert_eq!(&expected_Npk, Npk);
// 2. Compute the commitment of the pre_state account using the provided Npk
let commitment_pre = Commitment::new(Npk, &pre_states[i].account);
// 3. Verify that the commitment belongs to the global commitment set
assert!(verify_membership_proof(
&commitment_pre,
membership_proof,
&commitment_set_digest,
));
// At this point the account is correctly authenticated as a private account.
// Assert that `pre_states` marked this account as authenticated.
assert!(pre_states[i].is_authorized);
// Compute the nullifier of the pre state version of this private account
// and include it in the `new_nullifiers` list.
let nullifier = Nullifier::new(&commitment_pre, nsk);
new_nullifiers.push(nullifier);
} else if visibility_mask[i] == 2 {
assert_eq!(pre_states[i].account, Account::default());
assert!(!pre_states[i].is_authorized);
} else {
panic!();
}
// Update the nonce for the post state of this private account.
let mut post_with_updated_nonce = post_states[i].clone();
post_with_updated_nonce.nonce = *new_nonce;
// Compute the commitment of the post state of the private account,
// with the updated nonce, and include it in the `new_commitments` list.
let commitment_post = Commitment::new(Npk, &post_with_updated_nonce);
new_commitments.push(commitment_post);
// Encrypt the post state of the private account with the updated
// nonce and include it in the `encrypted_private_post_states` list.
//
let encrypted_account = EncryptedAccountData::new(&post_with_updated_nonce, esk, Npk, Ipk);
encrypted_private_post_states.push(encrypted_account);
}
}
let output = PrivacyPreservingCircuitOutput {
public_pre_states,
public_post_states,
encrypted_private_post_states,
new_commitments,
new_nullifiers,
commitment_set_digest,
};
env::commit(&output);
}

View File

@ -1,10 +1,10 @@
use nssa_core::account::{Account, Commitment, Nonce, Nullifier};
use nssa_core::{
EncryptedAccountData,
account::{Account, Commitment, Nonce, Nullifier},
};
use crate::Address;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncryptedAccountData;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message {
pub(crate) public_addresses: Vec<Address>,

View File

@ -1,10 +1,9 @@
use std::collections::{HashMap, HashSet};
use nssa_core::{CommitmentSetDigest, EncryptedAccountData};
use nssa_core::account::{Account, AccountWithMetadata};
use crate::error::NssaError;
use crate::privacy_preserving_transaction::message::EncryptedAccountData;
use crate::state::CommitmentSetDigest;
use crate::{Address, V01State};
use super::message::Message;
@ -45,7 +44,7 @@ impl PrivacyPreservingTransaction {
));
}
// Check there are no duplicate commitments in the new_nullifiers list
// Check there are no duplicate commitments in the new_commitments list
if n_unique(&message.new_commitments) != message.new_commitments.len() {
return Err(NssaError::InvalidInput(
"Duplicate commitments found in message".into(),
@ -61,7 +60,7 @@ impl PrivacyPreservingTransaction {
}
// Check the signatures are valid
if !witness_set.is_valid_for(message) {
if !witness_set.signatures_are_valid_for(message) {
return Err(NssaError::InvalidInput(
"Invalid signature for given message and public key".into(),
));
@ -89,7 +88,7 @@ impl PrivacyPreservingTransaction {
let set_commitment = state.commitment_set_digest();
// 4. Proof verification
check_privacy_preserving_circuit_execution_proof_is_valid(
check_privacy_preserving_circuit_proof_is_valid(
witness_set.proof,
&public_pre_states,
&message.public_post_states,
@ -130,7 +129,7 @@ impl PrivacyPreservingTransaction {
}
}
fn check_privacy_preserving_circuit_execution_proof_is_valid(
fn check_privacy_preserving_circuit_proof_is_valid(
proof: (),
public_pre_states: &[AccountWithMetadata],
public_post_states: &[Account],

View File

@ -15,7 +15,7 @@ impl WitnessSet {
todo!()
}
pub fn is_valid_for(&self, message: &Message) -> bool {
pub fn signatures_are_valid_for(&self, message: &Message) -> bool {
todo!()
}

View File

@ -4,13 +4,13 @@ use crate::{
public_transaction::PublicTransaction,
};
use nssa_core::{
CommitmentSetDigest,
account::{Account, Commitment, Nullifier},
program::{DEFAULT_PROGRAM_ID, ProgramId},
};
use std::collections::{HashMap, HashSet};
struct CommitmentSet(HashSet<Commitment>);
pub type CommitmentSetDigest = [u32; 8];
impl CommitmentSet {
fn extend(&mut self, commitments: Vec<Commitment>) {
@ -22,6 +22,7 @@ impl CommitmentSet {
[0; 8]
}
}
type NullifierSet = HashSet<Nullifier>;
pub struct V01State {