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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! This macro is copied from cumulus-pallet-parachain-system-proc-macro crate
//! and modified slightly to fit Tuxedo's needs.
use proc_macro2::{Literal, Span};
use proc_macro_crate::{crate_name, FoundCrate};
use syn::{
parse::{Parse, ParseStream},
Error, Ident, Token,
};
/// Provides an identifier that is a safe way to refer to the crate tuxedo_parachain_core within the macro
fn crate_() -> Result<Ident, Error> {
match crate_name("tuxedo-parachain-core") {
Ok(FoundCrate::Itself) => Ok(syn::Ident::new("tuxedo_parachain_core", Span::call_site())),
Ok(FoundCrate::Name(name)) => Ok(Ident::new(&name, Span::call_site())),
Err(e) => Err(Error::new(Span::call_site(), e)),
}
}
struct RegisterValidateBlockInput {
pub verifier: Ident,
_comma1: Token![,],
pub inner_constraint_checker: Ident,
_comma2: Token![,],
pub para_id: Literal,
}
impl Parse for RegisterValidateBlockInput {
fn parse(input: ParseStream) -> syn::Result<Self> {
let parsed = Self {
verifier: input.parse()?,
_comma1: input.parse()?,
inner_constraint_checker: input.parse()?,
_comma2: input.parse()?,
para_id: input.parse()?,
};
if !input.is_empty() {
return Err(Error::new(
input.span(),
"Expected exactly three parameters: Verifier, InnerConstraintChecker, ParaId.",
));
}
Ok(parsed)
}
}
#[proc_macro]
pub fn parachainify(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Extract the paths to the parts from the runtime developer's input
// I will likely need to revise or simplify the fields that are passed in.
// I hope to only use the exposed runtime APIs here, not some custom trait impls. (if possible)
let input: RegisterValidateBlockInput = match syn::parse(input) {
Ok(t) => t,
Err(e) => return e.into_compile_error().into(),
};
let verifier = input.verifier.clone();
let inner_constraint_checker = input.inner_constraint_checker.clone();
let para_id = input.para_id.clone();
// A way to refer to the tuxedo_parachain_core crate from within the macro.
let crate_ = match crate_() {
Ok(c) => c,
Err(e) => return e.into_compile_error().into(),
};
// Implementation of Polkadot's validate_block function. Inspired by Basti's frame version:
// https://github.com/paritytech/polkadot-sdk/blob/0becc45b/cumulus/pallets/parachain-system/proc-macro/src/lib.rs#L93-L153
let validate_block_func = if cfg!(not(feature = "std")) {
quote::quote! {
#[doc(hidden)]
mod parachain_validate_block {
use super::*;
#[no_mangle]
unsafe fn validate_block(arguments: *mut u8, arguments_len: usize) -> u64 {
// There is some complex low-level shared-memory stuff implemented here.
// It is basically a wrapper around the validate block implementation
// that handles extracting params and returning results via shared memory.
// Step 1. Extract the arguments from shared memory
// We convert the `arguments` into a boxed slice and then into `Bytes`.
let args = #crate_::sp_std::boxed::Box::from_raw(
#crate_::sp_std::slice::from_raw_parts_mut(
arguments,
arguments_len,
)
);
let args = #crate_::bytes::Bytes::from(args);
// Then we decode from these bytes the `MemoryOptimizedValidationParams`.
let params = #crate_::decode_from_bytes::<
#crate_::MemoryOptimizedValidationParams
>(args).expect("Invalid arguments to `validate_block`.");
// Step 2: Call the actual validate_block implementation
let res = #crate_::validate_block::validate_block::<
#verifier,
ParachainConstraintChecker,
>(params);
// Step 3: Write the return value back into the shared memory
let return_pointer = #crate_::polkadot_parachain_primitives::write_result(&res);
return_pointer
}
}
}
} else {
// If we are building to std, we don't include this validate_block entry point at all
quote::quote!()
};
// Write the piece config and the `ParachainConstraintChecker` enum.
let parachain_constraint_checker_enum = quote::quote! {
#[derive(PartialEq, Eq, Clone)]
pub struct RuntimeParachainConfig;
impl parachain_piece::ParachainPieceConfig for RuntimeParachainConfig {
const PARA_ID: u32 = #para_id;
type SetRelayParentNumberStorage = tuxedo_parachain_core::RelayParentNumberStorage;
}
/// The Outer / Aggregate Constraint Checker for the Parachain runtime.
///
/// It is comprized of two individual checkers:
/// First, the constraint checker from the normal Tuxedo Template Runtime.
/// Second, the parachain inherent piece
///
/// That first constituent checker, the normal tuxedo template runtime, is itself an aggregate
/// constraint checker aggregated from individual pieces such as money, amoeba, and others.
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
#[tuxedo_constraint_checker]
pub enum ParachainConstraintChecker {
/// All other calls are delegated to the normal Tuxedo Template Runtime.
Inner(#inner_constraint_checker),
/// Set some parachain related information via an inherent extrinsic.
ParachainInfo(InherentAdapter<parachain_piece::SetParachainInfo<RuntimeParachainConfig>>),
}
// We provide a way for the relay chain validators to extract the parachain inherent data from
// a raw transaction.
impl #crate_::ParachainConstraintChecker for ParachainConstraintChecker {
fn is_parachain(&self) -> bool {
matches!(self, Self::ParachainInfo(_))
}
}
};
// The final output is the `ParachainConstraintChecker` plus the `validate_block` function.
quote::quote! {
#validate_block_func
#parachain_constraint_checker_enum
}
.into()
}