This commit is contained in:
Rick Weber
2022-04-01 15:29:46 -07:00
parent d7c579e561
commit cc13dee263
5 changed files with 141 additions and 199 deletions

View File

@@ -1,10 +1,11 @@
use sunscreen::{
fhe_program,
types::{bfv::Rational, Cipher},
Ciphertext, CompiledFheProgram, Compiler, Params, PrivateKey, PublicKey,
Ciphertext, CompiledFheProgram, Compiler, Params, PrivateKey,
Error,
PublicKey,
Runtime,
};
use std::time::Instant;
#[fhe_program(scheme = "bfv")]
/// This program swaps NU tokens to receive ETH.
@@ -30,25 +31,25 @@ struct Miner {
}
impl Miner {
pub fn setup() -> Miner {
let swap_fhe = Compiler::with_fhe_program(swap_nu).compile().unwrap();
pub fn setup() -> Result<Miner, Error> {
let swap_fhe = Compiler::with_fhe_program(swap_nu).compile()?;
let runtime = Runtime::new(&swap_fhe.metadata.params).unwrap();
let runtime = Runtime::new(&swap_fhe.metadata.params)?;
Miner {
Ok(Miner {
swap_fhe,
runtime,
}
})
}
pub fn run_contract(
&self,
nu_tokens_to_trade: Ciphertext,
public_key: &PublicKey,
) -> Ciphertext {
let results = self.runtime.run(&self.swap_fhe, vec![nu_tokens_to_trade], public_key).unwrap();
) -> Result<Ciphertext, Error> {
let results = self.runtime.run(&self.swap_fhe, vec![nu_tokens_to_trade], public_key)?;
results[0].clone()
Ok(results[0].clone())
}
}
@@ -65,50 +66,51 @@ struct Alice {
}
impl Alice {
pub fn setup(params: &Params) -> Alice {
let runtime = Runtime::new(params).unwrap();
pub fn setup(params: &Params) -> Result<Alice, Error> {
let runtime = Runtime::new(params)?;
let (public_key, private_key) = runtime.generate_keys().unwrap();
let (public_key, private_key) = runtime.generate_keys()?;
Alice {
Ok(Alice {
public_key,
private_key,
runtime,
}
})
}
pub fn create_transaction(&self, amount: f64) -> Ciphertext {
self.runtime
.encrypt(Rational::try_from(amount).unwrap(), &self.public_key)
.unwrap()
pub fn create_transaction(&self, amount: f64) -> Result<Ciphertext, Error> {
Ok(self.runtime
.encrypt(Rational::try_from(amount)?, &self.public_key)?
)
}
pub fn check_received_eth(&self, received_eth: Ciphertext) {
pub fn check_received_eth(&self, received_eth: Ciphertext) -> Result<(), Error> {
let received_eth: Rational = self
.runtime
.decrypt(&received_eth, &self.private_key)
.unwrap();
.decrypt(&received_eth, &self.private_key)?;
let received_eth: f64 = received_eth.into();
println!("Alice received {}ETH", received_eth);
Ok(())
}
}
fn main() {
let comp_time = Instant::now();
fn main() -> Result<(), Error> {
// Set up the miner with some NU and ETH tokens.
let miner = Miner::setup();
println!("{}", comp_time.elapsed().as_secs_f64());
let miner = Miner::setup()?;
let run_time = Instant::now();
// Alice sets herself up. The FHE scheme parameters are public to the
// protocol, so Alice has them.
let alice = Alice::setup(&miner.swap_fhe.metadata.params);
let alice = Alice::setup(&miner.swap_fhe.metadata.params)?;
let transaction = alice.create_transaction(20.0)?;
let encrypted_received_eth =
miner.run_contract(alice.create_transaction(20.0), &alice.public_key);
miner.run_contract(transaction, &alice.public_key)?;
println!("{}", run_time.elapsed().as_secs_f64());
alice.check_received_eth(encrypted_received_eth);
alice.check_received_eth(encrypted_received_eth)?;
Ok(())
}

View File

@@ -1,47 +0,0 @@
#[derive(Debug)]
/// Represents any error that can happen in our app. This is the built-in
/// canonical way to do error handling in Rust, but there are better solutions
/// with less boilerplate. E.g. the [quick_error](https://docs.rs/quick-error/2.0.1/quick_error/)
/// and [anyhow](https://docs.rs/anyhow/latest/anyhow/index.html) crates.
pub enum Error {
SunscreenError(sunscreen::Error),
SendError,
RecvError,
IoError(std::io::Error),
ParseError,
}
/// Converts a sunscreen::Error into an Error. Needed to use `?` operator.
impl From<sunscreen::Error> for Error {
fn from(err: sunscreen::Error) -> Self {
Self::SunscreenError(err)
}
}
impl From<sunscreen::RuntimeError> for Error {
fn from(err: sunscreen::RuntimeError) -> Self {
Self::SunscreenError(err.into())
}
}
impl <T> From<std::sync::mpsc::SendError<T>> for Error {
fn from(_: std::sync::mpsc::SendError<T>) -> Error {
Self::SendError
}
}
impl From<std::sync::mpsc::RecvError> for Error {
fn from(_: std::sync::mpsc::RecvError) -> Error {
Self::RecvError
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Self::IoError(err)
}
}

View File

@@ -1,5 +1,3 @@
mod error;
use std::io::{self, Write};
use std::sync::mpsc::{Receiver, Sender};
use std::thread::{self, JoinHandle};
@@ -10,8 +8,6 @@ use sunscreen::{
RuntimeError,
};
use crate::error::Error;
fn help() {
println!("This is a privacy preserving calculator. You can add, subtract, multiply, divide decimal values. The operation is sent to Bob in cleartext while the operands
are encrypted. Bob chooses an FHE program corresponding to the selected operation and computes the result. Additionally, Bob saves the last computed value as `ans`, which you may use as either operand.");
@@ -50,6 +46,10 @@ enum ParseResult {
Expression(Expression),
}
enum Error {
ParseError,
}
fn parse_input(line: &str) -> Result<ParseResult, Error> {
if line == "help" {
return Ok(ParseResult::Help);
@@ -96,13 +96,14 @@ fn parse_input(line: &str) -> Result<ParseResult, Error> {
}))
}
fn encrypt_term(runtime: &Runtime, public_key: &PublicKey, input: Term) -> Result<Term, Error> {
fn encrypt_term(runtime: &Runtime, public_key: &PublicKey, input: Term) -> Term {
match input {
Term::Ans => Ok(Term::Ans),
Term::F64(v) => Ok(Term::Encrypted(
Term::Ans => Term::Ans,
Term::F64(v) => Term::Encrypted(
runtime
.encrypt(Rational::try_from(v)?, &public_key)?,
)),
.encrypt(Rational::try_from(v).unwrap(), &public_key)
.unwrap(),
),
_ => {
panic!("This shouldn't happen.");
}
@@ -116,88 +117,82 @@ fn alice(
recv_res: Receiver<Ciphertext>,
) -> JoinHandle<()> {
thread::spawn(move || {
let thread_body = move || -> Result<(), Error> {
let stdin = io::stdin();
let mut stdout = io::stdout();
let stdin = io::stdin();
let mut stdout = io::stdout();
println!("Bob's private calculator. Type `help` for help.");
println!("Bob's private calculator. Type `help` for help.");
// Bob needs to send us the scheme parameters compatible with his FHE program.
let params = recv_params.recv()?;
// Bob needs to send us the scheme parameters compatible with his FHE program.
let params = recv_params.recv().unwrap();
let runtime = Runtime::new(&params)?;
let runtime = Runtime::new(&params).unwrap();
let (public_key, private_key) = runtime.generate_keys()?;
let (public_key, private_key) = runtime.generate_keys().unwrap();
// Send Bob a copy of our public keys.
send_pub.send(public_key.clone())?;
// Send Bob a copy of our public keys.
send_pub.send(public_key.clone()).unwrap();
loop {
print!(">> ");
loop {
print!(">> ");
stdout.flush()?;
stdout.flush().unwrap();
let mut line = String::new();
stdin.read_line(&mut line)?;
let line = line.trim();
let mut line = String::new();
stdin.read_line(&mut line).unwrap();
let line = line.trim();
// Read the line and parse it into operands and an operator.
let parsed = parse_input(&line);
// Read the line and parse it into operands and an operator.
let parsed = parse_input(&line);
let Expression { left, right, op } = match parsed {
Ok(ParseResult::Expression(val)) => val,
Ok(ParseResult::Exit) => std::process::exit(0),
Ok(ParseResult::Help) => {
help();
continue;
}
Err(_) => {
println!("Parse error. Try again.");
continue;
}
};
let Expression { left, right, op } = match parsed {
Ok(ParseResult::Expression(val)) => val,
Ok(ParseResult::Exit) => std::process::exit(0),
Ok(ParseResult::Help) => {
help();
continue;
}
Err(_) => {
println!("Parse error. Try again.");
continue;
}
};
// Encrypt the left and right terms.
let encrypt_left = encrypt_term(&runtime, &public_key, left)?;
let encrypt_right = encrypt_term(&runtime, &public_key, right)?;
// Encrypt the left and right terms.
let encrypt_left = encrypt_term(&runtime, &public_key, left);
let encrypt_right = encrypt_term(&runtime, &public_key, right);
// Send Bob our encrypted operation.
send_calc
.send(Expression {
left: encrypt_left,
right: encrypt_right,
op: op,
})?;
// Send Bob our encrypted operation.
send_calc
.send(Expression {
left: encrypt_left,
right: encrypt_right,
op: op,
})
.unwrap();
// Get our result from Bob and print it.
let result: Ciphertext = recv_res.recv()?;
let result: Rational = match runtime.decrypt(&result, &private_key) {
Ok(v) => v,
Err(RuntimeError::TooMuchNoise) => {
println!("Decryption failed: too much noise");
continue;
}
Err(e) => { return Err(e.into()); },
};
let result: f64 = result.into();
// Get our result from Bob and print it.
let result: Ciphertext = recv_res.recv().unwrap();
let result: Rational = match runtime.decrypt(&result, &private_key) {
Ok(v) => v,
Err(RuntimeError::TooMuchNoise) => {
println!("Decryption failed: too much noise");
continue;
}
Err(e) => panic!("{:#?}", e),
};
let result: f64 = result.into();
println!("{}", result);
}
};
match thread_body() {
Ok(_) => {},
Err(e) => panic!("{:#?}", e),
println!("{}", result);
}
})
}
fn compile_fhe_programs() -> Result<(
fn compile_fhe_programs() -> (
CompiledFheProgram,
CompiledFheProgram,
CompiledFheProgram,
CompiledFheProgram,
), Error> {
) {
#[fhe_program(scheme = "bfv")]
fn add(a: Cipher<Rational>, b: Cipher<Rational>) -> Cipher<Rational> {
a + b
@@ -228,24 +223,24 @@ fn compile_fhe_programs() -> Result<(
.additional_noise_budget(32)
.plain_modulus_constraint(PlainModulusConstraint::Raw(1_000_000))
.compile()
?;
.unwrap();
let mul_program = Compiler::with_fhe_program(mul)
.with_params(&add_program.metadata.params)
.compile()
?;
.unwrap();
let div_program = Compiler::with_fhe_program(div)
.with_params(&add_program.metadata.params)
.compile()
?;
.unwrap();
let sub_program = Compiler::with_fhe_program(sub)
.with_params(&add_program.metadata.params)
.compile()
?;
.unwrap();
Ok((add_program, sub_program, mul_program, div_program))
(add_program, sub_program, mul_program, div_program)
}
fn bob(
@@ -255,58 +250,50 @@ fn bob(
send_res: Sender<Ciphertext>,
) -> JoinHandle<()> {
thread::spawn(move || {
let thread_body = move || -> Result<(), Error> {
let (add, sub, mul, div) = compile_fhe_programs()?;
let (add, sub, mul, div) = compile_fhe_programs();
send_params.send(add.metadata.params.clone())?;
send_params.send(add.metadata.params.clone()).unwrap();
let public_key = recv_pub.recv()?;
let public_key = recv_pub.recv().unwrap();
let runtime = Runtime::new(&add.metadata.params)?;
let runtime = Runtime::new(&add.metadata.params).unwrap();
let mut ans = runtime
.encrypt(Rational::try_from(0f64)?, &public_key)
?;
let mut ans = runtime
.encrypt(Rational::try_from(0f64).unwrap(), &public_key)
.unwrap();
loop {
let Expression { left, right, op } = recv_calc.recv()?;
loop {
let Expression { left, right, op } = recv_calc.recv().unwrap();
let left = match left {
Term::Ans => ans.clone(),
Term::Encrypted(c) => c,
_ => panic!("Alice sent us a plaintext!"),
};
let left = match left {
Term::Ans => ans.clone(),
Term::Encrypted(c) => c,
_ => panic!("Alice sent us a plaintext!"),
};
let right = match right {
Term::Ans => ans.clone(),
Term::Encrypted(c) => c,
_ => panic!("Alice sent us a plaintext!"),
};
let right = match right {
Term::Ans => ans.clone(),
Term::Encrypted(c) => c,
_ => panic!("Alice sent us a plaintext!"),
};
let mut c = match op {
Operand::Add => runtime.run(&add, vec![left, right], &public_key)?,
Operand::Sub => runtime.run(&sub, vec![left, right], &public_key)?,
Operand::Mul => runtime.run(&mul, vec![left, right], &public_key)?,
Operand::Div => runtime.run(&div, vec![left, right], &public_key)?,
};
let mut c = match op {
Operand::Add => runtime.run(&add, vec![left, right], &public_key).unwrap(),
Operand::Sub => runtime.run(&sub, vec![left, right], &public_key).unwrap(),
Operand::Mul => runtime.run(&mul, vec![left, right], &public_key).unwrap(),
Operand::Div => runtime.run(&div, vec![left, right], &public_key).unwrap(),
};
// Our FHE program produces a single value, so move the value out of the vector.
let c = c.drain(0..).next().expect("Internal error: FHE program didn't produce a result");
ans = c.clone();
// Our FHE program produces a single value, so move the value out of the vector.
let c = c.drain(0..).next().unwrap();
ans = c.clone();
send_res.send(c)?;
}
};
match thread_body() {
Ok(_) => {},
Err(e) => panic!("{:#?}", e),
};
send_res.send(c).unwrap();
}
})
}
fn main() -> Result<(), Error> {
fn main() {
// A channel for Alice to send her public keys to Bob.
let (send_alice_pub, receive_alice_pub) = std::sync::mpsc::channel::<PublicKey>();
@@ -337,6 +324,4 @@ fn main() -> Result<(), Error> {
a.join().unwrap();
b.join().unwrap();
Ok(())
}

View File

@@ -227,15 +227,15 @@ where
}
fn main() -> Result<(), Error> {
let n_0 = 1 << 31 - 1;
let n_1 = 1 << 31 - 1;
let n_2 = 1 << 31 - 1;
let n_0 = 2;
let n_1 = 7;
let n_2 = 9;
env_logger::init();
// Signed types allow us to use a really small modulus,
// allowing us to get very performant parameters.
let plain_modulus = PlainModulusConstraint::Raw(200);
let plain_modulus = PlainModulusConstraint::Raw(64);
println!("**********Naive**************");
println!("\t**********Native************");

View File

@@ -38,8 +38,10 @@ fn main() -> Result<(), Error> {
* Sunscreen allows experts to explicitly set the scheme parameters, but the default behavior
* is to let the compiler run your FHE program a number of times with different parameters and measure
* the resulting noise.
* Afterwards, we simply compile and assert the compilation succeeds by calling unwrap. Compilation
*
* Afterwards, we simply compile. The `?` operator is Rust's standard
* error handling mechanism; the current function (`main`)
* returns when an error occurs or continues on success. Compilation
* returns the compiled FHE program and parameters.
*/
let fhe_program = Compiler::with_fhe_program(simple_multiply)