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];