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
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This file is part of Frontier.
//
// Copyright (c) 2019-2022 Moonsong Labs.
// Copyright (c) 2023 Parity Technologies (UK) Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#![doc = include_str!("../../docs/precompile_macro.md")]
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use sp_crypto_hashing::keccak_256;
use std::collections::BTreeMap;
use syn::{parse_macro_input, spanned::Spanned};
pub mod attr;
pub mod expand;
pub mod parse;
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Macro must be used on `impl` block.
let mut impl_item = parse_macro_input!(item as syn::ItemImpl);
// We inspect the block to collect all the data we need for the
// expansion, and make various checks.
let precompile = match Precompile::try_from(&mut impl_item) {
Ok(p) => p,
Err(e) => return e.into_compile_error().into(),
};
// We generate additional code based on the collected data.
let new_items = precompile.expand();
let output = quote!(
#impl_item
#new_items
);
output.into()
}
struct Precompile {
/// Impl struct type.
impl_type: syn::Type,
/// Impl struct ident.
impl_ident: syn::Ident,
/// New parsing enum ident.
enum_ident: syn::Ident,
/// Generic part that needs to also be used by the input enum.
generics: syn::Generics,
/// Which selector corresponds to which variant of the input enum.
selector_to_variant: BTreeMap<u32, syn::Ident>,
/// Optional fallback function if no selector matches.
fallback_to_variant: Option<syn::Ident>,
/// Describes the content of each variant based on the precompile methods.
variants_content: BTreeMap<syn::Ident, Variant>,
/// Since being a precompile set implies lots of changes, we must know it early
/// in the form of an attribute on the impl block itself.
tagged_as_precompile_set: bool,
/// Ident of the function returning the PrecompileSet discriminant.
precompile_set_discriminant_fn: Option<syn::Ident>,
/// Type of the PrecompileSet discriminant.
precompile_set_discriminant_type: Option<syn::Type>,
/// When generating the selector test the data types might depend on type parameters.
/// The test thus need to be written using concrete types.
test_concrete_types: Option<Vec<syn::Type>>,
/// Ident of a function that performs a check before the call is dispatched to the proper
/// function.
pre_check: Option<syn::Ident>,
}
#[derive(Debug, PartialEq, Eq)]
enum Modifier {
NonPayable,
Payable,
View,
}
#[derive(Debug)]
struct Variant {
/// Description of the arguments of this method, which will also
/// be members of a struct variant.
arguments: Vec<Argument>,
/// String extracted from the selector attribute.
/// A unit test will be generated to check that this selector matches
/// the Rust arguments.
///
/// > solidity::Codec trait allows to generate this string at runtime only. Thus
/// > it is required to write it manually in the selector attribute, and
/// > a unit test is generated to check it matches.
solidity_arguments_type: String,
/// Modifier of the function. They are all exclusive and defaults to
/// `NonPayable`.
modifier: Modifier,
/// Selectors of this function to be able to encode back the data.
/// Empty if it only the fallback function.
selectors: Vec<u32>,
/// Output of the variant fn (for better error messages).
fn_output: syn::Type,
}
#[derive(Debug)]
struct Argument {
/// Identifier of the argument, which will be used in the struct variant.
ident: syn::Ident,
/// Type of the argument, which will be used in the struct variant and
/// to parse the input.
ty: syn::Type,
}