diff --git a/nssa/core/src/circuit_io.rs b/nssa/core/src/circuit_io.rs index 6370dc6..197f0eb 100644 --- a/nssa/core/src/circuit_io.rs +++ b/nssa/core/src/circuit_io.rs @@ -10,7 +10,7 @@ use crate::{ #[derive(Serialize, Deserialize)] pub struct PrivacyPreservingCircuitInput { - pub program_output: ProgramOutput, + pub program_outputs: Vec, pub visibility_mask: Vec, pub private_account_nonces: Vec, pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>, diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index d8ed15d..b7f70cf 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -3,17 +3,12 @@ use std::collections::HashSet; use risc0_zkvm::{guest::env, serde::to_vec}; use nssa_core::{ - Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptionScheme, - Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, - account::{Account, AccountId, AccountWithMetadata}, - compute_digest_for_path, - encryption::Ciphertext, - program::{DEFAULT_PROGRAM_ID, ProgramOutput, validate_execution}, + account::{Account, AccountId, AccountWithMetadata}, compute_digest_for_path, encryption::Ciphertext, program::{validate_execution, ProgramId, ProgramOutput, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, EncryptionScheme, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, DUMMY_COMMITMENT_HASH }; fn main() { let PrivacyPreservingCircuitInput { - program_output, + program_outputs, visibility_mask, private_account_nonces, private_account_keys, @@ -21,9 +16,43 @@ fn main() { program_id, } = env::read(); - // Check that `program_output` is consistent with the execution of the corresponding program. - env::verify(program_id, &to_vec(&program_output).unwrap()).unwrap(); + // These lists will be the public outputs of this circuit + // and will be populated next. + let mut public_pre_states: Vec = Vec::new(); + let mut public_post_states: Vec = Vec::new(); + let mut ciphertexts: Vec = Vec::new(); + let mut new_commitments: Vec = Vec::new(); + let mut new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)> = Vec::new(); + for program_output in program_outputs.iter() { + // Check that `program_output` is consistent with the execution of the corresponding program. + env::verify(program_id, &to_vec(program_output).unwrap()).unwrap(); + } + + if private_nonces_iter.next().is_some() { + panic!("Too many nonces."); + } + + if private_keys_iter.next().is_some() { + panic!("Too many private account keys."); + } + + if private_auth_iter.next().is_some() { + panic!("Too many private account authentication keys."); + } + + let output = PrivacyPreservingCircuitOutput { + public_pre_states, + public_post_states, + ciphertexts, + new_commitments, + new_nullifiers, + }; + + env::commit(&output); +} + +fn validate_program_execution(program_output: &ProgramOutput, program_id: ProgramId) { let ProgramOutput { pre_states, post_states, @@ -51,14 +80,6 @@ fn main() { panic!("Invalid visibility mask length"); } - // These lists will be the public outputs of this circuit - // and will be populated next. - let mut public_pre_states: Vec = Vec::new(); - let mut public_post_states: Vec = Vec::new(); - let mut ciphertexts: Vec = Vec::new(); - let mut new_commitments: Vec = Vec::new(); - let mut new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)> = Vec::new(); - let mut private_nonces_iter = private_account_nonces.iter(); let mut private_keys_iter = private_account_keys.iter(); let mut private_auth_iter = private_account_auth.iter(); @@ -152,28 +173,6 @@ fn main() { _ => panic!("Invalid visibility mask value"), } } - - if private_nonces_iter.next().is_some() { - panic!("Too many nonces."); - } - - if private_keys_iter.next().is_some() { - panic!("Too many private account keys."); - } - - if private_auth_iter.next().is_some() { - panic!("Too many private account authentication keys."); - } - - let output = PrivacyPreservingCircuitOutput { - public_pre_states, - public_post_states, - ciphertexts, - new_commitments, - new_nullifiers, - }; - - env::commit(&output); } fn validate_uniqueness_of_account_ids(pre_states: &[AccountWithMetadata]) -> bool { diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 9ce0610..7628e78 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -6,7 +6,7 @@ use nssa_core::{ }; use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover}; -use crate::{error::NssaError, program::Program}; +use crate::{error::NssaError, program::Program, state::MAX_NUMBER_CHAINED_CALLS}; use crate::program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID}; @@ -25,15 +25,29 @@ pub fn execute_and_prove( private_account_auth: &[(NullifierSecretKey, MembershipProof)], program: &Program, ) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> { - let inner_receipt = execute_and_prove_program(program, pre_states, instruction_data)?; + let mut env_builder = ExecutorEnv::builder(); + let mut program_outputs = Vec::new(); + for _i in 0..MAX_NUMBER_CHAINED_CALLS { + let inner_receipt = execute_and_prove_program(program, pre_states, instruction_data)?; - let program_output: ProgramOutput = inner_receipt - .journal - .decode() - .map_err(|e| NssaError::ProgramOutputDeserializationError(e.to_string()))?; + let program_output: ProgramOutput = inner_receipt + .journal + .decode() + .map_err(|e| NssaError::ProgramOutputDeserializationError(e.to_string()))?; + + // TODO: remove clone + program_outputs.push(program_output.clone()); + + // Prove circuit. + env_builder.add_assumption(inner_receipt); + + if program_output.chained_call.is_none() { + break; + } + } let circuit_input = PrivacyPreservingCircuitInput { - program_output, + program_outputs, visibility_mask: visibility_mask.to_vec(), private_account_nonces: private_account_nonces.to_vec(), private_account_keys: private_account_keys.to_vec(), @@ -41,9 +55,6 @@ pub fn execute_and_prove( program_id: program.id(), }; - // Prove circuit. - let mut env_builder = ExecutorEnv::builder(); - env_builder.add_assumption(inner_receipt); env_builder.write(&circuit_input).unwrap(); let env = env_builder.build().unwrap(); let prover = default_prover(); diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index d118d0c..27be904 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -8,9 +8,7 @@ use nssa_core::{ use sha2::{Digest, digest::FixedOutput}; use crate::{ - V02State, - error::NssaError, - public_transaction::{Message, WitnessSet}, + error::NssaError, public_transaction::{Message, WitnessSet}, state::MAX_NUMBER_CHAINED_CALLS, V02State }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -18,7 +16,6 @@ pub struct PublicTransaction { message: Message, witness_set: WitnessSet, } -const MAX_NUMBER_CHAINED_CALLS: usize = 10; impl PublicTransaction { pub fn new(message: Message, witness_set: WitnessSet) -> Self { diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 4120824..be3d0ab 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -10,6 +10,8 @@ use nssa_core::{ }; use std::collections::{HashMap, HashSet}; +pub const MAX_NUMBER_CHAINED_CALLS: usize = 10; + pub(crate) struct CommitmentSet { merkle_tree: MerkleTree, commitments: HashMap,