Skip to content

Commit

Permalink
Add initial end-to-end support for arithmetic circuits
Browse files Browse the repository at this point in the history
  • Loading branch information
cgouert committed Aug 28, 2023
1 parent 3574164 commit 8ce9058
Show file tree
Hide file tree
Showing 3 changed files with 233 additions and 169 deletions.
314 changes: 174 additions & 140 deletions src/bin/helm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use std::{collections::HashMap, time::Instant};
use termion::color;
use tfhe::{boolean::prelude::*, shortint::parameters::PARAM_MESSAGE_4_CARRY_0};
use tfhe::{generate_keys, set_server_key, ConfigBuilder};
use std::str::FromStr;
use std::fmt::Debug;

fn parse_args() -> (String, usize, bool, HashMap<String, bool>) {
fn parse_args() -> (String, usize, bool, bool, Option<String>) {
let matches = Command::new("HELM")
.about("HELM: Homomorphic Evaluation with EDA-driven Logic Minimization")
.arg(
Expand Down Expand Up @@ -39,100 +41,93 @@ fn parse_args() -> (String, usize, bool, HashMap<String, bool>) {
.help("Turn verbose printing on")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("arithmetic")
.short('a')
.long("arithmetic")
.help("Enable arithmetic mode")
.action(ArgAction::SetTrue),
)
.get_matches();
let file_name = matches
.get_one::<String>("input")
.expect("Verilog input file is required");
let num_cycles = *matches.get_one::<usize>("cycles").expect("required");
let verbose = matches.get_flag("verbose");
let arith_mode = matches.get_flag("arithmetic");
(file_name.to_string(), num_cycles, verbose, arith_mode, matches.get_one::<String>("wires").cloned())

let input_wire_map = {
if matches.contains_id("wires") {
let input_wires_file = matches.get_one::<String>("wires").unwrap();
}

verilog_parser::read_input_wires(input_wires_file)
fn get_input_wire_map<T>(wires: Option<String>) -> HashMap<String, T>
where
T: FromStr,
T::Err: Debug,
{
let input_wire_map = {
let wire_file_name = wires.unwrap_or("None".to_string());
if wire_file_name != "None" {
verilog_parser::read_input_wires::<T>(&wire_file_name)
} else {
println!(
"{}[!]{} No CSV file provided for the input wires,
they will be initialized to false.",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);

);
HashMap::new()
}
};

(file_name.to_string(), num_cycles, verbose, input_wire_map)
input_wire_map
}

fn main() {
ascii::print_art();
let (file_name, num_cycles, verbose, input_wire_map) = parse_args();
let (gates_set, wire_map_im, input_wires, output_wires, dff_outputs, is_sequential, has_luts, has_arith) =
verilog_parser::read_verilog_file(&file_name);

if num_cycles > 1 && !is_sequential {
panic!(
"{}[!]{} Cannot run combinational circuit for more than one cycles.",
color::Fg(color::LightRed),
color::Fg(color::Reset)
);
}
if gates_set.is_empty() {
panic!(
"{}[!]{} Parser error, no gates detected. Make sure to use the \
'no-expr' flag in Yosys.",
color::Fg(color::LightRed),
color::Fg(color::Reset)
);
}

let mut circuit_ptxt =
circuit::Circuit::new(gates_set, &input_wires, &output_wires, &dff_outputs);

circuit_ptxt.sort_circuit();
circuit_ptxt.compute_levels();
#[cfg(debug_assertions)]
circuit_ptxt.print_level_map();
debug_println!();

// Initialization of inputs
let mut wire_map = circuit_ptxt.initialize_wire_map(&wire_map_im, &input_wire_map);
debug_println!("before eval wire_map: {:?}", wire_map);

// Plaintext evaluation
for cycle in 0..num_cycles {
wire_map = circuit_ptxt.evaluate(&wire_map, 1);
println!("Cycle {}) Evaluation:", cycle);

#[cfg(debug_assertions)]
for wire_name in &output_wires {
println!(" {}: {}", wire_name, wire_map[wire_name]);
}
#[cfg(debug_assertions)]
println!();
}

// Encrypted Evaluation
if !has_luts && !has_arith {
let (file_name, num_cycles, verbose, arith_mode, wire_file) = parse_args();
let input_wire_map_bool : HashMap<String, bool>;
let input_wire_map_int : HashMap<String, u32>;
if arith_mode {
println!(
"{} -- Gates mode -- {}",
"{} -- Arithmetic mode -- {}",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);
input_wire_map_int = get_input_wire_map::<u32>(wire_file);
let (gates_set, wire_map_im, input_wires, output_wires, dff_outputs, _, _, _) =
verilog_parser::read_verilog_file::<u32>(&file_name, true);
let mut circuit_ptxt =
circuit::Circuit::new(gates_set.clone(), &input_wires, &output_wires, &dff_outputs);
if gates_set.is_empty() {
panic!(
"{}[!]{} Parser error, no arithmetic gates detected.",
color::Fg(color::LightRed),
color::Fg(color::Reset)
);
}
circuit_ptxt.sort_circuit();
circuit_ptxt.compute_levels();
#[cfg(debug_assertions)]
circuit_ptxt.print_level_map();
debug_println!();
// Initialization of inputs
let mut wire_map = circuit_ptxt.initialize_wire_map::<u32>(&wire_map_im, &input_wire_map_int);
debug_println!("before eval wire_map: {:?}", wire_map);

// Gate mode
// Arithmetic mode
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
None,
)
.build();
let mut start = Instant::now();
let (client_key, server_key) = gen_keys();
let (client_key, server_key) = generate_keys(config); // integer ctxt
let mut circuit = circuit::ArithCircuit::new(client_key, server_key, circuit_ptxt);
println!("KeyGen done in {} seconds.", start.elapsed().as_secs_f64());

let mut circuit = circuit::GateCircuit::new(client_key, server_key, circuit_ptxt);

// Client encrypts their inputs
start = Instant::now();
let mut enc_wire_map =
EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map);
EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map_int);
println!(
"Encryption done in {} seconds.",
start.elapsed().as_secs_f64()
Expand All @@ -156,93 +151,132 @@ fn main() {
"Decryption done in {} seconds.",
start.elapsed().as_secs_f64()
);
} else if has_arith {
println!(
"{} -- Arithmetic mode -- {}",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);

// Arithmetic mode
let config = ConfigBuilder::all_disabled()
.enable_custom_integers(
tfhe::shortint::parameters::PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS,
None,
)
.build();
let mut start = Instant::now();
let (client_key, server_key) = generate_keys(config); // integer ctxt
set_server_key(server_key.clone());
let mut circuit = circuit::ArithCircuit::new(client_key, server_key, circuit_ptxt);
println!("KeyGen done in {} seconds.", start.elapsed().as_secs_f64());
} else {
input_wire_map_bool = get_input_wire_map::<bool>(wire_file);
let (gates_set, wire_map_im, input_wires, output_wires, dff_outputs, is_sequential, has_luts, _) =
verilog_parser::read_verilog_file::<bool>(&file_name,false);
let mut circuit_ptxt =
circuit::Circuit::new(gates_set.clone(), &input_wires, &output_wires, &dff_outputs);
if num_cycles > 1 && !is_sequential {
panic!(
"{}[!]{} Cannot run combinational circuit for more than one cycles.",
color::Fg(color::LightRed),
color::Fg(color::Reset)
);
}
if gates_set.is_empty() {
panic!(
"{}[!]{} Parser error, no gates detected. Make sure to use the \
'no-expr' flag in Yosys.",
color::Fg(color::LightRed),
color::Fg(color::Reset)
);
}
circuit_ptxt.sort_circuit();
circuit_ptxt.compute_levels();
#[cfg(debug_assertions)]
circuit_ptxt.print_level_map();
debug_println!();

// Initialization of inputs
let mut wire_map = circuit_ptxt.initialize_wire_map::<bool>(&wire_map_im, &input_wire_map_bool);
debug_println!("before eval wire_map: {:?}", wire_map);

// Client encrypts their inputs
// start = Instant::now();
// let mut enc_wire_map =
// EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map);
// println!(
// "Encryption done in {} seconds.",
// start.elapsed().as_secs_f64()
// );
// Plaintext evaluation
for cycle in 0..num_cycles {
wire_map = circuit_ptxt.evaluate(&wire_map, 1);
println!("Cycle {}) Evaluation:", cycle);

// for cycle in 0..num_cycles {
// start = Instant::now();
// enc_wire_map = EvalCircuit::evaluate_encrypted(&mut circuit, &enc_wire_map, 1);
// println!(
// "Cycle {}) Evaluation done in {} seconds.\n",
// cycle,
// start.elapsed().as_secs_f64()
// );
// }
#[cfg(debug_assertions)]
for wire_name in &output_wires {
println!(" {}: {}", wire_name, wire_map[wire_name]);
}
#[cfg(debug_assertions)]
println!();
}

// // Client decrypts the output of the circuit
// start = Instant::now();
// println!("Encrypted Evaluation:");
// EvalCircuit::decrypt_outputs(&circuit, &enc_wire_map, verbose);
// println!(
// "Decryption done in {} seconds.",
// start.elapsed().as_secs_f64()
// );
} else {
println!(
"{} -- LUTs mode -- {}",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);
// Encrypted Evaluation
if !has_luts {
println!(
"{} -- Gates mode -- {}",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);

// LUT mode
let mut start = Instant::now();
let (client_key, server_key) = tfhe::shortint::gen_keys(PARAM_MESSAGE_4_CARRY_0); // single bit ctxt
let mut circuit = circuit::LutCircuit::new(client_key, server_key, circuit_ptxt);
println!("KeyGen done in {} seconds.", start.elapsed().as_secs_f64());
// Gate mode
let mut start = Instant::now();
let (client_key, server_key) = gen_keys();
println!("KeyGen done in {} seconds.", start.elapsed().as_secs_f64());
let mut circuit = circuit::GateCircuit::new(client_key, server_key, circuit_ptxt);

// Client encrypts their inputs
start = Instant::now();
let mut enc_wire_map =
EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map);
println!(
"Encryption done in {} seconds.",
start.elapsed().as_secs_f64()
);
// Client encrypts their inputs
start = Instant::now();
let mut enc_wire_map =
EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map_bool);
println!(
"Encryption done in {} seconds.",
start.elapsed().as_secs_f64()
);

for cycle in 0..num_cycles {
for cycle in 0..num_cycles {
start = Instant::now();
enc_wire_map = EvalCircuit::evaluate_encrypted(&mut circuit, &enc_wire_map, 1);
println!(
"Cycle {}) Evaluation done in {} seconds.\n",
cycle,
start.elapsed().as_secs_f64()
);
}

// Client decrypts the output of the circuit
start = Instant::now();
enc_wire_map = EvalCircuit::evaluate_encrypted(&mut circuit, &enc_wire_map, 1);
println!("Encrypted Evaluation:");
EvalCircuit::decrypt_outputs(&circuit, &enc_wire_map, verbose);
println!(
"Cycle {}) Evaluation done in {} seconds.\n",
cycle,
"Decryption done in {} seconds.",
start.elapsed().as_secs_f64()
);
} else {
println!(
"{} -- LUTs mode -- {}",
color::Fg(color::LightYellow),
color::Fg(color::Reset)
);

// LUT mode
let mut start = Instant::now();
let (client_key, server_key) = tfhe::shortint::gen_keys(PARAM_MESSAGE_4_CARRY_0); // single bit ctxt
let mut circuit = circuit::LutCircuit::new(client_key, server_key, circuit_ptxt);
println!("KeyGen done in {} seconds.", start.elapsed().as_secs_f64());

// Client encrypts their inputs
start = Instant::now();
let mut enc_wire_map =
EvalCircuit::encrypt_inputs(&mut circuit, &wire_map_im, &input_wire_map_bool);
println!(
"Encryption done in {} seconds.",
start.elapsed().as_secs_f64()
);

for cycle in 0..num_cycles {
start = Instant::now();
enc_wire_map = EvalCircuit::evaluate_encrypted(&mut circuit, &enc_wire_map, 1);
println!(
"Cycle {}) Evaluation done in {} seconds.\n",
cycle,
start.elapsed().as_secs_f64()
);
}

// Client decrypts the output of the circuit
start = Instant::now();
println!("Encrypted Evaluation:");
EvalCircuit::decrypt_outputs(&circuit, &enc_wire_map, verbose);
println!(
"Decryption done in {} seconds.",
start.elapsed().as_secs_f64()
);
}

// Client decrypts the output of the circuit
start = Instant::now();
println!("Encrypted Evaluation:");
EvalCircuit::decrypt_outputs(&circuit, &enc_wire_map, verbose);
println!(
"Decryption done in {} seconds.",
start.elapsed().as_secs_f64()
);
}
println!();
}
Loading

1 comment on commit 8ce9058

@jimouris
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cgouert This is the culprit!

Please sign in to comment.