evm_precompiles_derive/
lib.rs

1// Copyright 2019-2021 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14#![crate_type = "proc-macro"]
15extern crate proc_macro;
16use proc_macro::TokenStream;
17use proc_macro2::Literal;
18use quote::{quote, quote_spanned};
19use sha3::{Digest, Keccak256};
20use std::convert::TryInto;
21use syn::{parse_macro_input, spanned::Spanned, Expr, ExprLit, Ident, ItemEnum, Lit};
22
23/// This macro allows to associate to each variant of an enumeration a discriminant (of type u32
24/// whose value corresponds to the first 4 bytes of the Hash Keccak256 of the character string
25///indicated by the user of this macro.
26///
27/// Usage:
28///
29/// ```ignore
30/// #[evm_precompiles_derive::generate_function_selector]
31/// enum Action {
32///     Toto = "toto()",
33///     Tata = "tata()",
34/// }
35/// ```
36///
37/// Extanded to:
38///
39/// ```rust
40/// #[repr(u32)]
41/// enum Action {
42///     Toto = 119097542u32,
43///     Tata = 1414311903u32,
44/// }
45/// ```
46///
47#[proc_macro_attribute]
48pub fn generate_function_selector(_: TokenStream, input: TokenStream) -> TokenStream {
49    let item = parse_macro_input!(input as ItemEnum);
50
51    let ItemEnum {
52        attrs,
53        vis,
54        enum_token,
55        ident,
56        variants,
57        ..
58    } = item;
59
60    let mut ident_expressions: Vec<Ident> = vec![];
61    let mut variant_expressions: Vec<Expr> = vec![];
62    for variant in variants {
63        match variant.discriminant {
64            Some((_, Expr::Lit(ExprLit { lit, .. }))) => {
65                if let Lit::Str(lit_str) = lit {
66                    let selector = u32::from_be_bytes(
67                        Keccak256::digest(lit_str.value().as_bytes())[..4]
68                            .try_into()
69                            .unwrap(),
70                    );
71                    ident_expressions.push(variant.ident);
72                    variant_expressions.push(Expr::Lit(ExprLit {
73                        lit: Lit::Verbatim(Literal::u32_suffixed(selector)),
74                        attrs: Default::default(),
75                    }));
76                } else {
77                    return quote_spanned! {
78                        lit.span() => compile_error("Expected literal string");
79                    }
80                    .into();
81                }
82            }
83            Some((_eg, expr)) => {
84                return quote_spanned! {
85                    expr.span() => compile_error("Expected literal");
86                }
87                .into()
88            }
89            None => {
90                return quote_spanned! {
91                    variant.span() => compile_error("Each variant must have a discriminant");
92                }
93                .into()
94            }
95        }
96    }
97
98    (quote! {
99        #(#attrs)*
100        #[repr(u32)]
101        #vis #enum_token #ident {
102            #(
103                #ident_expressions = #variant_expressions,
104            )*
105        }
106    })
107    .into()
108}