2025-07-17 10:10:39 -03:00
|
|
|
use core::{
|
2025-07-20 22:02:16 -03:00
|
|
|
compute_nullifier, hash, is_in_tree, post_execution_consistency_checks,
|
2025-07-19 18:37:21 -03:00
|
|
|
types::{Nonce, PrivacyExecutionOutput, ProgramId, ProgramOutput},
|
2025-07-19 17:02:26 -03:00
|
|
|
visibility::AccountVisibility,
|
2025-07-14 19:25:41 -03:00
|
|
|
};
|
2025-07-17 12:46:57 -03:00
|
|
|
use risc0_zkvm::{guest::env, serde::to_vec};
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-18 17:30:38 -03:00
|
|
|
/// Privacy execution logic.
|
|
|
|
|
/// This is the circuit for proving correct off-chain executions of programs.
|
|
|
|
|
/// It also verifies that the chain's invariants are not violated.
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-18 17:30:38 -03:00
|
|
|
/// Inputs:
|
|
|
|
|
/// - Vec<Account>: The output of the inner program. This is assumed to include the accounts pre and
|
|
|
|
|
/// post-states of the execution of the inner program.
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-19 17:02:26 -03:00
|
|
|
/// - Vec<AccountVisibility>: A vector indicating which accounts are private and which are public.
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-18 17:30:38 -03:00
|
|
|
/// - Vec<Nonce>: The vector of nonces to be used for the output accounts. This is assumed to be
|
|
|
|
|
/// sampled at random by the host program.
|
|
|
|
|
///
|
|
|
|
|
/// - [u32; 8]: The root of the commitment tree. Commitments of used private accounts will be
|
|
|
|
|
/// checked against this to prove that they belong to the tree.
|
|
|
|
|
/// - ProgamId: The ID of the inner program.
|
|
|
|
|
///
|
|
|
|
|
/// Public outputs:
|
|
|
|
|
/// - The vector of accounts' pre and post states for the public accounts.
|
|
|
|
|
/// - The nullifiers of the used private accounts.
|
|
|
|
|
/// - The commitments for the ouput private accounts.
|
|
|
|
|
/// - The commitment tree root used for the authentication path verifications.
|
2025-07-11 18:47:03 -03:00
|
|
|
fn main() {
|
2025-07-19 22:05:03 -03:00
|
|
|
// Read inner program output
|
|
|
|
|
let inner_program_output: ProgramOutput = env::read();
|
2025-07-19 18:37:21 -03:00
|
|
|
let num_inputs = inner_program_output.accounts_pre.len();
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-14 19:25:41 -03:00
|
|
|
// Read visibilities
|
2025-07-20 19:58:02 -03:00
|
|
|
let visibilities: Vec<AccountVisibility> = env::read();
|
|
|
|
|
assert_eq!(visibilities.len(), num_inputs);
|
2025-07-15 08:07:27 -03:00
|
|
|
|
|
|
|
|
// Read nonces for outputs
|
2025-07-15 09:26:34 -03:00
|
|
|
let output_nonces: Vec<Nonce> = env::read();
|
2025-07-19 18:37:21 -03:00
|
|
|
assert_eq!(output_nonces.len(), num_inputs);
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-18 17:30:38 -03:00
|
|
|
// Read root and program id.
|
2025-07-14 19:25:41 -03:00
|
|
|
let commitment_tree_root: [u32; 8] = env::read();
|
2025-07-17 12:46:57 -03:00
|
|
|
let program_id: ProgramId = env::read();
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-20 19:44:16 -03:00
|
|
|
// Authentication step:
|
2025-07-20 19:58:02 -03:00
|
|
|
let nullifiers = verify_and_nullify_private_inputs(&inner_program_output, &visibilities, commitment_tree_root);
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-20 19:44:16 -03:00
|
|
|
// Verify pre states and post states of accounts are consistent
|
|
|
|
|
// with the execution of the `program_id` program
|
|
|
|
|
env::verify(program_id, &to_vec(&inner_program_output).unwrap()).unwrap();
|
2025-07-15 08:07:27 -03:00
|
|
|
|
2025-07-20 19:44:16 -03:00
|
|
|
// Assert accounts pre- and post-states preserve chains invariants
|
2025-07-20 22:02:16 -03:00
|
|
|
assert!(post_execution_consistency_checks(
|
2025-07-20 19:44:16 -03:00
|
|
|
&inner_program_output.accounts_pre,
|
2025-07-20 21:55:21 -03:00
|
|
|
&inner_program_output.accounts_post,
|
|
|
|
|
program_id
|
2025-07-20 19:44:16 -03:00
|
|
|
));
|
2025-07-18 17:30:38 -03:00
|
|
|
|
2025-07-18 17:35:03 -03:00
|
|
|
// From this point on the execution is considered valid
|
2025-07-20 19:58:02 -03:00
|
|
|
|
|
|
|
|
let output = assemble_privacy_execution_output(
|
|
|
|
|
inner_program_output,
|
|
|
|
|
visibilities,
|
|
|
|
|
output_nonces,
|
|
|
|
|
commitment_tree_root,
|
|
|
|
|
nullifiers,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
env::commit(&output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn assemble_privacy_execution_output(
|
|
|
|
|
inner_program_output: ProgramOutput,
|
|
|
|
|
visibilities: Vec<AccountVisibility>,
|
|
|
|
|
output_nonces: Vec<[u32; 8]>,
|
|
|
|
|
commitment_tree_root: [u32; 8],
|
|
|
|
|
nullifiers: Vec<[u32; 8]>,
|
|
|
|
|
) -> PrivacyExecutionOutput {
|
2025-07-18 17:35:03 -03:00
|
|
|
// Insert new nonces in outputs (including public ones)
|
2025-07-20 19:44:16 -03:00
|
|
|
let accounts_pre = inner_program_output.accounts_pre;
|
|
|
|
|
let mut accounts_post = inner_program_output.accounts_post;
|
|
|
|
|
accounts_post
|
2025-07-15 08:07:27 -03:00
|
|
|
.iter_mut()
|
|
|
|
|
.zip(output_nonces)
|
|
|
|
|
.for_each(|(account, new_nonce)| account.nonce = new_nonce);
|
|
|
|
|
|
2025-07-18 17:35:03 -03:00
|
|
|
// Compute commitments for every private output
|
2025-07-15 08:07:27 -03:00
|
|
|
let mut private_outputs = Vec::new();
|
2025-07-20 19:58:02 -03:00
|
|
|
for (output, visibility) in accounts_post.iter().zip(visibilities.iter()) {
|
2025-07-15 08:07:27 -03:00
|
|
|
match visibility {
|
2025-07-19 17:02:26 -03:00
|
|
|
AccountVisibility::Public => continue,
|
|
|
|
|
AccountVisibility::Private(_) => private_outputs.push(output),
|
2025-07-15 08:07:27 -03:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-18 17:35:03 -03:00
|
|
|
let private_output_commitments: Vec<_> = private_outputs.iter().map(|account| account.commitment()).collect();
|
2025-07-15 08:07:27 -03:00
|
|
|
|
2025-07-18 17:35:03 -03:00
|
|
|
// Get the list of public accounts pre and post states
|
2025-07-19 18:08:57 -03:00
|
|
|
let mut public_accounts_pre = Vec::new();
|
|
|
|
|
let mut public_accounts_post = Vec::new();
|
2025-07-20 19:44:16 -03:00
|
|
|
for ((account_pre, account_post), visibility) in accounts_pre
|
|
|
|
|
.into_iter()
|
|
|
|
|
.zip(accounts_post.into_iter())
|
2025-07-20 19:58:02 -03:00
|
|
|
.zip(visibilities)
|
2025-07-14 19:25:41 -03:00
|
|
|
{
|
2025-07-15 08:07:27 -03:00
|
|
|
match visibility {
|
2025-07-19 17:02:26 -03:00
|
|
|
AccountVisibility::Public => {
|
2025-07-19 18:08:57 -03:00
|
|
|
public_accounts_pre.push(account_pre);
|
|
|
|
|
public_accounts_post.push(account_post);
|
2025-07-15 08:07:27 -03:00
|
|
|
}
|
2025-07-19 17:02:26 -03:00
|
|
|
AccountVisibility::Private(_) => continue,
|
2025-07-15 08:07:27 -03:00
|
|
|
}
|
2025-07-14 19:25:41 -03:00
|
|
|
}
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-19 18:08:57 -03:00
|
|
|
let output = PrivacyExecutionOutput {
|
|
|
|
|
public_accounts_pre,
|
|
|
|
|
public_accounts_post,
|
2025-07-15 08:07:27 -03:00
|
|
|
private_output_commitments,
|
2025-07-19 18:08:57 -03:00
|
|
|
nullifiers,
|
2025-07-17 09:20:03 -03:00
|
|
|
commitment_tree_root,
|
2025-07-19 18:08:57 -03:00
|
|
|
};
|
2025-07-20 19:58:02 -03:00
|
|
|
output
|
|
|
|
|
}
|
2025-07-19 18:08:57 -03:00
|
|
|
|
2025-07-20 19:58:02 -03:00
|
|
|
/// Compute nullifiers of private accounts pre states and check that their commitments belong to the commitments tree
|
|
|
|
|
fn verify_and_nullify_private_inputs(
|
|
|
|
|
inner_program_output: &ProgramOutput,
|
|
|
|
|
account_visibilities: &Vec<AccountVisibility>,
|
|
|
|
|
commitment_tree_root: [u32; 8],
|
|
|
|
|
) -> Vec<[u32; 8]> {
|
|
|
|
|
let mut nullifiers = Vec::new();
|
|
|
|
|
for (visibility, input_account) in account_visibilities
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(inner_program_output.accounts_pre.iter())
|
|
|
|
|
{
|
|
|
|
|
match visibility {
|
|
|
|
|
AccountVisibility::Private(Some((private_key, auth_path))) => {
|
|
|
|
|
// Prove ownership of input accounts by proving knowledge of the pre-image of their addresses.
|
|
|
|
|
assert_eq!(hash(private_key), input_account.address);
|
|
|
|
|
// Check the input account was created by a previous transaction by checking it belongs to the commitments tree.
|
|
|
|
|
let commitment = input_account.commitment();
|
|
|
|
|
assert!(is_in_tree(commitment, auth_path, commitment_tree_root));
|
|
|
|
|
// Compute the nullifier to nullify this private input account.
|
|
|
|
|
let nullifier = compute_nullifier(&commitment, private_key);
|
|
|
|
|
nullifiers.push(nullifier);
|
|
|
|
|
}
|
|
|
|
|
AccountVisibility::Private(None) => {
|
|
|
|
|
// Private accounts without a companion private key are enforced to have default values
|
|
|
|
|
// Used for executions that need to create a new private account.
|
|
|
|
|
assert_eq!(input_account.balance, 0);
|
|
|
|
|
assert_eq!(input_account.nonce, [0; 8]);
|
|
|
|
|
}
|
|
|
|
|
// No checks on public accounts
|
|
|
|
|
AccountVisibility::Public => continue,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nullifiers
|
2025-07-11 18:47:03 -03:00
|
|
|
}
|