1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
extern crate alloc;
extern crate proc_macro;

use alloc::string::String;

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{self, FnArg, Ident};

mod key {
    pub const __U64: &str = "u64";
    pub const __U512: &str = "U512";
    pub const __STRING: &str = "String";
}

// The main macro that sets up the deploy function for the contract
// It loads the module of a given name and then iterates over the content of the
// module
#[proc_macro_attribute]
pub fn casperlabs_contract(_attr: TokenStream, input: TokenStream) -> TokenStream {
    let item: syn::ItemMod = syn::parse_macro_input!(input);
    let name = &item.ident;
    let mut deploy_args = proc_macro2::TokenStream::new();
    let mut deploy_def = proc_macro2::TokenStream::new();
    let mut func_def = proc_macro2::TokenStream::new();
    match item.content {
        Some(module_content) => {
            let (deploy_def_content, func_def_content, deploy_args_content) =
                get_entry_points(name, module_content.1).unwrap();
            deploy_def.extend(deploy_def_content);
            func_def.extend(func_def_content);
            deploy_args.extend(deploy_args_content);
        }
        None => println!("Empty"),
    };
    let gen = quote! {
        fn __deploy( #deploy_args ) {
            #deploy_def
        }
        #func_def
        fn ret<T: CLTyped + ToBytes>(value: T) {
            runtime::ret(CLValue::from_t(value).unwrap_or_revert())
        }
    };
    // Return the deploy function along with the call function
    gen.into()
}

