// Allows including the content of the README.md file at the path "../README.md" as documentation for this code. #![doc = include_str!("../README.md")] // Disables warnings for unused variables throughout the code. #![allow(unused_variables)] // External crates used by this contract for generating code, common structures, and functionalities. #[macro_use] extern crate pbc_contract_codegen; extern crate pbc_contract_common; extern crate pbc_lib; // Importing necessary modules and types from the `pbc_contract_common` crate. use pbc_contract_common::address::Address; use pbc_contract_common::context::ContractContext; use pbc_contract_common::events::EventGroup; use pbc_contract_common::shortname::ShortnameZkComputation; use pbc_contract_common::zk::ZkClosed; use pbc_contract_common::zk::{CalculationStatus, SecretVarId, ZkInputDef, ZkState, ZkStateChange}; use read_write_rpc_derive::ReadWriteRPC; use read_write_state_derive::ReadWriteState; // Defines two types of secret variables: Salary and SumResult, both are not used directly but are placeholders for ZK computations. /// Secret variable metadata. Unused for this contract, so we use a zero-sized struct to save space. #[derive(ReadWriteState, ReadWriteRPC, Debug)] #[repr(u8)] enum SecretVarType { #[discriminant(0)] Salary {}, #[discriminant(1)] SumResult {}, } // Specifies the bit length for secret salary variables, ensuring they fit within the computation's constraints. /// The maximum size of MPC variables. const BITLENGTH_OF_SECRET_SALARY_VARIABLES: u32 = 32; /// Number of employees to wait for before starting computation. A value of 2 or below is useless. const MIN_NUM_EMPLOYEES: u32 = 3; // Identifier for the ZK computation to sum salaries. const ZK_COMPUTE_SUM: ShortnameZkComputation = ShortnameZkComputation::from_u32(0x61); /// This contract's state #[state] struct ContractState { /// Address allowed to start computation administrator: Address, /// Will contain the result (average) when computation is complete average_salary_result: Option, /// Will contain the number of employees after starting the computation num_employees: Option, } /// Initializes contract /// /// Note that administrator is set to whoever initializes the contact. #[init(zk = true)] fn initialize(ctx: ContractContext, zk_state: ZkState) -> ContractState { ContractState { administrator: ctx.sender, average_salary_result: None, num_employees: None, } } /// Adds another salary variable /// /// The ZkInputDef encodes that the variable should have size [`BITLENGTH_OF_SECRET_SALARY_VARIABLES`]. #[zk_on_secret_input(shortname = 0x40)] fn add_salary( context: ContractContext, state: ContractState, zk_state: ZkState, ) -> (ContractState, Vec, ZkInputDef) { /* assert!( zk_state .secret_variables .iter() .chain(zk_state.pending_inputs.iter()) .all(|v| v.owner != context.sender), "Each address is only allowed to send one salary variable. Sender: {:?}", context.sender ); */ let input_def = ZkInputDef { seal: false, metadata: SecretVarType::Salary {}, expected_bit_lengths: vec![BITLENGTH_OF_SECRET_SALARY_VARIABLES], }; (state, vec![], input_def) } /// Automatically called when a variable is confirmed on chain. /// /// Unused for this contract, so we do nothing. #[zk_on_variable_inputted] fn inputted_variable( context: ContractContext, state: ContractState, zk_state: ZkState, inputted_variable: SecretVarId, ) -> ContractState { state } /// Allows the administrator to start the computation of the average salary. /// /// The averaging computation is automatic beyond this call, involving several steps, as described in the module documentation. #[action(shortname = 0x01, zk = true)] fn compute_average_salary( context: ContractContext, mut state: ContractState, zk_state: ZkState, ) -> (ContractState, Vec, Vec) { assert_eq!( zk_state.calculation_state, CalculationStatus::Waiting, "Computation must start from Waiting state, but was {:?}", zk_state.calculation_state, ); let num_employees = zk_state.secret_variables.len() as u32; assert!(num_employees >= MIN_NUM_EMPLOYEES , "At least {MIN_NUM_EMPLOYEES} employees must have submitted and confirmed their inputs, before starting computation, but had only {num_employees}"); state.num_employees = Some(num_employees); ( state, vec![], vec![ZkStateChange::start_computation( ZK_COMPUTE_SUM, vec![SecretVarType::SumResult {}], )], ) } /// Automatically called when the computation is completed /// /// The only thing we do is to instantly open/declassify the output variables. #[zk_on_compute_complete] fn sum_compute_complete( context: ContractContext, state: ContractState, zk_state: ZkState, output_variables: Vec, ) -> (ContractState, Vec, Vec) { ( state, vec![], vec![ZkStateChange::OpenVariables { variables: output_variables, }], ) } /// Automatically called when a variable is opened/declassified. /// /// We can now read the sum variable, and compute the average, which will be our final result. #[zk_on_variables_opened] fn open_sum_variable( context: ContractContext, mut state: ContractState, zk_state: ZkState, opened_variables: Vec, ) -> (ContractState, Vec, Vec) { assert_eq!( opened_variables.len(), 1, "Unexpected number of output variables" ); let opened_variable = zk_state .get_variable(*opened_variables.get(0).unwrap()) .unwrap(); let result = read_variable_u32_le(opened_variable); let mut zk_state_changes = vec![]; if let SecretVarType::SumResult {} = opened_variable.metadata { let num_employees = state.num_employees.unwrap(); state.average_salary_result = Some(result / num_employees); zk_state_changes = vec![ZkStateChange::ContractDone]; } (state, vec![], zk_state_changes) } /// Reads a variable's data as an u32. fn read_variable_u32_le(sum_variable: &ZkClosed) -> u32 { let mut buffer = [0u8; 4]; buffer.copy_from_slice(sum_variable.data.as_ref().unwrap().as_slice()); ::from_le_bytes(buffer) } //solved zk-average-salary