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}