This commit is contained in:
Sergio Chouhy 2025-07-19 18:08:57 -03:00
parent 8482a1b182
commit 763495a17f
6 changed files with 53 additions and 62 deletions

View File

@ -2,7 +2,10 @@ pub mod account;
pub mod types;
pub mod visibility;
use crate::types::{AuthenticationPath, Commitment, Key, Nullifier};
use crate::{
account::Account,
types::{AuthenticationPath, Commitment, Key, Nullifier},
};
use risc0_zkvm::sha::{Impl, Sha256};
pub fn hash(bytes: &[u32]) -> [u32; 8] {

View File

@ -1,3 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::account::Account;
/// For this POC we consider 32-bit commitments
pub type Commitment = u32;
pub type Nullifier = [u32; 8];
@ -6,3 +10,12 @@ pub type Nonce = [u32; 8];
pub type Key = [u32; 8];
pub type AuthenticationPath = [[u32; 8]; 32];
pub type ProgramId = [u32; 8];
#[derive(Serialize, Deserialize)]
pub struct PrivacyExecutionOutput {
pub public_accounts_pre: Vec<Account>,
pub public_accounts_post: Vec<Account>,
pub private_output_commitments: Vec<Commitment>,
pub nullifiers: Vec<Nullifier>,
pub commitment_tree_root: [u32; 8],
}

View File

@ -1,6 +1,6 @@
use core::{
account::Account,
types::{Commitment, Nullifier},
types::{Commitment, Nullifier, PrivacyExecutionOutput},
};
use risc0_zkvm::Receipt;
@ -11,27 +11,22 @@ impl MockedSequencer {
/// Processes a privacy execution request.
/// Verifies the proof of the privacy execution and updates the state of the chain.
pub fn process_privacy_execution(&mut self, receipt: Receipt) -> Result<(), ()> {
// Parse the output of the proof.
// This is the output of the "outer" program
let output: (Vec<Account>, Vec<Nullifier>, Vec<Commitment>, [u32; 8]) = receipt.journal.decode().unwrap();
let (public_inputs_outputs, nullifiers, commitments, commitment_tree_root) = output;
// Parse the output of the "outer" program
let output: PrivacyExecutionOutput = receipt.journal.decode().unwrap();
// Reject in case the root used in the privacy execution is not the current root.
if commitment_tree_root != self.get_commitment_tree_root() {
if output.commitment_tree_root != self.get_commitment_tree_root() {
return Err(());
}
// Reject in case the number of accounts in the public_inputs_outputs is not even.
// This is because it is expected to contain the pre and post-states of public the accounts
// of the inner execution.
if public_inputs_outputs.len() % 2 != 0 {
// Reject in case the number of accounts pre states is different from the post states
if output.public_accounts_pre.len() != output.public_accounts_post.len() {
return Err(());
}
// Reject if the states of the public input accounts used in the inner execution do not
// coincide with the on-chain state.
let num_input_public = public_inputs_outputs.len() >> 1;
for account in public_inputs_outputs.iter().take(num_input_public) {
for account in output.public_accounts_pre.iter() {
let current_account = self.get_account(&account.address).ok_or(())?;
if &current_account != account {
return Err(());
@ -39,7 +34,8 @@ impl MockedSequencer {
}
// Reject if the nullifiers of this privacy execution have already been published.
if nullifiers
if output
.nullifiers
.iter()
.any(|nullifier| self.nullifier_set.contains(nullifier))
{
@ -47,7 +43,8 @@ impl MockedSequencer {
}
// Reject if the commitments have already been seen.
if commitments
if output
.private_output_commitments
.iter()
.any(|commitment| self.commitment_tree.values().contains(commitment))
{
@ -61,30 +58,21 @@ impl MockedSequencer {
// - The given nullifiers correctly correspond to commitments that currently belong to
// the commitment tree.
// - The given commitments are correctly computed from valid accounts.
nssa::verify_privacy_execution(
receipt,
&public_inputs_outputs,
&nullifiers,
&commitments,
&commitment_tree_root,
)?;
nssa::verify_privacy_execution(receipt)?;
// At this point the privacy execution is considered valid.
//
// Update the state of the public accounts with the post-state of this privacy execution
public_inputs_outputs
.iter()
.cloned()
.skip(num_input_public)
.for_each(|account_post_state| {
self.accounts.insert(account_post_state.address, account_post_state);
});
output.public_accounts_post.into_iter().for_each(|account_post_state| {
self.accounts.insert(account_post_state.address, account_post_state);
});
// Add all nullifiers to the nullifier set.
self.nullifier_set.extend(nullifiers);
self.nullifier_set.extend(output.nullifiers);
// Add commitments to the commitment tree.
for commitment in commitments.iter() {
for commitment in output.private_output_commitments.iter() {
self.commitment_tree.add_value(*commitment);
}

View File

@ -61,8 +61,7 @@ fn main() {
.unwrap();
// Verify the proof
let output: (Vec<Account>, Vec<Nullifier>, Vec<Commitment>, [u32; 8]) = receipt.journal.decode().unwrap();
assert!(nssa::verify_privacy_execution(receipt, &output.0, &output.1, &output.2, &output.3).is_ok());
assert!(nssa::verify_privacy_execution(receipt).is_ok());
println!("OK!");
}

View File

@ -1,7 +1,7 @@
use core::{
account::Account,
compute_nullifier, hash, is_in_tree,
types::{Nonce, ProgramId},
types::{Nonce, PrivacyExecutionOutput, ProgramId},
visibility::AccountVisibility,
};
use risc0_zkvm::{guest::env, serde::to_vec};
@ -110,24 +110,29 @@ fn main() {
let private_output_commitments: Vec<_> = private_outputs.iter().map(|account| account.commitment()).collect();
// Get the list of public accounts pre and post states
let mut public_inputs_outputs = Vec::new();
for (account, visibility) in inputs
.iter()
.chain(outputs.iter())
let mut public_accounts_pre = Vec::new();
let mut public_accounts_post = Vec::new();
for ((account_pre, account_post), visibility) in inputs
.into_iter()
.zip(outputs.into_iter())
.zip(account_visibilities.iter().chain(account_visibilities.iter()))
{
match visibility {
AccountVisibility::Public => {
public_inputs_outputs.push(account);
public_accounts_pre.push(account_pre);
public_accounts_post.push(account_post);
}
AccountVisibility::Private(_) => continue,
}
}
env::commit(&(
public_inputs_outputs,
nullifiers,
let output = PrivacyExecutionOutput {
public_accounts_pre,
public_accounts_post,
private_output_commitments,
nullifiers,
commitment_tree_root,
));
};
env::commit(&output);
}

View File

@ -129,23 +129,6 @@ pub fn execute_offchain<P: Program>(
}
/// Verifies a proof of the outer program for the given parameters.
pub fn verify_privacy_execution(
receipt: Receipt,
public_accounts_inputs_outputs: &[Account],
nullifiers: &[Nullifier],
private_output_commitments: &[Commitment],
commitment_tree_root: &[u32; 8],
) -> Result<(), ()> {
let output: (Vec<Account>, Vec<Nullifier>, Vec<Commitment>, [u32; 8]) = receipt.journal.decode().unwrap();
let expected_output = (
public_accounts_inputs_outputs.to_vec(),
nullifiers.to_vec(),
private_output_commitments.to_vec(),
commitment_tree_root.to_owned(),
);
if output != expected_output {
Err(())
} else {
receipt.verify(OUTER_ID).map_err(|_| ())
}
pub fn verify_privacy_execution(receipt: Receipt) -> Result<(), ()> {
receipt.verify(OUTER_ID).map_err(|_| ())
}