2025-07-17 10:10:39 -03:00
use core ::{
2025-07-20 22:30:31 -03:00
check_well_behaved_account_transition , compute_nullifier , hash , is_in_tree ,
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.
2025-07-20 22:30:31 -03:00
/// It also verifies that the chain's invariants are not violated and the program is well-behaved.
2025-07-14 19:25:41 -03:00
///
2025-07-18 17:30:38 -03:00
/// Inputs:
2025-07-21 15:14:38 -03:00
/// - ProgramOuptut: The output of the inner program. This is includes the accounts pre and post-states of the execution of the inner program.
2025-07-19 17:02:26 -03:00
/// - Vec<AccountVisibility>: A vector indicating which accounts are private and which are public.
2025-07-21 15:14: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 input private accounts will be checked against this to prove that they belong to the tree.
2025-07-18 17:30:38 -03:00
/// - 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:30:31 -03:00
assert! ( check_well_behaved_account_transition (
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-21 15:14:38 -03:00
// Insert new nonces in private outputs
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 )
2025-07-21 15:14:38 -03:00
. zip ( visibilities . iter ( ) )
. for_each ( | ( ( account , new_nonce ) , visibility ) | {
if matches! ( visibility , AccountVisibility ::Private ( _ ) ) {
account . nonce = new_nonce ;
}
} ) ;
2025-07-15 08:07:27 -03:00
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-21 15:14:38 -03:00
PrivacyExecutionOutput {
2025-07-19 18:08:57 -03:00
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-21 15:14:38 -03:00
}
2025-07-20 19:58:02 -03:00
}
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 ,
2025-07-21 15:14:38 -03:00
account_visibilities : & [ AccountVisibility ] ,
2025-07-20 19:58:02 -03:00
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
}