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
// This file is part of Frontier.
// Copyright (c) Moonsong Labs.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![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,
}