Skip to content
This repository was archived by the owner on Jun 21, 2020. It is now read-only.

Commit ef068a1

Browse files
committed
rewritten eng_wasm_derive::pub_interface
* Enabled usage of the \#[pub_interface] annotation on `impl Struct` items as well as on trait items. When using the trait version the name of the struct/enum implementing the methods was (and still is) hardcoded as `Contract`. When using the new form, the user may use whatever name they want for the struct. It has no side-effects. (They can theoretically use any type they want now) * Added the following diagnostics to the macro invocation: (when any of these is detected, the user will see a useful error message) * The attribute was placed on an item which is not a trait or inherent impl * The trait or impl methods use the `self` receiver * Trait methods provided a default implementation (i understand that we wanted the users to define the implementations on the `Contract` struct) * Trait method with any additional attribute * Impl method with a \#[no_mangle] attribute * Impl method with visibility which isn't private or `pub` (we don't want them defining `pub(crate)` or `pub(in something)` because that can be misleading in this context) * Fixed issue around parsing return values from contract methods where their amount was incorrectly calculated * When parsing of method inputs fails at runtime, the panic message now mentions the type and argument that caused the failure. * If an unknown method is called, the panic message mentions the name of that method. * Using the contract no longer requires writing `use eng_wasm::*;` in the using module * restructured the module layout to make it easier to follow. * added unittests * documented a lot of things The ERC20 example now generates the following code: https://gist.github.com/rust-play/d9e375f7a587725be238ced968630d28
1 parent 33cebc4 commit ef068a1

File tree

11 files changed

+1329
-335
lines changed

11 files changed

+1329
-335
lines changed

eng-wasm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ categories = ["wasm"]
1111
[dependencies]
1212
serde_json = "1.0"
1313
serde = { version = "1.0", default-features = false }
14-
eng-pwasm-abi = "0.3"
14+
eng-pwasm-abi = "0.3"

eng-wasm/derive/.rustfmt.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# override global formatting. i want clean and standard runstfmt.

eng-wasm/derive/Cargo.toml

100755100644
Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
[package]
22
name = "eng-wasm-derive"
33
version = "0.1.3"
4-
authors = ["moria <[email protected]>"]
4+
authors = ["moria <[email protected]>", "Reuven Podmazo <[email protected]>"]
5+
edition = "2018"
56
license = "AGPL-3.0"
67
description = "Enigma library for creating Secret Contracts"
78
keywords = ["wasm", "webassembly", "blockchain", "sgx", "enigma"]
89
categories = ["wasm"]
910

11+
[lib]
12+
proc-macro = true
13+
1014
[dependencies]
11-
eng-wasm = { version = "0.1.3", path = "../" }
12-
proc-macro2 = "0.4"
13-
quote = "0.6"
14-
syn = { version = "0.15", features = ["full"] }
15-
#syn = { version = "0.15", features = ["full", "extra-traits"] } # for debug purposes
16-
failure = { version = "0.1.5", default-features = false, features = ["derive"] }
15+
proc-macro2 = "1.0"
16+
syn = { version = "1.0", features = ["full"] }
17+
quote = "1.0"
18+
failure = "0.1"
1719
ethabi = "6.1"
1820
serde_json = "1.0"
1921
tiny-keccak = "1.4"
2022

21-
[lib]
22-
proc-macro = true
23+
[dev-dependencies]
24+
syn = { version = "1.0", features = ["full", "extra-traits"] }

