This commit is contained in:
Rick Weber
2021-12-13 11:35:48 -08:00
parent 0110b3bc2d
commit b36bbe6901
7 changed files with 115 additions and 53 deletions

7
Cargo.lock generated
View File

@@ -134,6 +134,12 @@ dependencies = [
"cc",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "crossbeam"
version = "0.8.1"
@@ -565,6 +571,7 @@ dependencies = [
name = "sunscreen_compiler_macros"
version = "0.1.0"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"serde_json",

View File

@@ -9,9 +9,8 @@ use sunscreen_runtime::PrivateRuntime;
* the result. Circuits may take any number of parameters and return either a single result
* or a tuple of results.
*
* Currently, the Unsigned type is the only legal type in circuit parameters and return values,
* which serves as a placeholder that allows the compiler to build up the circuit. Don't attach
* much meaning to it in its current form; this example in fact uses unsigned values!
* The unsigned type refers to an unsigned integer modulo the plaintext
* modulus (p). p is passed to the compiler via plain_modulus_constraint.
*
* One takes a circuit and passes them to the compiler, which transforms it into a form
* suitable for execution.
@@ -63,17 +62,31 @@ fn main() {
let (public, secret) = runtime.generate_keys().unwrap();
/*
* Encrypt the values 15 and 5, which matches the circuits interface.
* Our circuit accepts 2 `Unsigned` types and adds them together.
* The encrypt macro takes a runtime and public key as its first 2
* arguments to faciliation encryption, and the remaining arguments
* are the actual arguments to the circuit. This macro is variadic;
* circuits that take more arguments require more parameters.
*/
let args = encrypt!(runtime, &public, Unsigned::from(15), Unsigned::from(5)).unwrap();
/*
* Run the circuit with our arguments. This produces a results
* bundle containing the encrypted outputs of the circuit.
*/
let mut results = runtime
.run(&circuit, args)
.unwrap();
/*
* Our circuit produces a single output rather than a tuple, so the resulting Vec should contain
* exactly one value.
* Our circuit produces a single `Unsigned` output. The decrypt
* macro takes a runtime, secret key, and results bundle as the
* first three arguments. The macro is variadic and the remaining
* arguments are the types of the circuit's outputs.
*
* The decrypt macro validates the types we pass match the
* circuit's return types. We need to pass these so types so
* the compiler can ensure the return type is known at compile time.
*/
let c = decrypt!(runtime, &secret, results, Unsigned).unwrap();

View File

@@ -9,6 +9,7 @@ proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
convert_case = "0.4.0"
proc-macro2 = "1.0.32"
quote = "1.0.10"
syn = { version = "1.0.81", features = ["derive", "full", "fold"] }

View File

@@ -1,8 +1,9 @@
use crate::internals::{attr::Attrs, case::Scheme};
use convert_case::{Case, Casing};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{
parse_macro_input, spanned::Spanned, FnArg, Ident, Index, ItemFn, Pat, ReturnType, Type,
parse_macro_input, spanned::Spanned, FnArg, Ident, Index, ItemFn, Pat, ReturnType, Type, punctuated::Punctuated, Token
};
pub fn circuit_impl(
@@ -87,47 +88,9 @@ pub fn circuit_impl(
}
});
let capture_outputs = match ret {
ReturnType::Type(_, t) => {
let tuple_inners = match &**t {
Type::Tuple(t) => t.elems.iter().map(|x| &*x).collect::<Vec<&Type>>(),
Type::Paren(t) => {
vec![&*t.elem]
}
Type::Path(_) => {
vec![&**t]
}
_ => {
return proc_macro::TokenStream::from(quote! {
compile_error!("Circuits must return a single Cipthertext or a tuple of Ciphertexts");
});
}
};
let catpured_outputs = capture_outputs(ret);
if tuple_inners.len() == 1 {
quote_spanned! {tuple_inners[0].span() =>
v.output();
}
} else {
tuple_inners
.iter()
.enumerate()
.map(|(i, t)| {
let index = Index::from(i);
quote_spanned! {t.span() =>
v.#index.output();
}
})
.collect()
}
}
ReturnType::Default => {
quote! {}
}
};
let foo = proc_macro::TokenStream::from(quote! {
proc_macro::TokenStream::from(quote! {
#(#attrs)*
#vis fn #circuit_name() -> (
sunscreen_compiler::SchemeType,
@@ -163,7 +126,7 @@ pub fn circuit_impl(
});
match panic_res {
Ok(v) => { #capture_outputs },
Ok(v) => { #catpured_outputs },
Err(err) => {
ctx.swap(&RefCell::new(None));
std::panic::resume_unwind(err)
@@ -180,11 +143,82 @@ pub fn circuit_impl(
(#scheme_type, circuit_builder, signature)
}
})
}
fn emit_encoder(
circuit_name: &str,
inputs: &[(&syn::PatType, &proc_macro2::Ident)],
ret: &ReturnType
) -> TokenStream {
let struct_name = Ident::new(&format!("{}Interface", circuit_name.to_case(Case::Pascal)), Span::call_site());
let arguments = inputs.iter().map(|(t, ident)| {
quote_spanned! { t.span()=>
.arg(#ident)
}
});
// panic!("{}", foo);
let fn_args = inputs.iter().map(|(t, _)| {
quote! {
#t,
}
}).collect::<Vec<TokenStream>>();
foo
quote! {
pub struct #struct_name;
impl #struct_name {
fn args(#(#fn_args)*) -> sunscreen_compiler::Arguments {
sunscreen_compiler::Arguments::new()
#(#arguments)*
}
}
}
}
fn capture_outputs(ret: &ReturnType) -> TokenStream {
match ret {
ReturnType::Type(_, t) => {
let tuple_inners = match &**t {
Type::Tuple(t) => t.elems.iter().map(|x| &*x).collect::<Vec<&Type>>(),
Type::Paren(t) => {
vec![&*t.elem]
}
Type::Path(_) => {
vec![&**t]
}
_ => {
return TokenStream::from(quote! {
compile_error!("Circuits must return a single Cipthertext or a tuple of Ciphertexts");
});
}
};
if tuple_inners.len() == 1 {
quote_spanned! {tuple_inners[0].span() =>
v.output();
}
} else {
tuple_inners
.iter()
.enumerate()
.map(|(i, t)| {
let index = Index::from(i);
quote_spanned! {t.span() =>
v.#index.output();
}
})
.collect()
}
}
ReturnType::Default => {
quote! {}
}
}
}
fn create_signature(args: &[&Type], ret: &ReturnType) -> TokenStream {

View File

@@ -11,6 +11,7 @@ use std::collections::HashMap;
pub struct Attrs {
pub scheme: Scheme,
pub interface_file: Option<String>
}
impl Parse for Attrs {
@@ -82,10 +83,16 @@ impl Parse for Attrs {
"`scheme` requires a value".to_owned(),
))?;
let interface_file = attrs
.get("interface_file")
.map(|s| s.to_owned())
.unwrap_or(None);
Ok(Self {
scheme: Scheme::parse(&scheme_type).map_err(|_e| {
Error::new_spanned(vars, format!("Unknown variant {}", &scheme_type))
})?,
interface_file: interface_file,
})
}
}

View File

@@ -41,4 +41,4 @@ pub struct InputBundle {
/**
* The encrypted result of running a circuit.
*/
pub struct OutputBundle(pub Vec<Ciphertext>);
pub struct OutputBundle(pub Vec<Ciphertext>);

View File

@@ -18,8 +18,8 @@ enum Context {
}
/**
* A private runtime is one that can perform operations that require either a public or
* secret key.
* A private runtime is one that can perform both operations that require
* a secret key and operations that require a public key.
*/
pub struct PrivateRuntime {
public_runtime: PublicRuntime,