precompile_utils_macro/
precompile_name_from_address.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19use super::*;
20use syn::{GenericArgument, Type};
21
22pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
23	let item = parse_macro_input!(input as ItemType);
24
25	let ItemType {
26		attrs,
27		vis,
28		type_token,
29		ident,
30		generics,
31		eq_token,
32		ty,
33		semi_token,
34	} = item;
35
36	if let Type::Tuple(ref type_tuple) = *ty {
37		let variants: Vec<(Ident, u64)> = type_tuple
38			.elems
39			.iter()
40			.filter_map(extract_precompile_name_and_prefix)
41			.collect();
42
43		let ident_expressions: Vec<&Ident> = variants.iter().map(|(ident, _)| ident).collect();
44		let variant_expressions: Vec<&u64> = variants.iter().map(|(_, id)| id).collect();
45
46		(quote! {
47			#(#attrs)*
48			#vis #type_token #ident #generics #eq_token #ty #semi_token
49
50			#[derive(num_enum::TryFromPrimitive, num_enum::IntoPrimitive, Debug)]
51			#[repr(u64)]
52			pub enum PrecompileName {
53				#(
54					#ident_expressions = #variant_expressions,
55				)*
56			}
57
58			impl PrecompileName {
59				pub fn from_address(address: sp_core::H160) -> Option<Self> {
60					let _u64 = address.to_low_u64_be();
61					if address == sp_core::H160::from_low_u64_be(_u64) {
62						use num_enum::TryFromPrimitive;
63						Self::try_from_primitive(_u64).ok()
64					} else {
65						None
66					}
67				}
68			}
69		})
70		.into()
71	} else {
72		quote_spanned! {
73			ty.span() => compile_error!("Expected tuple");
74		}
75		.into()
76	}
77}
78
79fn extract_precompile_name_and_prefix(type_: &Type) -> Option<(Ident, u64)> {
80	match type_ {
81		Type::Path(type_path) => {
82			if let Some(path_segment) = type_path.path.segments.last() {
83				match path_segment.ident.to_string().as_ref() {
84					"PrecompileAt" => {
85						extract_precompile_name_and_prefix_for_precompile_at(path_segment)
86					}
87					_ => None,
88				}
89			} else {
90				None
91			}
92		}
93		_ => None,
94	}
95}
96
97fn extract_precompile_name_and_prefix_for_precompile_at(
98	path_segment: &syn::PathSegment,
99) -> Option<(Ident, u64)> {
100	if let syn::PathArguments::AngleBracketed(generics) = &path_segment.arguments {
101		let mut iter = generics.args.iter();
102		if let (
103			Some(GenericArgument::Type(Type::Path(type_path_1))),
104			Some(GenericArgument::Type(Type::Path(type_path_2))),
105		) = (iter.next(), iter.next())
106		{
107			if let (Some(path_segment_1), Some(path_segment_2)) = (
108				type_path_1.path.segments.last(),
109				type_path_2.path.segments.last(),
110			) {
111				if let syn::PathArguments::AngleBracketed(generics_) = &path_segment_1.arguments {
112					if let Some(GenericArgument::Const(Expr::Lit(lit))) = generics_.args.first() {
113						if let Lit::Int(int) = &lit.lit {
114							if let Ok(precompile_id) = int.base10_parse() {
115								if &path_segment_2.ident.to_string() == "CollectivePrecompile" {
116									if let Some(instance_ident) =
117										precompile_instance_ident(path_segment_2)
118									{
119										return Some((instance_ident, precompile_id));
120									}
121								} else {
122									return Some((path_segment_2.ident.clone(), precompile_id));
123								}
124							}
125						}
126					}
127				}
128			}
129		}
130	}
131
132	None
133}
134
135fn precompile_instance_ident(path_segment: &syn::PathSegment) -> Option<Ident> {
136	if let syn::PathArguments::AngleBracketed(generics_) = &path_segment.arguments {
137		if let Some(GenericArgument::Type(Type::Path(instance_type_path))) = generics_.args.last() {
138			if let Some(instance_type) = instance_type_path.path.segments.last() {
139				return Some(instance_type.ident.clone());
140			}
141		}
142	}
143
144	None
145}