mazzaroth_rs_derive/
lib.rs

1//! # Mazzaroth Derive Library
2//!
3//! The Mazzaroth Derive Library is a rust library that defines the macros
4//! used to compile Mazzaroth Smart Contracts and generate the JSON ABI.
5//!
6//! ## How to use
7//!
8//! The first step to using this library is to include the necessary dependencies.
9//! The following 3 dependencies should be included in your Cargo.toml:
10//!
11//! mazzaroth-rs
12//! mazzaroth-rs-derive
13//! mazzaroth-xdr
14//!
15//! Every contract will have a similar base layout for the main function and the contract trait definition.
16//! `main()` is used as the entry point and has several important features.  It will instantiate the contract,
17//! call a host function to retrieve function input, execute the function, and return a response.
18//!
19//! Here is a basic Hello World contract example:
20//! ```ignore
21//! // must include the ContractInterface and mazzaroth_abi for compiling the macro
22//! extern crate mazzaroth_rs;
23//! extern crate mazzaroth_rs_derive;
24//! use mazzaroth_rs::ContractInterface;
25//! use mazzaroth_rs_derive::mazzaroth_abi;
26//!
27//! // using specific external host modules
28//! use mazzaroth_rs::external::{transaction, log};
29//!
30//! #[no_mangle]
31//! pub fn main() {
32//!     // panic hook is set to call the host error log function when a panic occurs
33//!     std::panic::set_hook(Box::new(mazzaroth_rs::external::errors::hook));
34//!
35//!     // Creates a new instance of the ABI generated around the Contract
36//!     let mut contract = HelloWorld::new(Hello {});
37//!
38//!     // Use a host function to get arguments
39//!     let args = transaction::arguments();
40//!
41//!     // Execute calls one of the functions defined in the contract
42//!     // Input for the function to call and it's params comes from the Runtime
43//!     let response = contract.execute(&args).unwrap();
44//!
45//!     // Provide return value through host call
46//!     transaction::ret(response);
47//! }
48//!
49//! // mazzaroth_abi used to generate the contract from the trait during compilation
50//! #[mazzaroth_abi(HelloWorld)]
51//! pub trait HelloWorldContract {
52//!     // hello() defined as a readonly function
53//!     #[readonly]
54//!     fn hello(&mut self) -> u32;
55//! }
56//!
57//! // Struct used to implement the contract trait
58//! pub struct Hello {}
59//!
60//! // Actual contract implementation
61//! impl HelloWorldContract for Hello {
62//!     fn hello(&mut self) -> u32 {
63//!         log("Hello World!".to_string());
64//!         14
65//!     }
66//! }
67//! ```
68#![recursion_limit = "256"]
69
70extern crate proc_macro;
71extern crate proc_macro2;
72#[macro_use]
73extern crate syn;
74#[macro_use]
75extern crate quote;
76
77extern crate mazzaroth_xdr;
78extern crate xdr_rs_serialize;
79
80use proc_macro::TokenStream;
81use proc_macro2::Span;
82
83mod contract;
84use contract::{Contract, TraitItem};
85
86mod error;
87use error::{ProcError, Result};
88
89mod json;
90use json::write_json_abi;
91
92/// Macro used to mark the trait that defines the mazzaroth contract
93///
94/// The argument becomes the module name used to construct the contract in main.
95///
96/// Example:
97/// ```ignore
98/// #[mazzaroth_abi(HelloWorld)]
99/// pub trait HelloWorldContract {
100///     
101/// }
102/// ```
103#[proc_macro_attribute]
104pub fn mazzaroth_abi(args: TokenStream, input: TokenStream) -> TokenStream {
105    let args_toks = parse_macro_input!(args as syn::AttributeArgs);
106    let input_toks = parse_macro_input!(input as syn::Item);
107
108    let output = match impl_mazzaroth_abi(args_toks, input_toks) {
109        Ok(output) => output,
110        Err(err) => panic!("mazzaroth_abi encountered error: {}", err),
111    };
112
113    output.into()
114}
115
116fn impl_mazzaroth_abi(
117    args: syn::AttributeArgs,
118    input: syn::Item,
119) -> Result<proc_macro2::TokenStream> {
120    // Get the name for the generated Contract from the Arg
121    if args.len() == 0 || args.len() > 1 {
122        return Err(ProcError::invalid_arguments(args.len()));
123    }
124
125    // Get the contract name passed as an argument to the mazzaroth_abi macro
126    let argument_name = if let syn::NestedMeta::Meta(syn::Meta::Word(ident)) = args.get(0).unwrap()
127    {
128        Ok(ident.to_string())
129    } else {
130        Err(ProcError::malformed_argument())
131    }?;
132    let argument_ident = syn::Ident::new(&argument_name, Span::call_site());
133
134    let contract = Contract::from_item(input);
135
136    // Write out a json abi for the functions available
137    write_json_abi(&contract)?;
138
139    // Mod that is created around contract trait
140    let mod_name = format!("mazzaroth_abi_impl_{}", &contract.name().clone());
141    let mod_name_ident = syn::Ident::new(&mod_name, Span::call_site());
142
143    // Tokenize the contract which will have a single entry
144    // to call the contract functions
145    let contract_toks = tokenize_contract(&argument_name, &contract);
146
147    // Note: Imports are included in the generated module here
148    // So if types are added that can be used as function params or returns, they must be included.
149    let result = quote! {
150        #contract
151        mod #mod_name_ident {
152            extern crate mazzaroth_rs;
153            extern crate mazzaroth_xdr;
154            use super::*; // Provide access to the user contract
155            #contract_toks
156        }
157        pub use self::#mod_name_ident::#argument_ident;
158
159    };
160    Ok(result)
161}
162
163// Tokenize contract to an implementation with a callable execute function
164fn tokenize_contract(name: &str, contract: &Contract) -> proc_macro2::TokenStream {
165    // Loop through the trait items of the contract and for Functions build a
166    // quote map of function name to a function wrapper that gets arguments from encoded bytes
167    // and returns bytes. Also includes Readonly functions in contract.
168    let functions: Vec<proc_macro2::TokenStream> = contract.trait_items().iter().filter_map(|item| {
169		match *item {
170			TraitItem::Function(ref function) => {
171				let function_ident = &function.name;
172
173                // Create a matchname string literal that matches name of function
174                let match_name = syn::Lit::Str(syn::LitStr::new(&function_ident.to_string(), Span::call_site()));
175
176                let arg_types = function.arguments.iter().map(|&(_, ref ty)| quote! { #ty });
177                let arg_types2 = function.arguments.iter().map(|&(_, ref ty)| quote! { #ty });
178                let ret_type = function.ret_types.iter().map(|ref ty| quote! {#ty}).next();
179
180                if function.ret_types.is_empty() {
181                    Some(quote! {
182                        #match_name => {
183                            inner.#function_ident(
184                                #(decoder.pop::<#arg_types>(stringify!(#arg_types2))?),*
185                            );
186                            Ok(Vec::new())
187                        }
188                    })
189                } else {
190                    Some(quote! {
191                        #match_name => {
192                            let result = inner.#function_ident(
193                                #(decoder.pop::<#arg_types>(stringify!(#arg_types2))?),*
194                            );
195                            let mut encoder = mazzaroth_rs::Encoder::default();
196                            encoder.push(result, stringify!(#ret_type));
197                            Ok(encoder.values())
198                        }
199                    })
200                }
201			},
202            TraitItem::Readonly(ref function) => {
203				let function_ident = &function.name;
204
205                // Create a matchname string literal that matches name of function
206                let match_name = syn::Lit::Str(syn::LitStr::new(&function_ident.to_string(), Span::call_site()));
207
208                let arg_types = function.arguments.iter().map(|&(_, ref ty)| quote! { #ty });
209                let arg_types2 = function.arguments.iter().map(|&(_, ref ty)| quote! { #ty });
210                let ret_type = function.ret_types.iter().map(|ref ty| quote! {#ty}).next();
211
212                if function.ret_types.is_empty() {
213                    Some(quote! {
214                        #match_name => {
215                            inner.#function_ident(
216                                #(decoder.pop::<#arg_types>(stringify!(#arg_types2))?),*
217                            );
218                            Ok(Vec::new())
219                        }
220                    })
221                } else {
222                    Some(quote! {
223                        #match_name => {
224                            let result = inner.#function_ident(
225                                #(decoder.pop::<#arg_types>(stringify!(#arg_types2))?),*
226                            );
227                            let mut encoder = mazzaroth_rs::Encoder::default();
228                            encoder.push(result, stringify!(#ret_type));
229                            Ok(encoder.values())
230                        }
231                    })
232                }
233			},
234			_ => None,
235		}
236	}).collect();
237
238    let endpoint_ident = syn::Ident::new(name, Span::call_site());
239    let name_ident = syn::Ident::new(&contract.name(), Span::call_site());
240
241    quote! {
242        pub struct #endpoint_ident<T: #name_ident> {
243            pub inner: T,
244        }
245
246        impl<T: #name_ident> From<T> for #endpoint_ident<T> {
247            fn from(inner: T) -> #endpoint_ident<T> {
248                #endpoint_ident {
249                    inner: inner,
250                }
251            }
252        }
253
254        impl<T: #name_ident> #endpoint_ident<T> {
255            pub fn new(inner: T) -> Self {
256                #endpoint_ident {
257                    inner: inner,
258                }
259            }
260
261            pub fn instance(&self) -> &T {
262                &self.inner
263            }
264        }
265
266        impl<T: #name_ident> mazzaroth_rs::ContractInterface for #endpoint_ident<T> {
267            #[allow(unused_mut)]
268            #[allow(unused_variables)]
269            fn execute(&mut self, payload: &[u8]) -> Result<Vec<u8>, mazzaroth_rs::ContractError> {
270                let inner = &mut self.inner;
271
272                // first decode the call from stream
273                let mut payload_decoder = mazzaroth_rs::Decoder::new(payload);
274                match payload_decoder.pop::<mazzaroth_xdr::Call>() {
275                    Ok(call) => {
276                         // Then create a decoder for arguments
277                        let mut decoder = mazzaroth_rs::InputDecoder::new(&call.arguments);
278
279                        match call.function.as_str() {
280                            #(#functions,)*
281                            _ => Err(mazzaroth_rs::ContractError::invalid_function()),
282                        }
283                    },
284                    _ => Err(mazzaroth_rs::ContractError::invalid_function())
285                }
286            }
287        }
288    }
289}