diff --git a/risc0-selective-privacy-poc/core/src/types.rs b/risc0-selective-privacy-poc/core/src/types.rs index a9608dc..8aa04db 100644 --- a/risc0-selective-privacy-poc/core/src/types.rs +++ b/risc0-selective-privacy-poc/core/src/types.rs @@ -4,3 +4,4 @@ pub type Address = [u32; 8]; pub type Nonce = [u32; 8]; pub type Key = [u32; 8]; pub type AuthenticationPath = [[u32; 8]; 32]; +pub type ProgramId = [u32; 8]; diff --git a/risc0-selective-privacy-poc/examples/private_execution.rs b/risc0-selective-privacy-poc/examples/private_execution.rs index c8ee654..a6b6721 100644 --- a/risc0-selective-privacy-poc/examples/private_execution.rs +++ b/risc0-selective-privacy-poc/examples/private_execution.rs @@ -5,8 +5,8 @@ use core::{ types::{Address, AuthenticationPath, Commitment, Nullifier}, }; use nssa::program::TransferMultipleProgram; -use sparse_merkle_tree::SparseMerkleTree; use program_methods::OUTER_ID; +use sparse_merkle_tree::SparseMerkleTree; fn mint_fresh_account(address: Address) -> Account { let nonce = [0; 8]; diff --git a/risc0-selective-privacy-poc/examples/sequencer.rs b/risc0-selective-privacy-poc/examples/sequencer.rs new file mode 100644 index 0000000..da98a47 --- /dev/null +++ b/risc0-selective-privacy-poc/examples/sequencer.rs @@ -0,0 +1,7 @@ +use crate::sequencer_mock::MockedSequencer; + +mod sequencer_mock; + +fn main() { + let sequencer = MockedSequencer::new(); +} diff --git a/risc0-selective-privacy-poc/examples/sequencer_mock/mod.rs b/risc0-selective-privacy-poc/examples/sequencer_mock/mod.rs new file mode 100644 index 0000000..53b9985 --- /dev/null +++ b/risc0-selective-privacy-poc/examples/sequencer_mock/mod.rs @@ -0,0 +1,102 @@ +use core::{ + account::Account, + types::{Address, Key, Nullifier, ProgramId}, +}; +use std::collections::{HashMap, HashSet}; + +use nssa; +use program_methods::{PINATA_ID, TRANSFER_ID, TRANSFER_MULTIPLE_ID}; +use sparse_merkle_tree::SparseMerkleTree; + +pub struct MockedSequencer { + accounts: HashMap, + commitment_tree: SparseMerkleTree, + nullifier_set: HashSet, + deployed_program_ids: HashSet, +} + +const ACCOUNTS_PRIVATE_KEYS: [Key; 5] = [[1; 8], [2; 8], [3; 8], [4; 8], [5; 8]]; +const ACCOUNTS_INITIAL_BALANCES: [u128; 5] = [100, 3, 900, 44, 0]; +const DEPLOYED_PROGRAM_IDS: [ProgramId; 3] = [TRANSFER_ID, TRANSFER_MULTIPLE_ID, PINATA_ID]; + +impl MockedSequencer { + pub fn new() -> Self { + let mut accounts: HashMap = ACCOUNTS_PRIVATE_KEYS + .iter() + .cloned() + .zip(ACCOUNTS_INITIAL_BALANCES) + .map(|(key, initial_balance)| { + let mut this = Account::new_from_private_key(key, [0; 8]); + this.balance = initial_balance; + this + }) + .map(|account| (account.address, account)) + .collect(); + + let pinata_account = { + let mut this = Account::new([0xcafe; 8], [0; 8]); + this.balance = 100; + this + }; + accounts.insert(pinata_account.address, pinata_account); + + let commitment_tree = SparseMerkleTree::new_empty(); + let nullifier_set = HashSet::new(); + Self { + accounts, + commitment_tree, + nullifier_set, + deployed_program_ids: DEPLOYED_PROGRAM_IDS.iter().collect(), + } + } + + pub fn get_account(&self, address: &Address) -> Option { + self.accounts.get(address).cloned() + } + + pub fn invoke_program_public( + &mut self, + program_id: ProgramId, + input_account_addresses: &[Address], + instruction_data: &P::InstructionData, + ) -> Result<(), ()> { + let input_accounts: Vec = input_account_addresses + .iter() + .map(|address| self.get_account(address).ok_or(|_| ())?) + .collect(); + let inputs_outputs = nssa::execute(input_accounts, instruction_data)?; + + self.inputs_outputs_are_consistent(&input_accounts, &inputs_outputs)? + } + + fn inputs_outputs_are_consistent( + &self, + input_accounts: &[Account], + inputs_outputs: &[Account], + ) -> Result<(), ()> { + let num_inputs = input_accounts.len(); + if inputs_outputs.len() != num_inputs * 2 { + return Err(()); + } + + let (claimed_accounts_pre, accounts_post) = inputs_outputs.split_at(num_inputs); + if claimed_accounts_pre != input_accounts { + return Err(()); + } + + for (account_pre, account_post) in input_accounts.iter().zip(accounts_post) { + if account_pre.address != account_post.address { + return Err(()); + } + if account_pre.nonce != account_post.nonce { + return Err(()); + } + } + let accounts_pre_total_balance = input_accounts.iter().map(|account| account.balance).sum(); + let accounts_post_total_balance = accounts_post.iter().map(|account| account.balance).sum(); + if accounts_pre_total_balance != accounts_post_total_balance { + return Err(()); + } + return Ok(()); + } +} diff --git a/risc0-selective-privacy-poc/program_methods/guest/src/bin/outer.rs b/risc0-selective-privacy-poc/program_methods/guest/src/bin/outer.rs index 0f67546..8fadb3e 100644 --- a/risc0-selective-privacy-poc/program_methods/guest/src/bin/outer.rs +++ b/risc0-selective-privacy-poc/program_methods/guest/src/bin/outer.rs @@ -1,7 +1,11 @@ -use risc0_zkvm::{guest::env, serde::to_vec}; use core::{ - account::Account, compute_nullifier, hash, input::InputVisibiility, is_in_tree, types::Nonce, + account::Account, + compute_nullifier, hash, + input::InputVisibiility, + is_in_tree, + types::{Nonce, ProgramId}, }; +use risc0_zkvm::{guest::env, serde::to_vec}; /// Private execution logic. /// Circuit for proving correct execution of some program with program id @@ -33,7 +37,7 @@ fn main() { assert_eq!(output_nonces.len() as u32, num_inputs); let commitment_tree_root: [u32; 8] = env::read(); - let program_id: [u32; 8] = env::read(); + let program_id: ProgramId = env::read(); // Verify pre states and post states of accounts are consistent // with the execution of the `program_id`` program diff --git a/risc0-selective-privacy-poc/src/program/mod.rs b/risc0-selective-privacy-poc/src/program/mod.rs index af1ede3..ea6f896 100644 --- a/risc0-selective-privacy-poc/src/program/mod.rs +++ b/risc0-selective-privacy-poc/src/program/mod.rs @@ -1,17 +1,19 @@ -use serde::{Deserialize, Serialize}; +use core::types::ProgramId; + use program_methods::{ PINATA_ELF, PINATA_ID, TRANSFER_ELF, TRANSFER_ID, {TRANSFER_MULTIPLE_ELF, TRANSFER_MULTIPLE_ID}, }; +use serde::{Deserialize, Serialize}; pub trait Program { - const PROGRAM_ID: [u32; 8]; + const PROGRAM_ID: ProgramId; const PROGRAM_ELF: &[u8]; type InstructionData: Serialize + for<'de> Deserialize<'de>; } pub struct TransferProgram; impl Program for TransferProgram { - const PROGRAM_ID: [u32; 8] = TRANSFER_ID; + const PROGRAM_ID: ProgramId = TRANSFER_ID; const PROGRAM_ELF: &[u8] = TRANSFER_ELF; /// Amount to transfer type InstructionData = u128; @@ -19,7 +21,7 @@ impl Program for TransferProgram { pub struct TransferMultipleProgram; impl Program for TransferMultipleProgram { - const PROGRAM_ID: [u32; 8] = TRANSFER_MULTIPLE_ID; + const PROGRAM_ID: ProgramId = TRANSFER_MULTIPLE_ID; const PROGRAM_ELF: &[u8] = TRANSFER_MULTIPLE_ELF; /// Amounts to transfer type InstructionData = Vec; @@ -27,7 +29,7 @@ impl Program for TransferMultipleProgram { pub struct PinataProgram; impl Program for PinataProgram { - const PROGRAM_ID: [u32; 8] = TRANSFER_MULTIPLE_ID; + const PROGRAM_ID: ProgramId = TRANSFER_MULTIPLE_ID; const PROGRAM_ELF: &[u8] = TRANSFER_MULTIPLE_ELF; /// Preimage of target hash to win price type InstructionData = [u32; 8];