1use crate::{
19 common::{
20 API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
21 RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
22 },
23 utils::{
24 extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
25 generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
26 prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
27 versioned_trait_name, AllowSelfRefInParameters,
28 },
29};
30
31use proc_macro2::{Span, TokenStream};
32
33use quote::quote;
34
35use syn::{
36 fold::{self, Fold},
37 parse::{Error, Parse, ParseStream, Result},
38 parse_macro_input, parse_quote,
39 spanned::Spanned,
40 token::Comma,
41 visit::{self, Visit},
42 Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound,
43 TraitItem, TraitItemFn,
44};
45
46use std::collections::{BTreeMap, HashMap};
47
48struct RuntimeApiDecls {
50 decls: Vec<ItemTrait>,
51}
52
53impl Parse for RuntimeApiDecls {
54 fn parse(input: ParseStream) -> Result<Self> {
55 let mut decls = Vec::new();
56
57 while !input.is_empty() {
58 decls.push(ItemTrait::parse(input)?);
59 }
60
61 Ok(Self { decls })
62 }
63}
64
65fn extend_generics_with_block(generics: &mut Generics) {
67 let c = generate_crate_access();
68
69 generics.lt_token = Some(Default::default());
70 generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
71 generics.gt_token = Some(Default::default());
72}
73
74fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static str, Attribute> {
78 let mut result = HashMap::new();
79 attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) {
80 Some(attribute) => {
81 result.insert(*attribute, v.clone());
82 false
83 },
84 None => true,
85 });
86
87 result
88}
89
90fn generate_versioned_api_traits(
135 api: ItemTrait,
136 methods: BTreeMap<u64, Vec<TraitItemFn>>,
137) -> Vec<ItemTrait> {
138 let mut result = Vec::<ItemTrait>::new();
139 for (version, _) in &methods {
140 let mut versioned_trait = api.clone();
141 versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
142 versioned_trait.items = Vec::new();
143 for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
146 versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m)));
147 }
148
149 result.push(versioned_trait);
150 }
151
152 result
153}
154
155fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
157 let err = || {
158 Error::new(
159 renamed.span(),
160 &format!(
161 "Unexpected `{RENAMED_ATTRIBUTE}` attribute. \
162 The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`",
163 ),
164 )
165 };
166
167 renamed
168 .parse_args_with(|input: ParseStream| {
169 let old_name: LitStr = input.parse()?;
170 let _comma: Comma = input.parse()?;
171 let version: LitInt = input.parse()?;
172
173 if !input.is_empty() {
174 return Err(input.error("No more arguments expected"))
175 }
176
177 Ok((old_name.value(), version.base10_parse()?))
178 })
179 .map_err(|_| err())
180}
181
182fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
184 let mut result = Vec::new();
185
186 for decl in decls {
187 let mut decl = decl.clone();
188 let decl_span = decl.span();
189 extend_generics_with_block(&mut decl.generics);
190 let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
191 let found_attributes = remove_supported_attributes(&mut decl.attrs);
192 let api_version =
193 get_api_version(&found_attributes).map(|v| generate_runtime_api_version(v as u32))?;
194 let id = generate_runtime_api_id(&decl.ident.to_string());
195
196 let metadata = crate::runtime_metadata::generate_decl_runtime_metadata(&decl);
197
198 let trait_api_version = get_api_version(&found_attributes)?;
199
200 let mut methods_by_version: BTreeMap<u64, Vec<TraitItemFn>> = BTreeMap::new();
201
202 decl.items.iter_mut().for_each(|i| match i {
205 TraitItem::Fn(ref mut method) => {
206 let method_attrs = remove_supported_attributes(&mut method.attrs);
207 let mut method_version = trait_api_version;
208 if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
211 method_version = match parse_runtime_api_version(version_attribute) {
212 Ok(method_api_ver) if method_api_ver < trait_api_version => {
213 let method_ver = method_api_ver.to_string();
214 let trait_ver = trait_api_version.to_string();
215 let mut err1 = Error::new(
216 version_attribute.span(),
217 format!(
218 "Method version `{}` is older than (or equal to) trait version `{}`.\
219 Methods can't define versions older than the trait version.",
220 method_ver,
221 trait_ver,
222 ),
223 );
224
225 let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
226 Some(attr) => Error::new(attr.span(), "Trait version is set here."),
227 None => Error::new(
228 decl_span,
229 "Trait version is not set so it is implicitly equal to 1.",
230 ),
231 };
232 err1.combine(err2);
233 result.push(err1.to_compile_error());
234
235 trait_api_version
236 },
237 Ok(method_api_ver) => method_api_ver,
238 Err(e) => {
239 result.push(e.to_compile_error());
240 trait_api_version
241 },
242 };
243 }
244
245 if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
248 replace_wild_card_parameter_names(&mut method.sig);
250
251 methods_by_version.entry(method_version).or_default().push(method.clone());
253 }
254 },
255 _ => (),
256 });
257
258 let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
259
260 let main_api_ident = decl.ident.clone();
261 let versioned_ident = &versioned_api_traits
262 .first()
263 .expect("There should always be at least one version.")
264 .ident;
265
266 result.push(quote!(
267 #[doc(hidden)]
268 #[allow(dead_code)]
269 #[allow(deprecated)]
270 pub mod #mod_name {
271 pub use super::*;
272
273 #( #versioned_api_traits )*
274
275 pub use #versioned_ident as #main_api_ident;
276
277 #metadata
278
279 pub #api_version
280
281 pub #id
282 }
283 ));
284 }
285
286 Ok(quote!( #( #result )* ))
287}
288
289struct ToClientSideDecl<'a> {
291 block_hash: &'a TokenStream,
292 crate_: &'a TokenStream,
293 found_attributes: &'a mut HashMap<&'static str, Attribute>,
294 errors: &'a mut Vec<TokenStream>,
296 trait_: &'a Ident,
297}
298
299impl<'a> ToClientSideDecl<'a> {
300 fn process(mut self, decl: ItemTrait) -> ItemTrait {
302 let mut decl = self.fold_item_trait(decl);
303
304 let block_hash = self.block_hash;
305 let crate_ = self.crate_;
306
307 decl.items.push(parse_quote! {
310 #[doc(hidden)]
312 fn __runtime_api_internal_call_api_at(
313 &self,
314 at: #block_hash,
315 params: std::vec::Vec<u8>,
316 fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
317 ) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
318 });
319
320 decl
321 }
322}
323
324impl<'a> ToClientSideDecl<'a> {
325 fn fold_item_trait_items(
326 &mut self,
327 items: Vec<TraitItem>,
328 trait_generics_num: usize,
329 ) -> Vec<TraitItem> {
330 let mut result = Vec::new();
331
332 items.into_iter().for_each(|i| match i {
333 TraitItem::Fn(method) => {
334 let fn_decl = self.create_method_decl(method, trait_generics_num);
335 result.push(fn_decl.into());
336 },
337 r => result.push(r),
338 });
339
340 result
341 }
342
343 fn create_method_decl(
347 &mut self,
348 mut method: TraitItemFn,
349 trait_generics_num: usize,
350 ) -> TraitItemFn {
351 let params = match extract_parameter_names_types_and_borrows(
352 &method.sig,
353 AllowSelfRefInParameters::No,
354 ) {
355 Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
356 Err(e) => {
357 self.errors.push(e.to_compile_error());
358 Vec::new()
359 },
360 };
361 let ret_type = return_type_extract_type(&method.sig.output);
362
363 fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_);
364
365 let crate_ = self.crate_;
366
367 let found_attributes = remove_supported_attributes(&mut method.attrs);
368
369 let mut renames = Vec::new();
371 for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
372 match parse_renamed_attribute(a) {
373 Ok((old_name, version)) => {
374 renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
375 },
376 Err(e) => self.errors.push(e.to_compile_error()),
377 }
378 }
379
380 renames.sort_by(|l, r| r.cmp(l));
381 let (versions, old_names) = renames.into_iter().fold(
382 (Vec::new(), Vec::new()),
383 |(mut versions, mut old_names), (version, old_name)| {
384 versions.push(version);
385 old_names.push(old_name);
386 (versions, old_names)
387 },
388 );
389
390 let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
393
394 match get_changed_in(&found_attributes) {
397 Ok(Some(version)) => {
398 if get_api_version(self.found_attributes).ok() < Some(version) {
400 self.errors.push(
401 Error::new(
402 method.span(),
403 "`changed_in` version can not be greater than the `api_version`",
404 )
405 .to_compile_error(),
406 );
407 }
408
409 let ident = Ident::new(
410 &format!("{}_before_version_{}", method.sig.ident, version),
411 method.sig.ident.span(),
412 );
413 method.sig.ident = ident;
414 method.attrs.push(parse_quote!( #[deprecated] ));
415 },
416 Ok(None) => {},
417 Err(e) => {
418 self.errors.push(e.to_compile_error());
419 },
420 };
421
422 let trait_name = &self.trait_;
424 let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
425 let underscores = (0..trait_generics_num).map(|_| quote!(_));
426
427 method.default = Some(parse_quote! {
429 {
430 let __runtime_api_impl_params_encoded__ =
431 #crate_::Encode::encode(&( #( &#params ),* ));
432
433 <Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
434 self,
435 __runtime_api_at_param__,
436 __runtime_api_impl_params_encoded__,
437 &|_version| {
438 #(
439 if _version.apis.iter().any(|(s, v)| {
441 s == &#runtime_mod::ID && *v < #versions
442 }) {
443 return #old_names
444 }
445 )*
446
447 #function_name
448 }
449 )
450 .and_then(|r|
451 std::result::Result::map_err(
452 <#ret_type as #crate_::Decode>::decode(&mut &r[..]),
453 |err| #crate_::ApiError::FailedToDecodeReturnValue {
454 function: #function_name,
455 error: err,
456 raw: r.clone(),
457 }
458 )
459 )
460 }
461 });
462
463 method
464 }
465}
466
467impl<'a> Fold for ToClientSideDecl<'a> {
468 fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
469 extend_generics_with_block(&mut input.generics);
470
471 *self.found_attributes = remove_supported_attributes(&mut input.attrs);
472 let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
474 let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
475
476 if is_core_trait {
477 input.supertraits = parse_quote!('static + Send);
479 } else {
480 let crate_ = &self.crate_;
482 input.supertraits.push(parse_quote!( #crate_::Core<#block_ident> ));
483 }
484
485 input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
486
487 fold::fold_item_trait(self, input)
488 }
489}
490
491fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
494 use blake2::digest::{consts::U8, Digest};
495
496 let mut res = [0; 8];
497 res.copy_from_slice(blake2::Blake2b::<U8>::digest(trait_name).as_slice());
498
499 quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
500}
501
502fn generate_runtime_api_version(version: u32) -> TokenStream {
504 quote!( const VERSION: u32 = #version; )
505}
506
507fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream {
509 let trait_name = &trait_.ident;
510 let crate_ = generate_crate_access();
511 let id = generate_runtime_api_id(&trait_name.to_string());
512 let version = generate_runtime_api_version(version as u32);
513
514 let impl_generics = trait_.generics.type_params().map(|t| {
515 let ident = &t.ident;
516 let colon_token = &t.colon_token;
517 let bounds = &t.bounds;
518
519 quote! { #ident #colon_token #bounds }
520 });
521
522 let ty_generics = trait_.generics.type_params().map(|t| {
523 let ident = &t.ident;
524 quote! { #ident }
525 });
526
527 quote!(
528 #crate_::std_enabled! {
529 impl < #( #impl_generics, )* > #crate_::RuntimeApiInfo
530 for dyn #trait_name < #( #ty_generics, )* >
531 {
532 #id
533 #version
534 }
535 }
536 )
537}
538
539fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u64>> {
541 found_attributes
542 .get(&CHANGED_IN_ATTRIBUTE)
543 .map(|v| parse_runtime_api_version(v).map(Some))
544 .unwrap_or(Ok(None))
545}
546
547fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u64> {
549 found_attributes
550 .get(&API_VERSION_ATTRIBUTE)
551 .map(parse_runtime_api_version)
552 .unwrap_or(Ok(1))
553}
554
555fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
557 let mut result = Vec::new();
558
559 for decl in decls {
560 let decl = decl.clone();
561
562 let crate_ = generate_crate_access();
563 let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
564 let mut found_attributes = HashMap::new();
565 let mut errors = Vec::new();
566 let trait_ = decl.ident.clone();
567
568 let decl = ToClientSideDecl {
569 crate_: &crate_,
570 block_hash: &block_hash,
571 found_attributes: &mut found_attributes,
572 errors: &mut errors,
573 trait_: &trait_,
574 }
575 .process(decl);
576
577 let api_version = get_api_version(&found_attributes);
578
579 let runtime_info = api_version.map(|v| generate_runtime_info_impl(&decl, v))?;
580
581 result.push(quote!(
582 #crate_::std_enabled! { #decl }
583 #runtime_info
584 #( #errors )*
585 ));
586 }
587
588 Ok(quote!( #( #result )* ))
589}
590
591struct CheckTraitDecl {
593 errors: Vec<Error>,
594}
595
596impl CheckTraitDecl {
597 fn check(&mut self, trait_: &ItemTrait) {
601 self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
602 TraitItem::Fn(method) => Some(method),
603 _ => None,
604 }));
605
606 visit::visit_item_trait(self, trait_);
607 }
608
609 fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemFn>) {
613 let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u64>>>::new();
614
615 methods.into_iter().for_each(|method| {
616 let attributes = remove_supported_attributes(&mut method.attrs.clone());
617
618 let changed_in = match get_changed_in(&attributes) {
619 Ok(r) => r,
620 Err(e) => {
621 self.errors.push(e);
622 return
623 },
624 };
625
626 method_to_signature_changed
627 .entry(method.sig.ident.clone())
628 .or_default()
629 .push(changed_in);
630
631 if method.default.is_some() {
632 self.errors.push(Error::new(
633 method.default.span(),
634 "A runtime API function cannot have a default implementation!",
635 ));
636 }
637 });
638
639 method_to_signature_changed.into_iter().for_each(|(f, changed)| {
640 if changed.iter().filter(|c| c.is_none()).count() == 0 {
643 self.errors.push(Error::new(
644 f.span(),
645 "There is no 'default' method with this name (without `changed_in` attribute).\n\
646 The 'default' method is used to call into the latest implementation.",
647 ));
648 }
649 });
650 }
651}
652
653impl<'ast> Visit<'ast> for CheckTraitDecl {
654 fn visit_fn_arg(&mut self, input: &'ast FnArg) {
655 if let FnArg::Receiver(_) = input {
656 self.errors.push(Error::new(input.span(), "`self` as argument not supported."))
657 }
658
659 visit::visit_fn_arg(self, input);
660 }
661
662 fn visit_generic_param(&mut self, input: &'ast GenericParam) {
663 match input {
664 GenericParam::Type(ty) if ty.ident == BLOCK_GENERIC_IDENT =>
665 self.errors.push(Error::new(
666 input.span(),
667 "`Block: BlockT` generic parameter will be added automatically by the \
668 `decl_runtime_apis!` macro!",
669 )),
670 _ => {},
671 }
672
673 visit::visit_generic_param(self, input);
674 }
675
676 fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
677 if let Some(last_ident) = input.path.segments.last().map(|v| &v.ident) {
678 if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
679 self.errors.push(Error::new(
680 input.span(),
681 "`Block: BlockT` generic parameter will be added automatically by the \
682 `decl_runtime_apis!` macro! If you try to use a different trait than the \
683 substrate `Block` trait, please rename it locally.",
684 ))
685 }
686 }
687
688 visit::visit_trait_bound(self, input)
689 }
690}
691
692fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
694 let mut checker = CheckTraitDecl { errors: Vec::new() };
695 decls.iter().for_each(|decl| checker.check(decl));
696
697 if let Some(err) = checker.errors.pop() {
698 Err(checker.errors.into_iter().fold(err, |mut err, other| {
699 err.combine(other);
700 err
701 }))
702 } else {
703 Ok(())
704 }
705}
706
707pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
709 let RuntimeApiDecls { decls: api_decls } = parse_macro_input!(input as RuntimeApiDecls);
711
712 decl_runtime_apis_impl_inner(&api_decls)
713 .unwrap_or_else(|e| e.to_compile_error())
714 .into()
715}
716
717fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
718 check_trait_decls(api_decls)?;
719
720 let runtime_decls = generate_runtime_decls(api_decls)?;
721 let client_side_decls = generate_client_side_decls(api_decls)?;
722
723 let decl = quote! {
724 #runtime_decls
725
726 #client_side_decls
727 };
728
729 let decl = expander::Expander::new("decl_runtime_apis")
730 .dry(std::env::var("EXPAND_MACROS").is_err())
731 .verbose(true)
732 .write_to_out_dir(decl)
733 .expect("Does not fail because of IO in OUT_DIR; qed");
734
735 Ok(decl)
736}