diff --git a/nssa/core/src/account/nullifier.rs b/nssa/core/src/account/nullifier.rs index 06c3e22..bb83854 100644 --- a/nssa/core/src/account/nullifier.rs +++ b/nssa/core/src/account/nullifier.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::account::Commitment; -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct NullifierPublicKey(pub(super) [u8; 32]); impl From<&NullifierSecretKey> for NullifierPublicKey { diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index bd01216..dd2e893 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -100,4 +100,3 @@ pub struct PrivacyPreservingCircuitOutput { pub new_nullifiers: Vec, pub commitment_set_digest: CommitmentSetDigest, } - diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index f5615e9..b942536 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -21,20 +21,6 @@ pub struct ProgramOutput { pub post_states: Vec, } -#[cfg(feature = "host")] -impl ProgramOutput { - pub fn to_bytes(&self) -> Result, NssaCoreError> { - use risc0_zkvm::serde::to_vec; - - let mut result = Vec::new(); - let b = to_vec(self).map_err(|e| NssaCoreError::DeserializationError(e.to_string()))?; - for word in &b { - result.extend_from_slice(&word.to_le_bytes()); - } - Ok(result) - } -} - pub fn read_nssa_inputs() -> ProgramInput { let pre_states: Vec = env::read(); let words: InstructionData = env::read(); @@ -103,60 +89,3 @@ pub fn validate_execution( true } - -#[cfg(test)] -mod tests { - use risc0_zkvm::Journal; - - use crate::{ - account::{Account, AccountWithMetadata}, - program::ProgramOutput, - }; - - #[test] - fn test_program_output_to_bytes_is_compatible_with_journal_decode() { - let account_pre1 = Account { - program_owner: [1, 2, 3, 4, 5, 6, 7, 8], - balance: 1112223333444455556666, - data: b"test data 1".to_vec(), - nonce: 3344556677889900, - }; - let account_pre2 = Account { - program_owner: [9, 8, 7, 6, 5, 4, 3, 2], - balance: 18446744073709551615, - data: b"test data 2".to_vec(), - nonce: 3344556677889901, - }; - let account_post1 = Account { - program_owner: [1, 2, 3, 4, 5, 6, 7, 8], - balance: 1, - data: b"other test data 1".to_vec(), - nonce: 113, - }; - let account_post2 = Account { - program_owner: [9, 8, 7, 6, 5, 4, 3, 2], - balance: 2, - data: b"other test data 2".to_vec(), - nonce: 112, - }; - - let program_output = ProgramOutput { - pre_states: vec![ - AccountWithMetadata { - account: account_pre1, - is_authorized: true, - }, - AccountWithMetadata { - account: account_pre2, - is_authorized: false, - }, - ], - post_states: vec![account_post1, account_post2], - }; - - let bytes = program_output.to_bytes().unwrap(); - let journal = Journal::new(bytes); - let decoded_program_output: ProgramOutput = journal.decode().unwrap(); - assert_eq!(program_output, decoded_program_output); - } -} diff --git a/nssa/src/error.rs b/nssa/src/error.rs index 038d217..e470513 100644 --- a/nssa/src/error.rs +++ b/nssa/src/error.rs @@ -36,4 +36,10 @@ pub enum NssaError { #[error("Core error")] Core(#[from] nssa_core::error::NssaCoreError), + + #[error("Program output deserialization error: {0}")] + ProgramOutputDeserializationError(String), + + #[error("Circuit output deserialization error: {0}")] + CircuitOutputDeserializationError(String), } diff --git a/nssa/src/privacy_preserving_transaction/mod.rs b/nssa/src/privacy_preserving_transaction/mod.rs index 4fed05e..3618e3e 100644 --- a/nssa/src/privacy_preserving_transaction/mod.rs +++ b/nssa/src/privacy_preserving_transaction/mod.rs @@ -5,59 +5,101 @@ mod witness_set; pub use transaction::PrivacyPreservingTransaction; -pub mod offchain { -// use nssa_core::{ -// account::{Account, AccountWithMetadata, NullifierSecretKey}, program::{InstructionData, ProgramOutput}, PrivacyPreservingCircuitInput -// }; -// -// use crate::{error::NssaError, program::Program}; -// - // pub type Proof = (); -// -// pub fn execute_offchain( -// pre_states: &[AccountWithMetadata], -// instruction_data: &InstructionData, -// private_account_keys: &[(NullifierSecretKey, ] -// visibility_mask: &[u8], -// commitment_set_digest: [u32; 8], -// program: Program, -// ) -> Result<(Proof, Vec), NssaError> { -// // Prove inner program and get post state of the accounts -// let inner_proof = program.execute_and_prove(pre_states, instruction_data)?; -// -// let program_output: ProgramOutput = inner_proof.journal.decode()?; -// -// // Sample fresh random nonces for the outputs of this execution -// let output_nonces: Vec<_> = (0..inputs.len()).map(|_| new_random_nonce()).collect(); -// -// let privacy_preserving_circuit_input = PrivacyPreservingCircuitInput { -// program_output, -// visibility_mask, -// private_account_data: todo!(), -// private_account_auth: todo!(), -// program_id: todo!(), -// commitment_set_digest, -// }; -// // -// // // Prove outer program. -// // let mut env_builder = ExecutorEnv::builder(); -// // env_builder.add_assumption(inner_receipt); -// // env_builder.write(&inner_program_output).unwrap(); -// // env_builder.write(&visibilities).unwrap(); -// // env_builder.write(&output_nonces).unwrap(); -// // env_builder.write(&commitment_tree_root).unwrap(); -// // env_builder.write(&P::PROGRAM_ID).unwrap(); -// // let env = env_builder.build().unwrap(); -// // let prover = default_prover(); -// // let prove_info = prover.prove(env, OUTER_ELF).unwrap(); -// // -// // // Build private accounts. -// // let private_outputs = build_private_outputs_from_inner_results( -// // &inner_program_output, -// // visibilities, -// // &output_nonces, -// // ); -// // -// // Ok((prove_info.receipt, private_outputs)) -// } +pub mod circuit { + use nssa_core::{ + CommitmentSetDigest, EphemeralSecretKey, IncomingViewingPublicKey, MembershipProof, + PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, + account::{Account, AccountWithMetadata, Nonce, NullifierPublicKey, NullifierSecretKey}, + program::{InstructionData, ProgramOutput}, + }; + use risc0_zkvm::{ExecutorEnv, Receipt, default_prover}; + + use crate::{error::NssaError, program::Program}; + + use program_methods::PRIVACY_PRESERVING_CIRCUIT_ELF; + + pub type Proof = Vec; + + /// Executes and proves the program `P`. + /// Returns the proof + fn execute_and_prove_program( + program: &Program, + pre_states: &[AccountWithMetadata], + instruction_data: &InstructionData, + ) -> Result { + // Write inputs to the program + let mut env_builder = ExecutorEnv::builder(); + Program::write_inputs(pre_states, instruction_data, &mut env_builder)?; + let env = env_builder.build().unwrap(); + + // Prove the program + let prover = default_prover(); + Ok(prover + .prove(env, program.elf()) + .map_err(|e| NssaError::ProgramProveFailed(e.to_string()))? + .receipt) + } + + pub fn prove_privacy_preserving_execution_circuit( + pre_states: &[AccountWithMetadata], + instruction_data: &InstructionData, + private_account_keys: &[( + NullifierPublicKey, + IncomingViewingPublicKey, + EphemeralSecretKey, + )], + private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>, + visibility_mask: &[u8], + commitment_set_digest: CommitmentSetDigest, + program: &Program, + ) -> Result<(Proof, PrivacyPreservingCircuitOutput), NssaError> { + 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 private_account_nonces: Vec<_> = (0..private_account_keys.len()) + .map(|_| new_random_nonce()) + .collect(); + + let circuit_input = PrivacyPreservingCircuitInput { + program_output, + visibility_mask: visibility_mask.to_vec(), + private_account_nonces: private_account_nonces.to_vec(), + private_account_keys: private_account_keys.to_vec(), + private_account_auth: private_account_auth.to_vec(), + program_id: program.id(), + commitment_set_digest, + }; + + // 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(); + let prove_info = prover.prove(env, PRIVACY_PRESERVING_CIRCUIT_ELF).unwrap(); + + let proof = borsh::to_vec(&prove_info.receipt.inner)?; + + let circuit_output: PrivacyPreservingCircuitOutput = prove_info + .receipt + .journal + .decode() + .map_err(|e| NssaError::CircuitOutputDeserializationError(e.to_string()))?; + + Ok((proof, circuit_output)) + } + + fn new_random_nonce() -> Nonce { + todo!() + } +} + +#[cfg(test)] +mod tests { + use crate::program::Program; + } diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 84fea64..9f5718d 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -4,7 +4,7 @@ use nssa_core::account::{Account, AccountWithMetadata}; use nssa_core::{CommitmentSetDigest, EncryptedAccountData}; use crate::error::NssaError; -use crate::program::Proof; +use crate::privacy_preserving_transaction::circuit::Proof; use crate::{Address, V01State}; use super::message::Message; diff --git a/nssa/src/privacy_preserving_transaction/witness_set.rs b/nssa/src/privacy_preserving_transaction/witness_set.rs index 920adb0..fe897ce 100644 --- a/nssa/src/privacy_preserving_transaction/witness_set.rs +++ b/nssa/src/privacy_preserving_transaction/witness_set.rs @@ -1,6 +1,6 @@ use crate::{ - PrivateKey, PublicKey, Signature, privacy_preserving_transaction::message::Message, - program::Proof, + PrivateKey, PublicKey, Signature, + privacy_preserving_transaction::{circuit::Proof, message::Message}, }; #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 0bd6dcb..410d674 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -12,8 +12,6 @@ use serde::Serialize; use crate::error::NssaError; -pub type Proof = Vec; - #[derive(Debug, PartialEq, Eq)] pub struct Program { id: ProgramId, @@ -25,6 +23,10 @@ impl Program { self.id } + pub(crate) fn elf(&self) -> &'static [u8] { + self.elf + } + pub fn serialize_instruction( instruction: T, ) -> Result { @@ -58,30 +60,8 @@ impl Program { Ok(post_states) } - /// Executes and proves the program `P`. - /// Returns the proof - pub(crate) fn execute_and_prove( - &self, - pre_states: &[AccountWithMetadata], - instruction_data: &InstructionData, - ) -> Result<(Proof, ProgramOutput), NssaError> { - // Write inputs to the program - let mut env_builder = ExecutorEnv::builder(); - Self::write_inputs(pre_states, instruction_data, &mut env_builder)?; - let env = env_builder.build().unwrap(); - - // Prove the program - let prover = default_prover(); - let prove_info = prover - .prove(env, self.elf) - .map_err(|e| NssaError::ProgramProveFailed(e.to_string()))?; - let proof = borsh::to_vec(&prove_info.receipt.inner).unwrap(); - let program_output = prove_info.receipt.journal.decode().unwrap(); - Ok((proof, program_output)) - } - /// Writes inputs to `env_builder` in the order expected by the programs - fn write_inputs( + pub(crate) fn write_inputs( pre_states: &[AccountWithMetadata], instruction_data: &[u32], env_builder: &mut ExecutorEnvBuilder, @@ -227,47 +207,4 @@ mod tests { assert_eq!(sender_post, expected_sender_post); assert_eq!(recipient_post, expected_recipient_post); } - - #[test] - fn test_program_execute_and_prove() { - let program = Program::simple_balance_transfer(); - let balance_to_move: u128 = 11223344556677; - let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); - let sender = AccountWithMetadata { - account: Account { - balance: 77665544332211, - ..Account::default() - }, - is_authorized: false, - }; - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; - - let expected_sender_post = Account { - balance: 77665544332211 - balance_to_move, - ..Account::default() - }; - let expected_recipient_post = Account { - balance: balance_to_move, - ..Account::default() - }; - - let (proof, program_output) = program - .execute_and_prove(&[sender, recipient], &instruction_data) - .unwrap(); - - let ProgramOutput { post_states, .. } = program_output.clone(); - let [sender_post, recipient_post] = post_states.try_into().unwrap(); - - assert_eq!(sender_post, expected_sender_post); - assert_eq!(recipient_post, expected_recipient_post); - - let journal = program_output.to_bytes().unwrap(); - let inner: InnerReceipt = borsh::from_slice(&proof).unwrap(); - let receipt = Receipt::new(inner, journal); - - receipt.verify(program.id()).unwrap(); - } }