2025-07-15 08:07:27 -03:00
|
|
|
use risc0_zkvm::{guest::env, serde::to_vec};
|
2025-07-17 10:10:39 -03:00
|
|
|
use core::{
|
2025-07-16 15:37:49 -03:00
|
|
|
account::Account, compute_nullifier, hash, input::InputVisibiility, is_in_tree, types::Nonce,
|
2025-07-14 19:25:41 -03:00
|
|
|
};
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-11 19:21:06 -03:00
|
|
|
/// Private execution logic.
|
|
|
|
|
/// Circuit for proving correct execution of some program with program id
|
|
|
|
|
/// equal to `program_id` (last input).
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-11 19:44:43 -03:00
|
|
|
/// Currently only supports private execution of a program with two input accounts, one
|
|
|
|
|
/// of which must be a fresh new account (`account_2`) (for example a private transfer function).
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-11 19:21:06 -03:00
|
|
|
/// This circuit checks:
|
|
|
|
|
/// - That accounts pre states and post states are consistent with the execution of the given `program_id`.
|
2025-07-11 19:44:43 -03:00
|
|
|
/// - That `account_2` is fresh (meaning, for this toy example, that it has 0 balance).
|
2025-07-11 19:21:06 -03:00
|
|
|
/// - That `program_id` execution didn't change addresses of the accounts.
|
2025-07-14 19:25:41 -03:00
|
|
|
///
|
2025-07-11 19:21:06 -03:00
|
|
|
/// Outputs:
|
|
|
|
|
/// - The nullifier for the only existing input account (account_1)
|
|
|
|
|
/// - The commitments for the private accounts post states.
|
2025-07-11 18:47:03 -03:00
|
|
|
fn main() {
|
2025-07-14 19:25:41 -03:00
|
|
|
let num_inputs: u32 = env::read();
|
|
|
|
|
// Read inputs and outputs
|
2025-07-15 08:07:27 -03:00
|
|
|
let mut inputs_outputs: Vec<Account> = env::read();
|
|
|
|
|
assert_eq!(inputs_outputs.len() as u32, num_inputs * 2);
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-14 19:25:41 -03:00
|
|
|
// Read visibilities
|
2025-07-15 08:07:27 -03:00
|
|
|
let input_visibilities: Vec<InputVisibiility> = env::read();
|
|
|
|
|
assert_eq!(input_visibilities.len() as u32, num_inputs);
|
|
|
|
|
|
|
|
|
|
// Read nonces for outputs
|
2025-07-15 09:26:34 -03:00
|
|
|
let output_nonces: Vec<Nonce> = env::read();
|
2025-07-15 08:07:27 -03:00
|
|
|
assert_eq!(output_nonces.len() as u32, num_inputs);
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-14 19:25:41 -03:00
|
|
|
let commitment_tree_root: [u32; 8] = env::read();
|
|
|
|
|
let program_id: [u32; 8] = env::read();
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-15 08:07:27 -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(&inputs_outputs).unwrap()).unwrap();
|
|
|
|
|
|
|
|
|
|
// Split inputs_outputs into two separate vectors
|
|
|
|
|
let (inputs, mut outputs) = {
|
|
|
|
|
let outputs = inputs_outputs.split_off(num_inputs as usize);
|
|
|
|
|
(inputs_outputs, outputs)
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-14 19:25:41 -03:00
|
|
|
let mut nullifiers = Vec::new();
|
2025-07-15 08:07:27 -03:00
|
|
|
for (visibility, input_account) in input_visibilities.iter().zip(inputs.iter()) {
|
2025-07-14 19:25:41 -03:00
|
|
|
match visibility {
|
2025-07-15 13:08:13 -03:00
|
|
|
InputVisibiility::Private(Some((private_key, auth_path))) => {
|
2025-07-16 16:45:07 -03:00
|
|
|
// Prove ownership of input accounts by proving knowledge of the pre-image of their addresses.
|
2025-07-14 19:25:41 -03:00
|
|
|
assert_eq!(hash(private_key), input_account.address);
|
2025-07-16 16:45:07 -03:00
|
|
|
// Check the input account was created by a previous transaction by checking it belongs to the commitments tree.
|
2025-07-14 19:25:41 -03:00
|
|
|
let commitment = input_account.commitment();
|
2025-07-15 13:08:13 -03:00
|
|
|
assert!(is_in_tree(commitment, auth_path, commitment_tree_root));
|
2025-07-14 19:25:41 -03:00
|
|
|
// Compute nullifier to nullify this private input account.
|
2025-07-15 08:07:27 -03:00
|
|
|
let nullifier = compute_nullifier(&commitment, private_key);
|
2025-07-14 19:25:41 -03:00
|
|
|
nullifiers.push(nullifier);
|
|
|
|
|
}
|
|
|
|
|
InputVisibiility::Private(None) => {
|
2025-07-16 16:45:07 -03:00
|
|
|
// Private accounts without a companion private key are enforced to have default values
|
2025-07-14 19:25:41 -03:00
|
|
|
assert_eq!(input_account.balance, 0);
|
2025-07-16 16:45:07 -03:00
|
|
|
assert_eq!(input_account.nonce, [0; 8]);
|
2025-07-14 19:25:41 -03:00
|
|
|
}
|
|
|
|
|
// No checks on public accounts
|
|
|
|
|
InputVisibiility::Public => continue,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-16 16:45:07 -03:00
|
|
|
// Assert `program_id` program didn't modify address fields or nonces
|
2025-07-15 08:07:27 -03:00
|
|
|
for (account_pre, account_post) in inputs.iter().zip(outputs.iter()) {
|
|
|
|
|
assert_eq!(account_pre.address, account_post.address);
|
2025-07-16 16:45:07 -03:00
|
|
|
assert_eq!(account_pre.nonce, account_post.nonce);
|
2025-07-15 08:07:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert new nonces in outputs (including public ones (?!))
|
|
|
|
|
outputs
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.zip(output_nonces)
|
|
|
|
|
.for_each(|(account, new_nonce)| account.nonce = new_nonce);
|
|
|
|
|
|
|
|
|
|
// Compute private outputs commitments
|
|
|
|
|
let mut private_outputs = Vec::new();
|
|
|
|
|
for (output, visibility) in outputs.iter().zip(input_visibilities.iter()) {
|
|
|
|
|
match visibility {
|
|
|
|
|
InputVisibiility::Public => continue,
|
|
|
|
|
InputVisibiility::Private(_) => private_outputs.push(output),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the list of public inputs pre states and their post states
|
|
|
|
|
let mut public_inputs_outputs = Vec::new();
|
|
|
|
|
for (account, visibility) in inputs
|
2025-07-14 19:25:41 -03:00
|
|
|
.iter()
|
2025-07-15 08:07:27 -03:00
|
|
|
.chain(outputs.iter())
|
|
|
|
|
.zip(input_visibilities.iter().chain(input_visibilities.iter()))
|
2025-07-14 19:25:41 -03:00
|
|
|
{
|
2025-07-15 08:07:27 -03:00
|
|
|
match visibility {
|
|
|
|
|
InputVisibiility::Public => {
|
|
|
|
|
public_inputs_outputs.push(account);
|
|
|
|
|
}
|
|
|
|
|
InputVisibiility::Private(_) => continue,
|
|
|
|
|
}
|
2025-07-14 19:25:41 -03:00
|
|
|
}
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-15 08:07:27 -03:00
|
|
|
// Compute commitments for every private output
|
|
|
|
|
let private_output_commitments: Vec<_> = private_outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|account| account.commitment())
|
|
|
|
|
.collect();
|
2025-07-11 18:47:03 -03:00
|
|
|
|
2025-07-14 19:25:41 -03:00
|
|
|
// Output nullifier of consumed input accounts and commitments of new output private accounts
|
2025-07-15 08:07:27 -03:00
|
|
|
env::commit(&(
|
|
|
|
|
public_inputs_outputs,
|
|
|
|
|
nullifiers,
|
|
|
|
|
private_output_commitments,
|
2025-07-17 09:20:03 -03:00
|
|
|
commitment_tree_root,
|
2025-07-15 08:07:27 -03:00
|
|
|
));
|
2025-07-11 18:47:03 -03:00
|
|
|
}
|