-
Notifications
You must be signed in to change notification settings - Fork 21
Ep 748 simplify pub interface #209
Changes from all commits
8006f7f
e0bfb8f
a33210a
3d07427
bab2194
b359a79
5f7f24b
922686f
371518d
39cc487
dbd3b1f
40cf488
dffcb5b
bfebca7
797cc40
e2a138e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# override global formatting. i want clean and standard rustfmt. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,38 @@ | ||
[package] | ||
name = "eng-wasm-derive" | ||
version = "0.1.6" # upgrade version in eng-wasm dependancy as well (if needed) | ||
authors = ["moria <[email protected]>"] | ||
authors = ["moria <[email protected]>", "Reuven Podmazo <[email protected]>"] | ||
edition = "2018" | ||
license = "AGPL-3.0" | ||
description = "Enigma library for creating Secret Contracts" | ||
keywords = ["wasm", "webassembly", "blockchain", "sgx", "enigma"] | ||
categories = ["wasm"] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
eng-wasm = { version = "0.1.6", path = "../" } | ||
proc-macro2 = "0.4" | ||
quote = "0.6" | ||
syn = { version = "0.15", features = ["full"] } | ||
#syn = { version = "0.15", features = ["full", "extra-traits"] } # for debug purposes | ||
failure = { version = "0.1.5", default-features = false, features = ["derive"] } | ||
proc-macro2 = "1.0" | ||
syn = { version = "1.0", features = ["full"] } | ||
quote = "1.0" | ||
failure = "0.1" | ||
parse-display = "0.1" | ||
ethabi = "6.1" | ||
serde_json = "1.0" | ||
tiny-keccak = "1.4" | ||
|
||
[lib] | ||
proc-macro = true | ||
[dev-dependencies] | ||
syn = { version = "1.0", features = ["full", "extra-traits"] } | ||
eng-wasm = { path = '..' } | ||
|
||
[[example]] | ||
name = 'struct-impl' | ||
crate-type = ["cdylib"] | ||
|
||
[[example]] | ||
name = 'trait-impl' | ||
crate-type = ["cdylib"] | ||
|
||
[[example]] | ||
name = 'trait-impl-rename' | ||
crate-type = ["cdylib"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#![no_std] | ||
|
||
use eng_wasm_derive::pub_interface; | ||
|
||
struct MyContract; | ||
|
||
#[pub_interface] | ||
impl MyContract { | ||
/// constructor | ||
pub fn construct(_x: u32) {} | ||
|
||
/// secret contract method | ||
pub fn expand(input: u32) -> u64 { | ||
Self::expand_impl(input) | ||
} | ||
|
||
/// private method, not exported from contract | ||
fn expand_impl(input: u32) -> u64 { | ||
input as u64 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#![no_std] | ||
|
||
use eng_wasm_derive::pub_interface; | ||
|
||
struct MyContractImplementation; | ||
|
||
#[pub_interface(MyContractImplementation)] | ||
trait MyContract { | ||
/// constructor | ||
fn construct(_x: u32); | ||
|
||
/// secret contract method | ||
fn expand(input: u32) -> u64; | ||
} | ||
|
||
impl MyContract for MyContractImplementation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would maybe add |
||
fn construct(_x: u32) {} | ||
|
||
fn expand(input: u32) -> u64 { | ||
Self::expand_impl(input) | ||
} | ||
} | ||
|
||
impl MyContractImplementation { | ||
/// private method, not exported from contract | ||
fn expand_impl(input: u32) -> u64 { | ||
input as u64 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#![no_std] | ||
|
||
use eng_wasm_derive::pub_interface; | ||
|
||
struct Contract; | ||
|
||
#[pub_interface] | ||
trait MyContract { | ||
/// constructor | ||
fn construct(_x: u32); | ||
|
||
/// secret contract method | ||
fn expand(input: u32) -> u64; | ||
} | ||
|
||
impl MyContract for Contract { | ||
fn construct(_x: u32) {} | ||
|
||
fn expand(input: u32) -> u64 { | ||
Self::expand_impl(input) | ||
} | ||
} | ||
|
||
impl Contract { | ||
/// private method, not exported from contract | ||
fn expand_impl(input: u32) -> u64 { | ||
input as u64 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
use std::fs::File; | ||
moriaab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
use quote::quote; | ||
|
||
use ethabi::{Contract, ParamType}; | ||
|
||
mod errors; | ||
mod ethereum; | ||
|
||
use errors::EngWasmError; | ||
use ethereum::short_signature; | ||
|
||
trait Write { | ||
fn write(&self) -> String; | ||
fn error(&self) -> String; | ||
} | ||
|
||
impl Write for ParamType { | ||
/// Returns string which is a formatted representation of param. | ||
fn write(&self) -> String { | ||
match *self { | ||
ParamType::Address => "Address".to_owned(), | ||
ParamType::Bytes => "Vec<u8>".to_owned(), | ||
ParamType::FixedBytes(len) => format!("u8[{}]", len), | ||
ParamType::Int(len) => match len { | ||
32 | 64 => format!("i{}", len), | ||
_ => panic!("{}", self.error()), | ||
}, | ||
ParamType::Uint(len) => match len { | ||
32 | 64 => format!("u{}", len), | ||
256 => "U256".to_owned(), | ||
_ => panic!("{}", self.error()), | ||
}, | ||
ParamType::Bool => "bool".to_owned(), | ||
ParamType::String => "String".to_owned(), | ||
ParamType::FixedArray(ref param, len) => format!("{}[{}]", param.write(), len), | ||
ParamType::Array(ref param) => format!("Vec<{}>", param.write()), | ||
} | ||
} | ||
fn error(&self) -> String { | ||
format!("The type {} is not supported", self.to_string()) | ||
} | ||
} | ||
|
||
struct FunctionAst { | ||
name: syn::Ident, | ||
args_ast_types: Vec<syn::Type>, | ||
args_types: Vec<ParamType>, | ||
} | ||
|
||
fn read_contract_file(file_path: String) -> Result<Box<File>, EngWasmError> { | ||
let file = File::open(file_path)?; | ||
let contents = Box::new(file); | ||
Ok(contents) | ||
} | ||
|
||
fn generate_eth_functions( | ||
contract: &Contract, | ||
) -> Result<Vec<proc_macro2::TokenStream>, EngWasmError> { | ||
let mut functions: Vec<FunctionAst> = Vec::new(); | ||
for function in &contract.functions { | ||
let mut args_ast_types = Vec::new(); | ||
for input in &function.1.inputs { | ||
let arg_type: syn::Type = syn::parse_str(&input.kind.clone().write())?; | ||
args_ast_types.push(arg_type); | ||
} | ||
let args_types = function | ||
.1 | ||
.inputs | ||
.iter() | ||
.map(|input| input.kind.clone()) | ||
.collect(); | ||
|
||
let name = syn::Ident::new(&function.1.name, proc_macro2::Span::call_site()); | ||
functions.push(FunctionAst { | ||
name, | ||
args_types, | ||
args_ast_types, | ||
}) | ||
} | ||
|
||
let result: Vec<proc_macro2::TokenStream> = functions | ||
.iter() | ||
.map(|function| { | ||
let function_name = &function.name; | ||
let args_ast_types = function.args_ast_types.clone(); | ||
let sig_u32 = short_signature(&function_name.to_string(), &function.args_types); | ||
let sig = syn::Lit::Int(syn::LitInt::new( | ||
&format!("{}_u32", sig_u32 as u32), | ||
proc_macro2::Span::call_site(), | ||
)); | ||
let args_number = syn::Lit::Int(syn::LitInt::new( | ||
&format!("{}_usize", args_ast_types.len() as usize), | ||
proc_macro2::Span::call_site(), | ||
)); | ||
let args_names: Vec<syn::Ident> = function | ||
.args_ast_types | ||
.iter() | ||
.enumerate() | ||
.map(|item| { | ||
let mut arg = String::from("arg"); | ||
arg.push_str(item.0.to_string().as_str()); | ||
syn::Ident::new(&arg, proc_macro2::Span::call_site()) | ||
}) | ||
.collect(); | ||
let args_names_copy = args_names.clone(); | ||
quote! { | ||
fn #function_name(&self, #(#args_names: #args_ast_types),*){ | ||
#![allow(unused_mut)] | ||
#![allow(unused_variables)] | ||
let mut payload = Vec::with_capacity(4 + #args_number * 32); | ||
payload.push((#sig >> 24) as u8); | ||
payload.push((#sig >> 16) as u8); | ||
payload.push((#sig >> 8) as u8); | ||
payload.push(#sig as u8); | ||
|
||
let mut sink = eng_pwasm_abi::eth::Sink::new(#args_number); | ||
#(sink.push(#args_names_copy);)* | ||
sink.drain_to(&mut payload); | ||
write_ethereum_bridge(&payload, &self.addr); | ||
} | ||
} | ||
}) | ||
.collect(); | ||
|
||
Ok(result) | ||
} | ||
|
||
pub fn impl_eth_contract( | ||
args: proc_macro2::TokenStream, | ||
input: proc_macro2::TokenStream, | ||
) -> proc_macro2::TokenStream { | ||
let input_tokens = parse_macro_input2!(input as syn::ItemStruct); | ||
let struct_name = input_tokens.ident; | ||
let file_path = parse_macro_input2!(args as syn::LitStr); | ||
let contents: Box<File> = read_contract_file(file_path.value()).expect("Bad contract file"); | ||
let contract = Contract::load(contents).unwrap(); | ||
let it: Vec<proc_macro2::TokenStream> = generate_eth_functions(&contract).unwrap(); | ||
|
||
quote! { | ||
struct #struct_name { | ||
addr: Address, | ||
} | ||
impl EthContract{ | ||
fn new(addr_str: /*Address*/&str) -> Self { | ||
use core::str::FromStr; | ||
|
||
// Ethereum Addresses need to start with `0x` so we remove the first two characters | ||
let addr = Address::from_str(&addr_str[2..]).expect("Failed converting the address from hex"); | ||
EthContract{ addr } | ||
} | ||
#(#it)* | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,37 @@ | ||
use failure::Fail; | ||
use std::string::ToString; | ||
use std::io; | ||
use std::{io, string::ToString}; | ||
use syn; | ||
|
||
#[derive(Debug, Fail)] | ||
pub enum EngWasmError { | ||
#[fail(display = "I/O error: {:?}", error)] | ||
IoError{ | ||
error: String, | ||
}, | ||
IoError { error: String }, | ||
#[fail(display = "Json error: {}", error)] | ||
JsonError { | ||
error: String, | ||
}, | ||
JsonError { error: String }, | ||
#[fail(display = "Token parse error: {}", error)] | ||
TokenParseError { | ||
error: String, | ||
} | ||
TokenParseError { error: String }, | ||
} | ||
|
||
|
||
impl From<io::Error> for EngWasmError { | ||
fn from(error: io::Error) -> Self { | ||
match error { | ||
_ => EngWasmError::IoError {error: error.to_string()}, | ||
EngWasmError::IoError { | ||
error: error.to_string(), | ||
} | ||
} | ||
} | ||
|
||
|
||
impl From<serde_json::Error> for EngWasmError { | ||
fn from(err: serde_json::Error) -> Self { | ||
match err { | ||
_ => EngWasmError::JsonError {error: err.to_string()}, | ||
EngWasmError::JsonError { | ||
error: err.to_string(), | ||
} | ||
} | ||
} | ||
|
||
impl From<syn::parse::Error> for EngWasmError { | ||
fn from (err: syn::parse::Error) -> Self { | ||
match err { | ||
_ => EngWasmError::TokenParseError{error: err.to_string()} | ||
fn from(err: syn::parse::Error) -> Self { | ||
EngWasmError::TokenParseError { | ||
error: err.to_string(), | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
pub trait IntoIdent { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fairly self-explanatory, but if not i can add a docstring. i'm surprised this is not implemented upstream, so we can file a pull request with this to https://github.com/dtolnay/syn There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice addition! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realised this is probably why it isn't made to be as easy to use as i made it: |
||
fn into_ident(self) -> syn::Ident; | ||
} | ||
|
||
impl IntoIdent for &str { | ||
fn into_ident(self) -> syn::Ident { | ||
quote::format_ident!("{}", self) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use the
extra-traits
feature for printing and comparisons of AST objects