eng-wasm/derive/src/eth_contract.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use std::fs::File;
2+
3+
use quote::quote;
4+
5+
use ethabi::{Contract, ParamType};
6+
7+
mod errors;
8+
mod ethereum;
9+
10+
use errors::EngWasmError;
11+
use ethereum::short_signature;
12+
13+
trait Write {
14+
fn write(&self) -> String;
15+
fn error(&self) -> String;
16+
}
17+
18+
impl Write for ParamType {
19+
/// Returns string which is a formatted representation of param.
20+
fn write(&self) -> String {
21+
match *self {
22+
ParamType::Address => "Address".to_owned(),
23+
ParamType::Bytes => "Vec<u8>".to_owned(),
24+
ParamType::FixedBytes(len) => format!("u8[{}]", len),
25+
ParamType::Int(len) => match len {
26+
32 | 64 => format!("i{}", len),
27+
_ => panic!("{}", self.error()),
28+
},
29+
ParamType::Uint(len) => match len {
30+
32 | 64 => format!("u{}", len),
31+
256 => "U256".to_owned(),
32+
_ => panic!("{}", self.error()),
33+
},
34+
ParamType::Bool => "bool".to_owned(),
35+
ParamType::String => "String".to_owned(),
36+
ParamType::FixedArray(ref param, len) => format!("{}[{}]", param.write(), len),
37+
ParamType::Array(ref param) => format!("Vec<{}>", param.write()),
38+
}
39+
}
40+
fn error(&self) -> String {
41+
format!("The type {} is not supported", self.to_string())
42+
}
43+
}
44+
45+
struct FunctionAst {
46+
name: syn::Ident,
47+
args_ast_types: Vec<syn::Type>,
48+
args_types: Vec<ParamType>,
49+
}
50+
51+
fn read_contract_file(file_path: String) -> Result<Box<File>, EngWasmError> {
52+
let file = File::open(file_path)?;
53+
let contents = Box::new(file);
54+
Ok(contents)
55+
}
56+
57+
fn generate_eth_functions(
58+
contract: &Contract,
59+
) -> Result<Vec<proc_macro2::TokenStream>, EngWasmError> {
60+
let mut functions: Vec<FunctionAst> = Vec::new();
61+
for function in &contract.functions {
62+
let mut args_ast_types = Vec::new();
63+
for input in &function.1.inputs {
64+
let arg_type: syn::Type = syn::parse_str(&input.kind.clone().write())?;
65+
args_ast_types.push(arg_type);
66+
}
67+
let args_types = function
68+
.1
69+
.inputs
70+
.iter()
71+
.map(|input| input.kind.clone())
72+
.collect();
73+
74+
let name = syn::Ident::new(&function.1.name, proc_macro2::Span::call_site());
75+
functions.push(FunctionAst {
76+
name,
77+
args_types,
78+
args_ast_types,
79+
})
80+
}
81+
82+
let result: Vec<proc_macro2::TokenStream> = functions
83+
.iter()
84+
.map(|function| {
85+
let function_name = &function.name;
86+
let args_ast_types = function.args_ast_types.clone();
87+
let sig_u32 = short_signature(&function_name.to_string(), &function.args_types);
88+
let sig = syn::Lit::Int(syn::LitInt::new(
89+
&format!("{}_u32", sig_u32 as u32),
90+
proc_macro2::Span::call_site(),
91+
));
92+
let args_number = syn::Lit::Int(syn::LitInt::new(
93+
&format!("{}_usize", args_ast_types.len() as usize),
94+
proc_macro2::Span::call_site(),
95+
));
96+
let args_names: Vec<syn::Ident> = function
97+
.args_ast_types
98+
.iter()
99+
.enumerate()
100+
.map(|item| {
101+
let mut arg = String::from("arg");
102+
arg.push_str(item.0.to_string().as_str());
103+
syn::Ident::new(&arg, proc_macro2::Span::call_site())
104+
})
105+
.collect();
106+
let args_names_copy = args_names.clone();
107+
quote! {
108+
fn #function_name(&self, #(#args_names: #args_ast_types),*){
109+
#![allow(unused_mut)]
110+
#![allow(unused_variables)]
111+
let mut payload = Vec::with_capacity(4 + #args_number * 32);
112+
payload.push((#sig >> 24) as u8);
113+
payload.push((#sig >> 16) as u8);
114+
payload.push((#sig >> 8) as u8);
115+
payload.push(#sig as u8);
116+
117+
let mut sink = eng_pwasm_abi::eth::Sink::new(#args_number);
118+
#(sink.push(#args_names_copy);)*
119+
sink.drain_to(&mut payload);
120+
write_ethereum_bridge(&payload, &self.addr);
121+
}
122+
}
123+
})
124+
.collect();
125+
126+
Ok(result)
127+
}
128+
129+
pub fn impl_eth_contract(
130+
args: proc_macro2::TokenStream,
131+
input: proc_macro2::TokenStream,
132+
) -> proc_macro2::TokenStream {
133+
let input_tokens = parse_macro_input2!(input as syn::ItemStruct);
134+
let struct_name = input_tokens.ident;
135+
let file_path = parse_macro_input2!(args as syn::LitStr);
136+
let contents: Box<File> = read_contract_file(file_path.value()).expect("Bad contract file");
137+
let contract = Contract::load(contents).unwrap();
138+
let it: Vec<proc_macro2::TokenStream> = generate_eth_functions(&contract).unwrap();
139+
140+
quote! {
141+
struct #struct_name {
142+
addr: Address,
143+
}
144+
impl EthContract{
145+
fn new(addr_str: /*Address*/&str) -> Self {
146+
use core::str::FromStr;
147+
// Ethereum Addresses need to start with `0x` so we remove the first two characters
148+
let addr = Address::from_str(&addr_str[2..]).expect("Failed converting the address from hex");
149+
EthContract{ addr }
150+
}
151+
#(#it)*
152+
}
153+
}
154+
}

eng-wasm/derive/src/errors.rs renamed to eng-wasm/derive/src/eth_contract/errors.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@ pub enum EngWasmError {
1414

1515
impl From<io::Error> for EngWasmError {
1616
fn from(error: io::Error) -> Self {
17-
match error {
18-
_ => EngWasmError::IoError { error: error.to_string() },
17+
EngWasmError::IoError {
18+
error: error.to_string(),
1919
}
2020
}
2121
}
2222

2323
impl From<serde_json::Error> for EngWasmError {
2424
fn from(err: serde_json::Error) -> Self {
25-
match err {
26-
_ => EngWasmError::JsonError { error: err.to_string() },
25+
EngWasmError::JsonError {
26+
error: err.to_string(),
2727
}
2828
}
2929
}
3030

3131
impl From<syn::parse::Error> for EngWasmError {
3232
fn from(err: syn::parse::Error) -> Self {
33-
match err {
34-
_ => EngWasmError::TokenParseError { error: err.to_string() },
33+
EngWasmError::TokenParseError {
34+
error: err.to_string(),
3535
}
3636
}
3737
}

eng-wasm/derive/src/ethereum.rs renamed to eng-wasm/derive/src/eth_contract/ethereum.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ pub fn short_signature(name: &str, params: &[ParamType]) -> u32 /*[u8; 4] */ {
99
}
1010

1111
fn fill_signature(name: &str, params: &[ParamType], result: &mut [u8]) {
12-
let types = params.iter().map(Writer::write).collect::<Vec<String>>().join(",");
12+
let types = params
13+
.iter()
14+
.map(Writer::write)
15+
.collect::<Vec<String>>()
16+
.join(",");
1317

1418
let data: Vec<u8> = From::from(format!("{}({})", name, types).as_str());
1519

eng-wasm/derive/src/into_ident.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub trait IntoIdent {
2+
fn into_ident(self) -> syn::Ident;
3+
}
4+
5+
impl IntoIdent for &str {
6+
fn into_ident(self) -> syn::Ident {
7+
quote::format_ident!("{}", self)
8+
}
9+
}

0 commit comments

Comments
 (0)