This is an automated email from the ASF dual-hosted git repository.
mssun pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave.git
The following commit(s) were added to refs/heads/develop by this push:
new 7fc6cdb [workers] Bridging mesapy with tests (#215)
7fc6cdb is described below
commit 7fc6cdb66a1a5639db264ef195adb689a38a2ddb
Author: Zhaofeng Chen <[email protected]>
AuthorDate: Mon Feb 3 23:11:20 2020 -0800
[workers] Bridging mesapy with tests (#215)
---
tests/test_cases/mesapy/input.txt | 2 +
tests/test_cases/mesapy/output.txt | 0
types/src/worker.rs | 9 +
worker/Cargo.toml | 1 +
worker/src/function/context.rs | 382 +++++++++++++++++++++++++++++++++++
worker/src/function/gbdt_training.rs | 10 +-
worker/src/function/mesapy.rs | 152 ++++++++++++--
worker/src/function/mod.rs | 8 +-
worker/src/lib.rs | 1 +
9 files changed, 544 insertions(+), 21 deletions(-)
diff --git a/tests/test_cases/mesapy/input.txt
b/tests/test_cases/mesapy/input.txt
new file mode 100644
index 0000000..ed81d07
--- /dev/null
+++ b/tests/test_cases/mesapy/input.txt
@@ -0,0 +1,2 @@
+Hello
+World
\ No newline at end of file
diff --git a/tests/test_cases/mesapy/output.txt
b/tests/test_cases/mesapy/output.txt
new file mode 100644
index 0000000..e69de29
diff --git a/types/src/worker.rs b/types/src/worker.rs
index c86f391..5dcb10c 100644
--- a/types/src/worker.rs
+++ b/types/src/worker.rs
@@ -170,6 +170,15 @@ impl TeaclaveFunctionArguments {
.map_err(|_| anyhow::anyhow!("parse argument error"))
})
}
+
+ pub fn into_vec(self) -> Vec<String> {
+ let mut vector = Vec::new();
+ self.args.into_iter().for_each(|(k, v)| {
+ vector.push(k);
+ vector.push(v);
+ });
+ vector
+ }
}
#[derive(Serialize, Deserialize, Debug)]
diff --git a/worker/Cargo.toml b/worker/Cargo.toml
index b8f8f92..1ca91fa 100644
--- a/worker/Cargo.toml
+++ b/worker/Cargo.toml
@@ -26,6 +26,7 @@ serde_json = { version = "1.0.39" }
serde = { version = "1.0.92", features = ["derive"] }
thiserror = { version = "1.0.9" }
gbdt = { version = "0.1.0", features = ["input", "enable_training"] }
+itertools = { version = "0.8.0", default-features = false }
teaclave_types = { path = "../types" }
teaclave_test_utils = { path = "../tests/test_utils", optional = true }
diff --git a/worker/src/function/context.rs b/worker/src/function/context.rs
new file mode 100644
index 0000000..791ca04
--- /dev/null
+++ b/worker/src/function/context.rs
@@ -0,0 +1,382 @@
+#[cfg(feature = "mesalock_sgx")]
+use std::prelude::v1::*;
+
+use std::cell::RefCell;
+use std::slice;
+use std::thread_local;
+
+use sgx_types::{c_char, c_int, c_uchar, size_t};
+
+use anyhow;
+use std::collections::HashMap;
+use std::format;
+
+use crate::runtime::TeaclaveRuntime;
+
+const FFI_OK: c_int = 0;
+const FFI_FILE_ERROR: c_int = -1;
+
+pub struct Context {
+ runtime: Box<dyn TeaclaveRuntime + Send + Sync>,
+ seq: Sequence,
+ read_handles: HandleRegistry<Box<dyn std::io::Read>>,
+ write_handles: HandleRegistry<Box<dyn std::io::Write>>,
+}
+
+impl Context {
+ pub fn new(runtime: Box<dyn TeaclaveRuntime + Send + Sync>) -> Context {
+ Context {
+ runtime,
+ seq: Sequence::new(1, 1024),
+ read_handles: HandleRegistry::default(),
+ write_handles: HandleRegistry::default(),
+ }
+ }
+
+ fn open_input(&mut self, fid: &str) -> anyhow::Result<FileHandle> {
+ let file = self.runtime.open_input(fid)?;
+ let handle = self.seq.next()?.into_read_handle();
+ self.read_handles.add(handle, file)?;
+ Ok(handle)
+ }
+
+ fn create_output(&mut self, fid: &str) -> anyhow::Result<FileHandle> {
+ let file = self.runtime.create_output(fid)?;
+ let handle = self.seq.next()?.into_write_handle();
+ self.write_handles.add(handle, file)?;
+ Ok(handle)
+ }
+
+ fn read_handle(&mut self, handle: FileHandle, buf: &mut [u8]) ->
anyhow::Result<usize> {
+ let file = self.read_handles.get_mut(handle)?;
+ let size = file.read(buf)?;
+ Ok(size)
+ }
+
+ fn write_handle(&mut self, handle: FileHandle, buf: &[u8]) ->
anyhow::Result<usize> {
+ let file = self.write_handles.get_mut(handle)?;
+ let size = file.write(buf)?;
+ Ok(size)
+ }
+
+ fn close_handle(&mut self, handle: FileHandle) -> anyhow::Result<()> {
+ if handle.is_read_handle() {
+ self.read_handles.remove(handle)?;
+ } else {
+ self.write_handles.remove(handle)?;
+ }
+ Ok(())
+ }
+}
+
+trait HandleEncoding {
+ fn into_write_handle(self) -> FileHandle;
+ fn into_read_handle(self) -> FileHandle;
+ fn is_write_handle(&self) -> bool;
+ fn is_read_handle(&self) -> bool;
+}
+
+impl HandleEncoding for FileHandle {
+ fn into_write_handle(self) -> FileHandle {
+ assert!(self < HANDLE_UPPDER_BOUND);
+ 0x4000_0000 | self
+ }
+
+ fn is_write_handle(&self) -> bool {
+ 0x4000_0000 & self > 0
+ }
+
+ fn into_read_handle(self) -> FileHandle {
+ assert!(self < HANDLE_UPPDER_BOUND);
+ self
+ }
+
+ fn is_read_handle(&self) -> bool {
+ !self.is_write_handle()
+ }
+}
+
+struct Sequence {
+ range: std::ops::Range<FileHandle>,
+}
+
+impl Sequence {
+ fn new(start: FileHandle, end: FileHandle) -> Self {
+ Sequence {
+ range: (start..end),
+ }
+ }
+
+ fn next(&mut self) -> anyhow::Result<FileHandle> {
+ self.range
+ .next()
+ .ok_or_else(|| anyhow::anyhow!("Reached max sequence"))
+ }
+}
+
+type FileHandle = i32;
+const HANDLE_UPPDER_BOUND: FileHandle = 0x1000_0000;
+
+struct HandleRegistry<T> {
+ entries: HashMap<FileHandle, T>,
+}
+
+impl<T> HandleRegistry<T> {
+ fn add(&mut self, handle: FileHandle, obj: T) -> anyhow::Result<()> {
+ anyhow::ensure!(
+ self.entries.get(&handle).is_none(),
+ "Reuse a existed handle: {}",
+ handle
+ );
+ self.entries.insert(handle, obj);
+ Ok(())
+ }
+
+ fn get_mut(&mut self, handle: FileHandle) -> anyhow::Result<&mut T> {
+ self.entries
+ .get_mut(&handle)
+ .ok_or_else(|| anyhow::anyhow!("Get an invalid handle: {}",
handle))
+ }
+
+ fn remove(&mut self, handle: FileHandle) -> anyhow::Result<()> {
+ self.entries
+ .remove(&handle)
+ .ok_or_else(|| anyhow::anyhow!("Remove an invalid handle: {}",
handle))?;
+ Ok(())
+ }
+}
+
+impl<T> std::default::Default for HandleRegistry<T> {
+ fn default() -> Self {
+ HandleRegistry {
+ entries: HashMap::<FileHandle, T>::new(),
+ }
+ }
+}
+
+thread_local! {
+ pub static CONTEXT: RefCell<Option<Context>> = RefCell::new(None);
+}
+
+pub fn reset_thread_context() -> anyhow::Result<()> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ *ctx = None;
+ Ok(())
+ })
+}
+
+pub fn set_thread_context(context: Context) -> anyhow::Result<()> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_none(), "Context already initialized");
+ *ctx = Some(context);
+ Ok(())
+ })
+}
+
+pub fn rtc_open_input(fid: &str) -> anyhow::Result<FileHandle> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ ctx.as_mut().unwrap().open_input(fid)
+ })
+}
+
+pub fn rtc_create_output(fid: &str) -> anyhow::Result<FileHandle> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ ctx.as_mut().unwrap().create_output(fid)
+ })
+}
+
+pub fn rtc_read_handle(f: FileHandle, buf: &mut [u8]) -> anyhow::Result<usize>
{
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ ctx.as_mut().unwrap().read_handle(f, buf)
+ })
+}
+
+pub fn rtc_write_handle(f: FileHandle, buf: &[u8]) -> anyhow::Result<usize> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ ctx.as_mut().unwrap().write_handle(f, buf)
+ })
+}
+
+pub fn rtc_close_handle(f: FileHandle) -> anyhow::Result<()> {
+ CONTEXT.with(|ctx| {
+ let mut ctx = ctx.borrow_mut();
+ anyhow::ensure!(ctx.is_some(), "Context not initialized");
+ ctx.as_mut().unwrap().close_handle(f)
+ })
+}
+
+#[cfg(feature = "enclave_unit_test")]
+pub mod tests {
+ use super::*;
+ use crate::hashmap;
+ use crate::runtime::RawIoRuntime;
+ use std::path::PathBuf;
+ use std::str::FromStr;
+ use teaclave_test_utils::*;
+ use teaclave_types::AesGcm256CryptoInfo;
+ use teaclave_types::TeaclaveFileCryptoInfo;
+ use teaclave_types::TeaclaveWorkerFileInfo;
+ use teaclave_types::TeaclaveWorkerFileRegistry;
+
+ pub fn run_tests() -> bool {
+ run_tests!(test_file_handle_encoding, test_rtc_api,)
+ }
+
+ fn test_file_handle_encoding() {
+ assert_eq!(5, 5.into_read_handle());
+ assert_eq!(0x4000_0006, 6.into_write_handle());
+ assert_eq!(true, 0x4000_0000.is_write_handle());
+ assert_eq!(false, 0x4000_0000.is_read_handle());
+ assert_eq!(true, 0x9.is_read_handle());
+ assert_eq!(false, 0xff.is_write_handle());
+ }
+
+ fn test_rtc_api() {
+ let input = PathBuf::from_str("test_cases/mesapy/input.txt").unwrap();
+ let output =
PathBuf::from_str("test_cases/mesapy/output.txt.out").unwrap();
+
+ let input_info = TeaclaveWorkerFileInfo {
+ path: input,
+ crypto_info:
TeaclaveFileCryptoInfo::AesGcm256(AesGcm256CryptoInfo::default()),
+ };
+
+ let output_info = TeaclaveWorkerFileInfo {
+ path: output,
+ crypto_info:
TeaclaveFileCryptoInfo::AesGcm256(AesGcm256CryptoInfo::default()),
+ };
+
+ let in_fid = "in_f1";
+ let out_fid = "out_f1";
+ let input_files = TeaclaveWorkerFileRegistry {
+ entries: hashmap!(in_fid.to_string() => input_info),
+ };
+
+ let output_files = TeaclaveWorkerFileRegistry {
+ entries: hashmap!(out_fid.to_string() => output_info),
+ };
+
+ let runtime = Box::new(RawIoRuntime::new(input_files, output_files));
+ set_thread_context(Context::new(runtime)).unwrap();
+
+ let expected_input = "Hello\nWorld".as_bytes();
+ let f = rtc_open_input(&in_fid).unwrap();
+ let mut buf = [0u8; 128];
+ let size = rtc_read_handle(f, &mut buf).unwrap();
+ assert_eq!(&expected_input[..], &buf[..size]);
+
+ assert!(rtc_close_handle(f).is_ok());
+ assert!(rtc_close_handle(f).is_err());
+
+ let f = rtc_create_output(&out_fid).unwrap();
+ let size = rtc_write_handle(f, &expected_input).unwrap();
+ assert_eq!(size, expected_input.len());
+
+ assert!(rtc_close_handle(f).is_ok());
+ assert!(rtc_close_handle(f).is_err());
+ }
+}
+
+use std::ffi::CStr;
+
+#[allow(unused)]
+#[no_mangle]
+extern "C" fn c_open_input(fid: *mut c_char, out_handle: *mut c_int) -> c_int {
+ let fid = unsafe { CStr::from_ptr(fid).to_string_lossy().into_owned() };
+ match rtc_open_input(&fid) {
+ Ok(handle) => {
+ unsafe {
+ *out_handle = handle;
+ }
+ FFI_OK
+ }
+ Err(e) => {
+ error!("c_open_file: {:?}", e);
+ FFI_FILE_ERROR
+ }
+ }
+}
+
+#[allow(unused)]
+#[no_mangle]
+extern "C" fn c_create_output(fid: *mut c_char, out_handle: *mut c_int) ->
c_int {
+ let fid = unsafe { CStr::from_ptr(fid).to_string_lossy().into_owned() };
+ match rtc_create_output(&fid) {
+ Ok(handle) => {
+ unsafe {
+ *out_handle = handle;
+ }
+ FFI_OK
+ }
+ Err(e) => {
+ error!("c_open_file: {:?}", e);
+ FFI_FILE_ERROR
+ }
+ }
+}
+
+#[allow(unused)]
+#[no_mangle]
+extern "C" fn c_read_file(
+ handle: c_int,
+ out_buf: *mut c_uchar,
+ out_buf_size_p: *mut size_t,
+) -> c_int {
+ let out_buf_size = unsafe { *out_buf_size_p };
+ let out: &mut [u8] = unsafe { slice::from_raw_parts_mut(out_buf,
out_buf_size) };
+
+ match rtc_read_handle(handle, out) {
+ Ok(size) => {
+ unsafe {
+ *out_buf_size_p = size;
+ }
+ FFI_OK
+ }
+ Err(e) => {
+ error!("c_read_file: {:?}", e);
+ FFI_FILE_ERROR
+ }
+ }
+}
+
+#[allow(unused)]
+#[no_mangle]
+extern "C" fn c_write_file(handle: c_int, in_buf: *mut c_uchar, buf_size_p:
*mut size_t) -> c_int {
+ let out_buf_size = unsafe { *buf_size_p };
+ let in_buf: &[u8] = unsafe { slice::from_raw_parts_mut(in_buf,
out_buf_size) };
+
+ match rtc_write_handle(handle, in_buf) {
+ Ok(size) => {
+ unsafe {
+ *buf_size_p = size;
+ }
+ FFI_OK
+ }
+ Err(e) => {
+ error!("c_write_file: {:?}", e);
+ FFI_FILE_ERROR
+ }
+ }
+}
+
+#[allow(unused)]
+#[no_mangle]
+extern "C" fn c_close_file(handle: c_int) -> c_int {
+ match rtc_close_handle(handle) {
+ Ok(size) => FFI_OK,
+ Err(e) => {
+ error!("c_close_file: {:?}", e);
+ FFI_FILE_ERROR
+ }
+ }
+}
diff --git a/worker/src/function/gbdt_training.rs
b/worker/src/function/gbdt_training.rs
index b597f02..4406380 100644
--- a/worker/src/function/gbdt_training.rs
+++ b/worker/src/function/gbdt_training.rs
@@ -125,12 +125,18 @@ fn parse_training_data(input: impl io::Read,
feature_size: usize) -> anyhow::Res
#[cfg(feature = "enclave_unit_test")]
pub mod tests {
use super::*;
+ use teaclave_test_utils::*;
+
use crate::function::TeaclaveFunction;
use crate::runtime::RawIoRuntime;
use std::untrusted::fs;
use teaclave_types::{TeaclaveFunctionArguments,
TeaclaveWorkerFileRegistry};
- pub fn test_gbdt_training() {
+ pub fn run_tests() -> bool {
+ run_tests!(test_gbdt_training, test_gbdt_parse_training_data,)
+ }
+
+ fn test_gbdt_training() {
let args_json = r#"
{
"feature_size": "4",
@@ -189,7 +195,7 @@ pub mod tests {
assert_eq!(&result[..], &expected[..]);
}
- pub fn test_gbdt_parse_training_data() {
+ fn test_gbdt_parse_training_data() {
let line = "4.8,3.0,1.4,0.3,3.0";
let result = parse_data_line(&line, 4);
assert_eq!(result.is_ok(), true);
diff --git a/worker/src/function/mesapy.rs b/worker/src/function/mesapy.rs
index a627cb6..7cfe721 100644
--- a/worker/src/function/mesapy.rs
+++ b/worker/src/function/mesapy.rs
@@ -19,21 +19,31 @@
use std::prelude::v1::*;
use anyhow;
+use itertools::Itertools;
use crate::function::TeaclaveFunction;
use crate::runtime::TeaclaveRuntime;
use teaclave_types::TeaclaveFunctionArguments;
-/* TODO: export wrapped io stream handle to mesapy-sgx
-extern "C"
-t_open(context, file_identifier) -> handle () {
- runtime = c_to_rust(context); // thread_local
- runtime.open(file_identifier);
+use crate::function::context::reset_thread_context;
+use crate::function::context::set_thread_context;
+use crate::function::context::Context;
+use std::ffi::CString;
+use std::format;
+
+const MAXPYBUFLEN: usize = 20480;
+const MESAPY_ERROR_BUFFER_TOO_SHORT: i64 = -1i64;
+const MESAPY_EXEC_ERROR: i64 = -2i64;
+
+extern "C" {
+ fn mesapy_exec(
+ input: *const u8,
+ argc: usize,
+ argv: *const *const sgx_types::c_char,
+ output: *mut u8,
+ buflen: u64,
+ ) -> i64;
}
-t_read(context, handle, buf);
-t_write(context, handle, buf);
-t_close(context, handle);
-*/
#[derive(Default)]
pub struct Mesapy;
@@ -41,13 +51,123 @@ pub struct Mesapy;
impl TeaclaveFunction for Mesapy {
fn execute(
&self,
- _runtime: Box<dyn TeaclaveRuntime + Send + Sync>,
- _args: TeaclaveFunctionArguments,
+ runtime: Box<dyn TeaclaveRuntime + Send + Sync>,
+ args: TeaclaveFunctionArguments,
) -> anyhow::Result<String> {
- // TODO:
- // args.get("py_payload")
- // args.get("py_args")
- // mesapy_exec();
- unimplemented!()
+ let script = args.try_get::<String>("py_payload")?;
+ let py_args = args.try_get::<String>("py_args")?;
+ let py_args: TeaclaveFunctionArguments =
serde_json::from_str(&py_args)?;
+ let py_argv = py_args.into_vec();
+ let cstr_argv: Vec<_> = py_argv
+ .iter()
+ .map(|arg| CString::new(arg.as_str()).unwrap())
+ .collect();
+
+ let mut script_bytes = script.into_bytes();
+ script_bytes.push(0u8);
+
+ let mut p_argv: Vec<_> = cstr_argv
+ .iter() // do NOT into_iter()
+ .map(|arg| arg.as_ptr())
+ .collect();
+
+ p_argv.push(std::ptr::null());
+
+ let mut py_result = [0u8; MAXPYBUFLEN];
+
+ set_thread_context(Context::new(runtime))?;
+
+ let result = unsafe {
+ mesapy_exec(
+ script_bytes.as_ptr(),
+ p_argv.len() - 1,
+ p_argv.as_ptr(),
+ &mut py_result as *mut _ as *mut u8,
+ MAXPYBUFLEN as u64,
+ )
+ };
+
+ reset_thread_context()?;
+ match result {
+ MESAPY_ERROR_BUFFER_TOO_SHORT =>
Ok("MESAPY_ERROR_BUFFER_TOO_SHORT".to_string()),
+ MESAPY_EXEC_ERROR => Ok("MESAPY_EXEC_ERROR".to_string()),
+ len => {
+ let r: Vec<u8> = py_result.iter().take(len as
usize).copied().collect();
+ let payload = format!("marshal.loads(b\"\\x{:02X}\")",
r.iter().format("\\x"));
+ Ok(payload)
+ }
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! hashmap {
+ ($( $key: expr => $val: expr ),*) => {{
+ let mut map = ::std::collections::HashMap::new();
+ $( map.insert($key, $val); )*
+ map
+ }}
+}
+
+#[cfg(feature = "enclave_unit_test")]
+pub mod tests {
+ use super::*;
+ use teaclave_test_utils::*;
+
+ use crate::function::TeaclaveFunction;
+ use crate::runtime::RawIoRuntime;
+ use std::path::PathBuf;
+ use std::str::FromStr;
+ use teaclave_types::AesGcm256CryptoInfo;
+ use teaclave_types::TeaclaveFileCryptoInfo;
+ use teaclave_types::TeaclaveFunctionArguments;
+ use teaclave_types::TeaclaveWorkerFileInfo;
+ use teaclave_types::TeaclaveWorkerFileRegistry;
+
+ pub fn run_tests() -> bool {
+ run_tests!(test_mesapy,)
+ }
+
+ fn test_mesapy() {
+ let py_args = hashmap!("--name" => "Teaclave");
+ let py_payload = "
+import sys
+def entrypoint(argv):
+ print argv[0]
+ print argv[1]
+";
+
+ let input = PathBuf::from_str("test_cases/mesapy/input.txt").unwrap();
+ let output =
PathBuf::from_str("test_cases/mesapy/output.txt").unwrap();
+
+ let input_info = TeaclaveWorkerFileInfo {
+ path: input,
+ crypto_info:
TeaclaveFileCryptoInfo::AesGcm256(AesGcm256CryptoInfo::default()),
+ };
+
+ let output_info = TeaclaveWorkerFileInfo {
+ path: output,
+ crypto_info:
TeaclaveFileCryptoInfo::AesGcm256(AesGcm256CryptoInfo::default()),
+ };
+
+ let input_files = TeaclaveWorkerFileRegistry {
+ entries: hashmap!("in_f1".to_string() => input_info),
+ };
+
+ let output_files = TeaclaveWorkerFileRegistry {
+ entries: hashmap!("out_f1".to_string() => output_info),
+ };
+ let runtime = Box::new(RawIoRuntime::new(input_files, output_files));
+
+ let func_args = TeaclaveFunctionArguments {
+ args: hashmap!(
+ "py_payload".to_string() => py_payload.to_string(),
+ "py_args".to_string() =>
serde_json::to_string(&py_args).unwrap()
+ ),
+ };
+
+ let function = Mesapy;
+ let summary = function.execute(runtime, func_args).unwrap();
+ assert_eq!(summary, "marshal.loads(b\"\\x4E\")");
}
}
diff --git a/worker/src/function/mod.rs b/worker/src/function/mod.rs
index 6465ed0..8430b2b 100644
--- a/worker/src/function/mod.rs
+++ b/worker/src/function/mod.rs
@@ -38,6 +38,7 @@ mod gbdt_training;
mod mesapy;
pub use gbdt_training::GbdtTraining;
pub use mesapy::Mesapy;
+mod context;
#[cfg(feature = "enclave_unit_test")]
pub mod tests {
@@ -45,9 +46,10 @@ pub mod tests {
use teaclave_test_utils::*;
pub fn run_tests() -> bool {
- run_tests!(
- gbdt_training::tests::test_gbdt_parse_training_data,
- gbdt_training::tests::test_gbdt_training,
+ check_all_passed!(
+ gbdt_training::tests::run_tests(),
+ mesapy::tests::run_tests(),
+ context::tests::run_tests(),
)
}
}
diff --git a/worker/src/lib.rs b/worker/src/lib.rs
index e8dfa1c..b6ed6db 100644
--- a/worker/src/lib.rs
+++ b/worker/src/lib.rs
@@ -22,6 +22,7 @@ extern crate sgx_tstd as std;
#[cfg(feature = "mesalock_sgx")]
use std::prelude::v1::*;
+#[macro_use]
extern crate log;
mod function;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]