From c74fa88380f3936407a3712bc1d822f515c86163 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 11 Jul 2025 18:47:03 -0300 Subject: [PATCH] wip --- .gitignore | 5 +- risc0-selective-privacy-poc/Cargo.toml | 17 +++ risc0-selective-privacy-poc/core/Cargo.toml | 8 ++ risc0-selective-privacy-poc/core/src/lib.rs | 44 +++++++ .../outer_methods/Cargo.toml | 10 ++ .../outer_methods/build.rs | 3 + .../outer_methods/guest/Cargo.toml | 11 ++ .../outer_methods/guest/src/bin/outer.rs | 42 +++++++ .../outer_methods/src/lib.rs | 1 + .../rust-toolchain.toml | 4 + risc0-selective-privacy-poc/src/lib.rs | 119 ++++++++++++++++++ .../transfer_methods/Cargo.toml | 10 ++ .../transfer_methods/build.rs | 3 + .../transfer_methods/guest/Cargo.toml | 10 ++ .../guest/src/bin/transfer.rs | 18 +++ .../transfer_methods/src/lib.rs | 1 + 16 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 risc0-selective-privacy-poc/Cargo.toml create mode 100644 risc0-selective-privacy-poc/core/Cargo.toml create mode 100644 risc0-selective-privacy-poc/core/src/lib.rs create mode 100644 risc0-selective-privacy-poc/outer_methods/Cargo.toml create mode 100644 risc0-selective-privacy-poc/outer_methods/build.rs create mode 100644 risc0-selective-privacy-poc/outer_methods/guest/Cargo.toml create mode 100644 risc0-selective-privacy-poc/outer_methods/guest/src/bin/outer.rs create mode 100644 risc0-selective-privacy-poc/outer_methods/src/lib.rs create mode 100644 risc0-selective-privacy-poc/rust-toolchain.toml create mode 100644 risc0-selective-privacy-poc/src/lib.rs create mode 100644 risc0-selective-privacy-poc/transfer_methods/Cargo.toml create mode 100644 risc0-selective-privacy-poc/transfer_methods/build.rs create mode 100644 risc0-selective-privacy-poc/transfer_methods/guest/Cargo.toml create mode 100644 risc0-selective-privacy-poc/transfer_methods/guest/src/bin/transfer.rs create mode 100644 risc0-selective-privacy-poc/transfer_methods/src/lib.rs diff --git a/.gitignore b/.gitignore index f2f9e58..d97316e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ target -Cargo.lock \ No newline at end of file +Cargo.lock +risc0-selective-privacy-poc/target +risc0-selective-privacy-poc/Cargo.lock +risc0-selective-privacy-poc/methods/guest/Cargo.lock \ No newline at end of file diff --git a/risc0-selective-privacy-poc/Cargo.toml b/risc0-selective-privacy-poc/Cargo.toml new file mode 100644 index 0000000..e817b5d --- /dev/null +++ b/risc0-selective-privacy-poc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tuki" +version = "0.12.0" +edition = "2021" + +[dependencies] +risc0-zkvm = "2.2" +toy-example-core = {path = "core"} +transfer-methods = {path = "transfer_methods"} +outer-methods = {path = "outer_methods"} +serde = "1.0" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[features] +cuda = ["risc0-zkvm/cuda"] +default = [] +prove = ["risc0-zkvm/prove"] \ No newline at end of file diff --git a/risc0-selective-privacy-poc/core/Cargo.toml b/risc0-selective-privacy-poc/core/Cargo.toml new file mode 100644 index 0000000..1d84937 --- /dev/null +++ b/risc0-selective-privacy-poc/core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "toy-example-core" +version = "0.12.0" +edition = "2021" + +[dependencies] +risc0-zkvm = "2.0.2" +serde = { version = "1.0", default-features = false } \ No newline at end of file diff --git a/risc0-selective-privacy-poc/core/src/lib.rs b/risc0-selective-privacy-poc/core/src/lib.rs new file mode 100644 index 0000000..fc3da32 --- /dev/null +++ b/risc0-selective-privacy-poc/core/src/lib.rs @@ -0,0 +1,44 @@ +#![cfg_attr(not(test), no_std)] + +use serde::{Serialize, Deserialize}; +use risc0_zkvm::{sha::{Impl, Sha256}, serde::to_vec}; + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Account { + pub address: [u32; 8], + pub balance: u128, + pub nonce: [u32; 8] +} + +impl Account { + /// Creates a new account with address = hash(private_key) and balance = 0 + pub fn new(private_key: [u32; 8], nonce: [u32; 8]) -> Self { + let address = hash(&private_key); + Self { address, balance: 0, nonce } + } + + /// Returns Hash(Account) + pub fn commitment(&self) -> [u32; 8] { + hash(&to_vec(&self).unwrap()) + } + +} + + +pub fn hash(bytes: &[u32]) -> [u32; 8] { + Impl::hash_words(&bytes).as_words().try_into().unwrap() +} + +pub fn is_in_commitment_tree(_commitment: [u32; 8], _tree_root: [u32; 8]) -> bool { + // Dummy implementation + true +} + +/// Returns Hash(Commitment || private_key) +pub fn compute_nullifier(commitment: [u32; 8], private_key: [u32; 8]) -> [u32; 8] { + let mut bytes_to_hash = [0; 16]; + bytes_to_hash[..8].copy_from_slice(&commitment); + bytes_to_hash[8..].copy_from_slice(&private_key); + hash(&bytes_to_hash) +} + diff --git a/risc0-selective-privacy-poc/outer_methods/Cargo.toml b/risc0-selective-privacy-poc/outer_methods/Cargo.toml new file mode 100644 index 0000000..1f5e9ec --- /dev/null +++ b/risc0-selective-privacy-poc/outer_methods/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "outer-methods" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "2.2" } + +[package.metadata.risc0] +methods = ["guest"] diff --git a/risc0-selective-privacy-poc/outer_methods/build.rs b/risc0-selective-privacy-poc/outer_methods/build.rs new file mode 100644 index 0000000..08a8a4e --- /dev/null +++ b/risc0-selective-privacy-poc/outer_methods/build.rs @@ -0,0 +1,3 @@ +fn main() { + risc0_build::embed_methods(); +} diff --git a/risc0-selective-privacy-poc/outer_methods/guest/Cargo.toml b/risc0-selective-privacy-poc/outer_methods/guest/Cargo.toml new file mode 100644 index 0000000..ceb31ad --- /dev/null +++ b/risc0-selective-privacy-poc/outer_methods/guest/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "toy_example" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] } +toy-example-core = {path = "../../core" } +transfer-methods = {path = "../../transfer_methods"} diff --git a/risc0-selective-privacy-poc/outer_methods/guest/src/bin/outer.rs b/risc0-selective-privacy-poc/outer_methods/guest/src/bin/outer.rs new file mode 100644 index 0000000..d111bdd --- /dev/null +++ b/risc0-selective-privacy-poc/outer_methods/guest/src/bin/outer.rs @@ -0,0 +1,42 @@ +use risc0_zkvm::{guest::env, sha::{Impl, Sha256}, serde::to_vec}; +use toy_example_core::{Account, hash, compute_nullifier, is_in_commitment_tree}; +use transfer_methods::TRANSFER_ID; + +fn main() { + // Read inputs + let sender_private_key: [u32; 8] = env::read(); + let sender: Account = env::read(); + let receiver: Account = env::read(); + let balance_to_move: u128 = env::read(); + let sender_post: Account = env::read(); + let receiver_post: Account = env::read(); + let commitment_tree_root: [u32; 8] = env::read(); + + // Assert receiver account is fresh + assert_eq!(receiver.balance, 0); + + // Prove ownership of sender account by proving + // knowledge of the pre-image of its address + assert_eq!(hash(&sender_private_key), sender.address); + + // Compute sender account commitment and prove it belongs to commitments tree + let sender_commitment = sender.commitment(); + assert!(is_in_commitment_tree(sender_commitment, commitment_tree_root)); + + // Compute nullifier of sender account + let sender_nullifier = compute_nullifier(sender_commitment, sender_private_key); + + // Compute receiver commitment + let receiver_commitment = receiver_post.commitment(); + + // Verify pre states and post states of accounts are consistent + // with the execution of the TRANSFER_ELF program + env::verify(TRANSFER_ID, &to_vec(&(sender.clone(), receiver.clone(), sender_post.clone(), receiver_post.clone())).unwrap()).unwrap(); + + // Assert TRANSFER_ELF program didn't modify address fields + assert_eq!(sender.address, sender_post.address); + assert_eq!(receiver.address, receiver_post.address); + + // Output nullifier + env::commit(&(sender_nullifier, receiver_commitment)); +} diff --git a/risc0-selective-privacy-poc/outer_methods/src/lib.rs b/risc0-selective-privacy-poc/outer_methods/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/risc0-selective-privacy-poc/outer_methods/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs")); diff --git a/risc0-selective-privacy-poc/rust-toolchain.toml b/risc0-selective-privacy-poc/rust-toolchain.toml new file mode 100644 index 0000000..36614c3 --- /dev/null +++ b/risc0-selective-privacy-poc/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "stable" +components = ["rustfmt", "rust-src"] +profile = "minimal" diff --git a/risc0-selective-privacy-poc/src/lib.rs b/risc0-selective-privacy-poc/src/lib.rs new file mode 100644 index 0000000..b2b85bb --- /dev/null +++ b/risc0-selective-privacy-poc/src/lib.rs @@ -0,0 +1,119 @@ +use std::u128; + +// These constants represent the RISC-V ELF and the image ID generated by risc0-build. +// The ELF is used for proving and the ID is used for verification. +use transfer_methods::{ + TRANSFER_ELF, TRANSFER_ID +}; +use outer_methods::{ + OUTER_ELF, OUTER_ID +}; +use risc0_zkvm::{default_executor, default_prover, ExecutorEnv, Receipt}; +use toy_example_core::Account; + +fn prove_inner(sender: Account, receiver: Account, balance_to_move: u128) -> (Receipt, Account, Account) { + let mut env_builder = ExecutorEnv::builder(); + env_builder.write(&sender).unwrap(); + env_builder.write(&receiver).unwrap(); + env_builder.write(&balance_to_move).unwrap(); + let env = env_builder.build().unwrap(); + + let prover = default_prover(); + let prove_info = prover + .prove(env, TRANSFER_ELF) + .unwrap(); + + let receipt = prove_info.receipt; + + let output: [Account; 4] = receipt.journal.decode().unwrap(); + let [_, _, sender_post, receiver_post] = output; + + println!("sender_before: {:?}, sender_after: {:?}", sender, sender_post); + println!("receiver_before: {:?}, receiver_after: {:?}", receiver, receiver_post); + + // Sanity check + receipt + .verify(TRANSFER_ID) + .unwrap(); + + (receipt, sender_post, receiver_post) +} + +pub fn run_private_execution_of_transfer_program() { + let commitment_tree_root = [0xdd, 0xee, 0xaa, 0xdd, 0xbb, 0xee, 0xee, 0xff]; + let sender_private_key = [0; 8]; + let mut sender = Account::new(sender_private_key, [1; 8]); + sender.balance = 150; + + let receiver_private_key = [99; 8]; + let receiver = Account::new(receiver_private_key, [1; 8]); + let balance_to_move: u128 = 3; + + // Prove inner + let (inner_receipt, sender_post, receiver_post) = prove_inner(sender.clone(), receiver.clone(), balance_to_move); + + // Prover outer + let mut env_builder = ExecutorEnv::builder(); + env_builder.add_assumption(inner_receipt); + env_builder.write(&sender_private_key).unwrap(); + env_builder.write(&sender).unwrap(); + env_builder.write(&receiver) .unwrap(); + env_builder.write(&balance_to_move).unwrap(); + env_builder.write(&sender_post).unwrap(); + env_builder.write(&receiver_post).unwrap(); + env_builder.write(&commitment_tree_root).unwrap(); + let env = env_builder.build().unwrap(); + + let prover = default_prover(); + let prove_info = prover + .prove(env, OUTER_ELF) + .unwrap(); + + let receipt = prove_info.receipt; + + receipt.verify(OUTER_ID).unwrap(); + + let (nullifier, commitment): ([u32; 8], [u32; 8]) = receipt.journal.decode().unwrap(); + println!("nullifier: {:?}", nullifier); + println!("commitment: {:?}", commitment); +} + +pub fn run_public_execution_of_transfer_program() { + let sender_private_key = [0; 8]; + let mut sender = Account::new(sender_private_key, [1; 8]); + sender.balance = 150; + + let receiver_private_key = [99; 8]; + let mut receiver = Account::new(receiver_private_key, [1; 8]); + receiver.balance = 900; + + let balance_to_move: u128 = 3; + + let mut env_builder = ExecutorEnv::builder(); + env_builder.write(&sender).unwrap(); + env_builder.write(&receiver).unwrap(); + env_builder.write(&balance_to_move).unwrap(); + let env = env_builder.build().unwrap(); + + let executor = default_executor(); + let result: [Account; 4] = executor.execute(env, TRANSFER_ELF).unwrap().journal.decode().unwrap(); + let [_, _, sender_post, receiver_post] = result; + + println!("sender_before: {:?}, sender_after: {:?}", sender, sender_post); + println!("receiver_before: {:?}, receiver_after: {:?}", receiver, receiver_post); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_private() { + run_private_execution_of_transfer_program(); + } + + #[test] + fn test_public() { + run_public_execution_of_transfer_program(); + } +} diff --git a/risc0-selective-privacy-poc/transfer_methods/Cargo.toml b/risc0-selective-privacy-poc/transfer_methods/Cargo.toml new file mode 100644 index 0000000..523dc0a --- /dev/null +++ b/risc0-selective-privacy-poc/transfer_methods/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "transfer-methods" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +risc0-build = { version = "2.2" } + +[package.metadata.risc0] +methods = ["guest"] diff --git a/risc0-selective-privacy-poc/transfer_methods/build.rs b/risc0-selective-privacy-poc/transfer_methods/build.rs new file mode 100644 index 0000000..08a8a4e --- /dev/null +++ b/risc0-selective-privacy-poc/transfer_methods/build.rs @@ -0,0 +1,3 @@ +fn main() { + risc0_build::embed_methods(); +} diff --git a/risc0-selective-privacy-poc/transfer_methods/guest/Cargo.toml b/risc0-selective-privacy-poc/transfer_methods/guest/Cargo.toml new file mode 100644 index 0000000..81e568f --- /dev/null +++ b/risc0-selective-privacy-poc/transfer_methods/guest/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "toy_example" +version = "0.1.0" +edition = "2021" + +[workspace] + +[dependencies] +risc0-zkvm = { version = "2.2.0", default-features = false, features = ['std'] } +toy-example-core = {path = "../../core" } diff --git a/risc0-selective-privacy-poc/transfer_methods/guest/src/bin/transfer.rs b/risc0-selective-privacy-poc/transfer_methods/guest/src/bin/transfer.rs new file mode 100644 index 0000000..1addb8d --- /dev/null +++ b/risc0-selective-privacy-poc/transfer_methods/guest/src/bin/transfer.rs @@ -0,0 +1,18 @@ +use risc0_zkvm::{guest::env, sha::{Impl, Sha256}, serde::to_vec}; +use toy_example_core::Account; + +fn main() { + let sender: Account = env::read(); + let receiver: Account = env::read(); + let balance_to_move: u128 = env::read(); + + assert!(sender.balance >= balance_to_move); + + let mut sender_post = sender.clone(); + let mut receiver_post = receiver.clone(); + + sender_post.balance -= balance_to_move; + receiver_post.balance += balance_to_move; + + env::commit(&(sender, receiver, sender_post, receiver_post)); +} diff --git a/risc0-selective-privacy-poc/transfer_methods/src/lib.rs b/risc0-selective-privacy-poc/transfer_methods/src/lib.rs new file mode 100644 index 0000000..1bdb308 --- /dev/null +++ b/risc0-selective-privacy-poc/transfer_methods/src/lib.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/methods.rs"));