#[proc_macro_attribute]
pub fn casperlabs_initiator(_attr: TokenStream, input: TokenStream) -> TokenStream {
    // An optional macro that loads the initating function for the contract which returns the BTreeMap
    // The macro itself does not return the BTreeMap
    let item: syn::ItemFn = syn::parse_macro_input!(input);
    let func_def = quote! { #item };
    let gen = quote! {
        #func_def
    };
    // Return the function defintion for the intiating function
    gen.into()
}

#[proc_macro_attribute]
pub fn casperlabs_constructor(_attr: TokenStream, input: TokenStream) -> TokenStream {
    // The macro responsible for generating the constructor
    let mut item: syn::ItemFn = syn::parse_macro_input!(input);
    let mut input_strings: Vec<String> = Vec::new();
    let mut declaration = proc_macro2::TokenStream::new();
    let orignal_ident = item.sig.ident.clone();
    let name = &orignal_ident;
    let new_ident = Ident::new(&format!("__{}", name), name.span());
    item.sig.ident = new_ident;
    let internal = &item.sig.ident;
    let constructor_def = quote! { #item };
    if item.sig.inputs.is_empty() {
        let gen = quote! {
            #[no_mangle]
            fn call() {
                __deploy();
            }
            #constructor_def
            #[no_mangle]
            fn #name() {
                #internal()
            }
        };
        return gen.into();
    }
    for indiviual_input in item.sig.inputs {
        let (dec, arg, _, _) = get_var_declaration(&indiviual_input);
        declaration.extend(dec);
        input_strings.push(arg);
    }
    let input_args = prep_input(input_strings);
    let gen = quote! {
        #[no_mangle]
        fn call() {
            #declaration
            __deploy(#input_args)
        }
        #constructor_def
        #[no_mangle]
        fn #name() {
            #declaration
            #internal(#input_args)
        }
    };
    // Return the function defintion for the constructor function
    gen.into()
}

#[proc_macro_attribute]
pub fn casperlabs_method(_attr: TokenStream, input: TokenStream) -> TokenStream {
    let mut item: syn::ItemFn = syn::parse_macro_input!(input);
    let orignal_ident = item.sig.ident.clone();
    let return_type = item.sig.output.clone();
    let mut return_code = proc_macro2::TokenStream::new();
    let mut return_func = proc_macro2::TokenStream::new();
    if let syn::ReturnType::Type(_arrow, rt) = return_type {
        if let syn::Type::Path(path) = *rt {
            let return_ident = &path.path.segments[0].ident;
            let code = quote! { let val: #return_ident = };
            return_code.extend(code);
        }
    }
    if !return_code.is_empty() {
        let runtime_return = quote! { ret(val) };
        return_func.extend(runtime_return)
    }
    let name = &orignal_ident;
    let new_ident = Ident::new(&format!("__{}", name), name.span());
    item.sig.ident = new_ident;
    let internal = &item.sig.ident;
    let func_def = quote! { #item };
    let mut declaration = proc_macro2::TokenStream::new();
    let mut input_strings: Vec<String> = Vec::new();
    if item.sig.inputs.is_empty() {
        let gen = quote! {
            #func_def
            #[no_mangle]
            fn #name() {
                #declaration
                #return_code #internal();
                #return_func
            }
        };
        return gen.into();
    }
    for indiviual_input in item.sig.inputs {
        let (dec, arg, _, _) = get_var_declaration(&indiviual_input);
        declaration.extend(dec);
        input_strings.push(arg);
    }
    let input_args = prep_input(input_strings);
    let gen = quote! {
        #func_def
        #[no_mangle]
        fn #name()  {
            #declaration
            #return_code #internal(#input_args);
            #return_func
        }
    };
    // Return both the entry point and the function defintion
    // #[no_mangle]
    // fn bar() {
    //     __bar()
    // }
    //  fn __bar() {}
    gen.into()
}

// Constructs a new entry point that can be inserted into
// the deploy function.
fn get_entry_points(
    name: &syn::Ident,
    funcs: Vec<syn::Item>,
) -> Option<(
    proc_macro2::TokenStream,
    proc_macro2::TokenStream,
    proc_macro2::TokenStream,
)> {
    // The initial empty defintion Tokenstream that will be incrementally added to with various parts of the deploy function
    let mut definitions: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
    // The start of the deploy function, which is mostly static and generic for any standard casperlabs contract
    let mut init = quote! {
        let (contract_package_hash, _) = storage::create_contract_package_at_hash();
        let _constructor_access_uref: URef = storage::create_contract_user_group(
            contract_package_hash,
            "constructor",
            1,
            BTreeSet::new(),
        )
        .unwrap_or_revert()
        .pop()
        .unwrap_or_revert();
        let constructor_group = Group::new("constructor");
        let mut entry_points = EntryPoints::new();
    };
    // The two empty streams for arguments that must be passed to the deploy function and the content of the contract call respectively
    let mut deploy_args: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
    let mut contract_call: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
    // The constructor function has a different EntryAccessPoint as compared to regular function
    // We must thus create an identifier to see which of the annotated function is the constructor
    let constructor_ident = Ident::new("casperlabs_constructor", Span::call_site());
    // The intiating function must also be idenitfied so that the function can be placed in the deploy function
    let member_ident = Ident::new("casperlabs_initiator", Span::call_site());
    let mut init_ident: proc_macro2::TokenStream = quote! { Default::default() };
    // Empty token stream that will change depending on the type of function passed to it (constructor/method)
    let mut access_token: proc_macro2::TokenStream;
    // Loop over every possible function and match to see if it is indeed a function, the statment could be a literal like
    //  'use::super::*;'
    //  For every #[casperlabs_methods] in the contract module
    let mut constructor_presence: bool = false;
    for func in funcs {
        if let syn::Item::Fn(func_body) = func {
            // If the function is not a constructor or method it will have no attributes and we must ignore that function
            // Additionally, the function could also be an intiatior, which it means it should be ignored as well.
            if func_body.attrs.is_empty() {
                let gen = quote! { #func_body };
                definitions.extend(gen);
            } else if !func_body.attrs.is_empty()
                && member_ident != func_body.attrs[0].path.segments[0].ident
            {
                //  Identify if the function is a constructor or a regular method
                if constructor_ident == func_body.attrs[0].path.segments[0].ident {
                    constructor_presence = true;
                    access_token = quote! { EntryPointAccess::Groups(vec![constructor_group]) };
                    let (call, __d_args) = get_call(&func_body);
                    contract_call.extend(call);
                    deploy_args.extend(__d_args);
                } else {
                    //  EntryAccessPoint for a regular casperlabs_method
                    access_token = quote! { EntryPointAccess::Public };
                }
                let name = &func_body.sig.ident;
                let def = quote! { #func_body };
                definitions.extend(def);
                let string_name = format!("{}", name);
                let mut arg: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
                if !func_body.sig.inputs.is_empty() {
                    for input in func_body.sig.inputs {
                        arg.extend(get_param(input));
                    }
                }
                let params = quote! {
                    vec![
                    #arg
                    ]
                };
                // Setup each entry point for the casperlabs_method or one single entry point
                // for the constructor
                let gen = quote! {
                    entry_points.add_entry_point(EntryPoint::new(
                        String::from(#string_name),
                        #params,
                        CLType::Unit,
                        #access_token,
                        EntryPointType::Contract,
                    ));
                };
                init.extend(gen)
            } else if member_ident == func_body.attrs[0].path.segments[0].ident
                && !func_body.attrs.is_empty()
            {
                // If it is the intitator function, then set the identifier with the name of the specified function
                // Return the function definition
                let init_name = func_body.sig.ident.clone();
                let def = quote! { #func_body };
                definitions.extend(def);
                init_ident = quote! { #init_name() };
            } else if func_body.attrs.is_empty() {
                // Final path to ensure all non-associated functions are included within the contract
                let def = quote! { #func_body };
                definitions.extend(def);
            }
        }
        // Simply pass over all other literals
    }
    if !constructor_presence {
        panic!("No constructor was present");
    }
    let string_name = format!("{}", name);
    let string_hash_name = format!("{}_hash", string_name);
    //  Setup the tail end of the deploy function with remains mostly generic for all casperlabs contracts
    let tail = quote! {
        let (contract_hash, _) = storage::add_contract_version(contract_package_hash, entry_points, #init_ident);
        runtime::put_key(#string_name,contract_hash.into());
        let contract_hash_pack = storage::new_uref(contract_hash);
        runtime::put_key(#string_hash_name, contract_hash_pack.into());
        #contract_call
    };
    init.extend(tail);
    // Finally once all fragments of the deploy function, defintions for all other functions and the arguments for the deploy function have been aggregated
    // Return the indiviual pieces
    Some((init, definitions, deploy_args))
}

// Construct the content for the contract call to be placed in the deploy method
fn get_call(init: &syn::ItemFn) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
    // The name of the function that must be called
    let name = &init.sig.ident.clone();
    // Generate the string represenation of the function name
    let string_name = format!("{}", name);
    // Empty TokenStream for the inputs that will be given to the deploy function
    let mut input_for_deploy: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
    // The call_contract method requires all the arguments that the constructor function
    // takes. Thus we create a new empty token stream for it
    let mut args: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
    // For every argument in the arguments of the function defintion
    // Loop over and fill both token streams
    for indiviual_input in &init.sig.inputs {
        // Retrieve the arguments, the name for each argument, and the type
        //  _, somearg, "somearg", type_of_arg
        let (_, arg, arg_name, arg_type) = get_var_declaration(&indiviual_input);
        // Create an identifier for the argument
        let arg_ident = Ident::new(arg.as_str(), Span::call_site());
        let temp = quote! { #arg => #arg_ident, };
        let input_dec = quote! { #arg_name: #arg_type, };
        // Fill the arguments for the call_contract function
        args.extend(temp);
        // Fill the arguments for the deploy function
        input_for_deploy.extend(input_dec);
    }
    // Generate the code forthe call_contract function call
    let gen = quote! {
        runtime::call_contract::<()>(
            contract_hash,
            #string_name,
            runtime_args! {
                #args
            },
        );
    };
    // Return the call_contract code, along with the inputs for the deploy
    (gen, input_for_deploy)
}

fn get_param(arg: FnArg) -> proc_macro2::TokenStream {
    //Generate a TokenSteam for each indiviual parameter for indiviual args passed to the functions
    let arg_name: &syn::Ident;
    let arg_type: &syn::Ident;
    match arg {
        FnArg::Typed(ref pat_type) => {
            match *pat_type.pat {
                syn::Pat::Ident(ref pat_ident) => {
                    arg_name = &pat_ident.ident;
                }
                _ => panic!("Incorrect"),
            }
            match *pat_type.ty {
                syn::Type::Path(ref path) => {
                    arg_type = &path.path.segments[0].ident;
                }
                _ => panic!("Incorrect"),
            }
        }
        _ => panic!("Incorrect"),
    }
    let string_arg = format!("{}", arg_name);
    match format!("{}", arg_type).as_str() {
        //Currently supported types this can be extended to support additional casperlab types
        "bool" => quote! { Parameter::new(#string_arg, CLType::Bool), },
        "i32" => quote! { Parameter::new(#string_arg, CLType::I32), },
        "i64" => quote! { Parameter::new(#string_arg, CLType::I64), },
        "u8" => quote! { Parameter::new(#string_arg, CLType::U8), },
        "u32" => quote! { Parameter::new(#string_arg, CLType::U32), },
        "u64" => quote! { Parameter::new(#string_arg, CLType::U64), },
        "U128" => quote! { Parameter::new(#string_arg, CLType::128), },
        "U256" => quote! { Parameter::new(#string_arg, CLType::U256),},
        "U512" => quote! { Parameter::new(#string_arg, CLType::U512), },
        "String" => quote! { Parameter::new(#string_arg, CLType::String), },
        "AccountHash" => quote! { Parameter::new(#string_arg, AccountHash::cl_type()), },

        _ => panic!("Currently unsupported type: {}", arg_name),
    }
}

// Generate the inputs for the function call
fn prep_input(input_strings: Vec<String>) -> proc_macro2::TokenStream {
    // When a function must be called we must create an identifier foe each of the args,
    // seperated by a comma
    let first_arg = Ident::new(&input_strings[0], Span::call_site());
    let mut args = quote! { #first_arg, };
    //
    for input_string in input_strings.iter().take(input_strings.len() - 1).skip(1) {
        //Create an Ident from the string representation of each of the args passed to the function
        let ident = Ident::new(&input_string, Span::call_site());
        let temp = quote! { #ident, };
        args.extend(temp);
    }
    if input_strings.len() == 1 {
        return args;
    }
    let last_ident = Ident::new(&input_strings[input_strings.len() - 1], Span::call_site());
    let last_arg = quote! { #last_ident };
    args.extend(last_arg);
    args
}

// If an function has the declaration: bar(a: u64, b: String)
// Then we must generate variable declartion for each argument
// let a: u64 = runtime::get_named_arg("a");
// let b: String = runtime::get_named_arg("b");
// The function returns the declaration for an indiviual argument
fn get_var_declaration(
    arg: &FnArg,
) -> (proc_macro2::TokenStream, String, &syn::Ident, &syn::Ident) {
    // A helper function to parse the arguments of any function and return the associated declaration for the variable,
    // Its string representation, its identifier along with the type of the arg
    let arg_name: &syn::Ident;
    let arg_type: &syn::Ident;
    match arg {
        FnArg::Typed(ref pat_type) => {
            match *pat_type.pat {
                syn::Pat::Ident(ref pat_ident) => {
                    arg_name = &pat_ident.ident;
                }
                _ => panic!("Incorrect"),
            }
            match *pat_type.ty {
                syn::Type::Path(ref path) => {
                    arg_type = &path.path.segments[0].ident;
                }
                _ => panic!("Incorrect"),
            }
        }
        _ => panic!("Incorrect"),
    }
    // Generate the declation
    let string_arg = format!("{}", arg_name);
    let dec = quote! {let #arg_name: #arg_type = runtime::get_named_arg(#string_arg); };
    let arg = format!("{}", arg_name);
    // Return
    // let some_arg: argype = runtime::get_named::arg("some_arg");
    // "some_arg"
    // "some_arg"
    // type of the arg
    (dec, arg, arg_name, arg_type)
}