Bring support for calling check_inherents (#490)

* Start

* More work

* Add proc-macro for `validate_block`

* Make everything compile

* Add some test
This commit is contained in:
Bastian Köcher
2021-06-12 19:21:46 +01:00
committed by GitHub
parent c5819c9e78
commit 2dc281b482
20 changed files with 684 additions and 326 deletions
+2 -6
View File
@@ -9,6 +9,7 @@ description = "Base pallet for cumulus-based parachains"
# Cumulus dependencies
cumulus-primitives-core = { path = "../../primitives/core", default-features = false }
cumulus-primitives-parachain-inherent = { path = "../../primitives/parachain-inherent", default-features = false }
cumulus-pallet-parachain-system-proc-macro = { path = "proc-macro", default-features = false }
# Polkadot dependencies
polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, features = [ "wasm-api" ], branch = "master" }
@@ -31,10 +32,7 @@ sp-externalities = { git = "https://github.com/paritytech/substrate", default-fe
# Other Dependencies
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"]}
serde = { version = "1.0.101", optional = true, features = ["derive"] }
hash-db = { version = "0.15.2", default-features = false }
log = { version = "0.4.14", default-features = false }
memory-db = { version = "0.26.0", default-features = false }
trie-db = { version = "0.22.0", default-features = false }
environmental = { version = "1.1.2", default-features = false }
[dev-dependencies]
@@ -63,16 +61,14 @@ std = [
"sp-runtime/std",
"sp-io/std",
"sp-std/std",
"hash-db/std",
"log/std",
"memory-db/std",
"trie-db/std",
"sp-state-machine/std",
"sp-trie/std",
"sp-externalities/std",
"frame-system/std",
"cumulus-primitives-core/std",
"cumulus-primitives-parachain-inherent/std",
"cumulus-pallet-parachain-system-proc-macro/std",
"environmental/std",
"xcm/std"
]
@@ -0,0 +1,19 @@
[package]
name = "cumulus-pallet-parachain-system-proc-macro"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
description = "Proc macros provided by the parachain-system pallet"
[lib]
proc-macro = true
[dependencies]
syn = "1.0.73"
proc-macro2 = "1.0.27"
quote = "1.0.9"
proc-macro-crate = "1.0.0"
[features]
default = [ "std" ]
std = []
@@ -0,0 +1,144 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus 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.
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use proc_macro_crate::{crate_name, FoundCrate};
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
token, Error, Ident, Path,
};
mod keywords {
syn::custom_keyword!(Runtime);
syn::custom_keyword!(BlockExecutor);
syn::custom_keyword!(CheckInherents);
}
struct Input {
runtime: Path,
block_executor: Path,
check_inherents: Path,
}
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self, Error> {
let mut runtime = None;
let mut block_executor = None;
let mut check_inherents = None;
fn parse_inner<KW: Parse + Spanned>(
input: ParseStream,
result: &mut Option<Path>,
) -> Result<(), Error> {
let kw = input.parse::<KW>()?;
if result.is_none() {
input.parse::<token::Eq>()?;
*result = Some(input.parse::<Path>()?);
if input.peek(token::Comma) {
input.parse::<token::Comma>()?;
}
Ok(())
} else {
Err(Error::new(kw.span(), "Is only allowed to be passed once"))
}
}
while runtime.is_none() || block_executor.is_none() || check_inherents.is_none() {
let lookahead = input.lookahead1();
if lookahead.peek(keywords::Runtime) {
parse_inner::<keywords::Runtime>(input, &mut runtime)?;
} else if lookahead.peek(keywords::BlockExecutor) {
parse_inner::<keywords::BlockExecutor>(input, &mut block_executor)?;
} else if lookahead.peek(keywords::CheckInherents) {
parse_inner::<keywords::CheckInherents>(input, &mut check_inherents)?;
} else {
return Err(lookahead.error());
}
}
let rest = input.parse::<TokenStream>()?;
if !rest.is_empty() {
return Err(Error::new(rest.span(), "Unexpected input data"));
}
Ok(Self {
runtime: runtime.expect("Everything is parsed before; qed"),
block_executor: block_executor.expect("Everything is parsed before; qed"),
check_inherents: check_inherents.expect("Everything is parsed before; qed"),
})
}
}
fn crate_() -> Result<Ident, Error> {
match crate_name("cumulus-pallet-parachain-system") {
Ok(FoundCrate::Itself) => Ok(syn::Ident::new(
"cumulus_pallet_parachain_system",
Span::call_site(),
)),
Ok(FoundCrate::Name(name)) => Ok(Ident::new(&name, Span::call_site())),
Err(e) => Err(Error::new(Span::call_site(), e)),
}
}
#[proc_macro]
pub fn register_validate_block(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let Input {
runtime,
check_inherents,
block_executor,
} = match syn::parse(input) {
Ok(t) => t,
Err(e) => return e.into_compile_error().into(),
};
let crate_ = match crate_() {
Ok(c) => c,
Err(e) => return e.into_compile_error().into(),
};
if cfg!(not(feature = "std")) {
quote::quote! {
#[doc(hidden)]
mod parachain_validate_block {
use super::*;
#[no_mangle]
unsafe fn validate_block(arguments: *const u8, arguments_len: usize) -> u64 {
let params = #crate_::validate_block::polkadot_parachain::load_params(
arguments,
arguments_len,
);
let res = #crate_::validate_block::implementation::validate_block::<
<#runtime as #crate_::validate_block::GetRuntimeBlockType>::RuntimeBlock,
#block_executor,
#runtime,
#check_inherents,
>(params);
#crate_::validate_block::polkadot_parachain::write_result(&res)
}
}
}
} else {
quote::quote!()
}
.into()
}
+53 -14
View File
@@ -30,8 +30,8 @@
use cumulus_primitives_core::{
relay_chain, AbridgedHostConfiguration, ChannelStatus, CollationInfo, DmpMessageHandler,
GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, MessageSendError, OnValidationData,
OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender,
XcmpMessageHandler, XcmpMessageSource,
OutboundHrmpMessage, ParaId, UpwardMessage, UpwardMessageSender, XcmpMessageHandler,
XcmpMessageSource, PersistedValidationData,
};
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use frame_support::{
@@ -46,7 +46,7 @@ use frame_system::{ensure_none, ensure_root};
use polkadot_parachain::primitives::RelayChainBlockNumber;
use relay_state_snapshot::MessagingStateSnapshot;
use sp_runtime::{
traits::{BlakeTwo256, Hash},
traits::{BlakeTwo256, Block as BlockT, Hash},
transaction_validity::{
InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity,
ValidTransaction,
@@ -60,6 +60,31 @@ pub mod validate_block;
#[cfg(test)]
mod tests;
/// Register the `validate_block` function that is used by parachains to validate blocks on a
/// validator.
///
/// Does *nothing* when `std` feature is enabled.
///
/// Expects as parameters the runtime, a block executor and an inherent checker.
///
/// # Example
///
/// ```
/// struct BlockExecutor;
/// struct Runtime;
/// struct CheckInherents;
///
/// cumulus_pallet_parachain_system::register_validate_block! {
/// Runtime = Runtime,
/// BlockExecutor = Executive,
/// CheckInherents = CheckInherents,
/// }
///
/// # fn main() {}
/// ```
pub use cumulus_pallet_parachain_system_proc_macro::register_validate_block;
pub use relay_state_snapshot::RelayChainStateProof;
pub use pallet::*;
#[frame_support::pallet]
@@ -309,17 +334,19 @@ pub mod pallet {
}
}
let (host_config, relevant_messaging_state) =
match relay_state_snapshot::extract_from_proof(
T::SelfParaId::get(),
vfp.relay_parent_storage_root,
relay_chain_state,
) {
Ok(r) => r,
Err(err) => {
panic!("invalid relay chain merkle proof: {:?}", err);
}
};
let relay_state_proof = RelayChainStateProof::new(
T::SelfParaId::get(),
vfp.relay_parent_storage_root,
relay_chain_state,
)
.expect("Invalid relay chain state proof");
let host_config = relay_state_proof
.read_abridged_host_configuration()
.expect("Invalid host configuration in relay chain state proof");
let relevant_messaging_state = relay_state_proof
.read_messaging_state_snapshot()
.expect("Invalid messaging state in relay chain state proof");
<ValidationData<T>>::put(&vfp);
<RelevantMessagingState<T>>::put(relevant_messaging_state.clone());
@@ -999,3 +1026,15 @@ impl<T: Config> UpwardMessageSender for Pallet<T> {
Self::send_upward_message(message)
}
}
/// Something that can check the inherents of a block.
pub trait CheckInherents<Block: BlockT> {
/// Check all inherents of the block.
///
/// This function gets passed all the extrinsics of the block, so it is up to the callee to
/// identify the inherents. The `validation_data` can be used to access the
fn check_inherents(
extrinsics: &[Block::Extrinsic],
validation_data: &RelayChainStateProof,
) -> frame_support::inherent::CheckInherentsResult;
}
@@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use codec::{Encode, Decode};
use cumulus_primitives_core::{relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId};
use hash_db::{HashDB, EMPTY_PREFIX};
use codec::{Decode, Encode};
use cumulus_primitives_core::{
relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId,
};
use sp_trie::{MemoryDB, HashDBT, EMPTY_PREFIX};
use sp_runtime::traits::HashFor;
use sp_state_machine::{Backend, TrieBackend};
use sp_trie::StorageProof;
use sp_std::vec::Vec;
use sp_trie::StorageProof;
/// A snapshot of some messaging related state of relay chain pertaining to the current parachain.
///
@@ -61,6 +63,8 @@ pub struct MessagingStateSnapshot {
pub enum Error {
/// The provided proof was created against unexpected storage root.
RootMismatch,
/// The slot cannot be extracted.
Slot(ReadEntryErr),
/// The host configuration cannot be extracted.
Config(ReadEntryErr),
/// The DMQ MQC head cannot be extracted.
@@ -105,94 +109,122 @@ where
.ok_or(ReadEntryErr::Absent)
}
/// Extract the relay chain state from the given storage proof. This function accepts the `para_id`
/// of the current parachain and the expected storage root the proof should stem from.
pub fn extract_from_proof(
/// A state proof extracted from the relay chain.
///
/// This state proof is extracted from the relay chain block we are building on top of.
pub struct RelayChainStateProof {
para_id: ParaId,
relay_parent_storage_root: relay_chain::v1::Hash,
proof: StorageProof,
) -> Result<(AbridgedHostConfiguration, MessagingStateSnapshot), Error> {
let db = proof.into_memory_db::<HashFor<relay_chain::Block>>();
if !db.contains(&relay_parent_storage_root, EMPTY_PREFIX) {
return Err(Error::RootMismatch);
trie_backend: TrieBackend<MemoryDB<HashFor<relay_chain::Block>>, HashFor<relay_chain::Block>>,
}
impl RelayChainStateProof {
/// Create a new instance of `Self`.
///
/// Returns an error if the given `relay_parent_storage_root` is not the root of the given
/// `proof`.
pub fn new(
para_id: ParaId,
relay_parent_storage_root: relay_chain::v1::Hash,
proof: StorageProof,
) -> Result<Self, Error> {
let db = proof.into_memory_db::<HashFor<relay_chain::Block>>();
if !db.contains(&relay_parent_storage_root, EMPTY_PREFIX) {
return Err(Error::RootMismatch);
}
let trie_backend = TrieBackend::new(db, relay_parent_storage_root);
Ok(Self {
para_id,
trie_backend,
})
}
let backend = TrieBackend::new(db, relay_parent_storage_root);
let host_config: AbridgedHostConfiguration = read_entry(
&backend,
relay_chain::well_known_keys::ACTIVE_CONFIG,
None,
)
.map_err(Error::Config)?;
let dmq_mqc_head: relay_chain::Hash = read_entry(
&backend,
&relay_chain::well_known_keys::dmq_mqc_head(para_id),
Some(Default::default()),
)
.map_err(Error::DmqMqcHead)?;
let relay_dispatch_queue_size: (u32, u32) = read_entry(
&backend,
&relay_chain::well_known_keys::relay_dispatch_queue_size(para_id),
Some((0, 0)),
)
.map_err(Error::RelayDispatchQueueSize)?;
let ingress_channel_index: Vec<ParaId> = read_entry(
&backend,
&relay_chain::well_known_keys::hrmp_ingress_channel_index(para_id),
Some(Vec::new()),
)
.map_err(Error::HrmpIngressChannelIndex)?;
let egress_channel_index: Vec<ParaId> = read_entry(
&backend,
&relay_chain::well_known_keys::hrmp_egress_channel_index(para_id),
Some(Vec::new()),
)
.map_err(Error::HrmpEgressChannelIndex)?;
let mut ingress_channels = Vec::with_capacity(ingress_channel_index.len());
for sender in ingress_channel_index {
let channel_id = relay_chain::v1::HrmpChannelId {
sender,
recipient: para_id,
};
let hrmp_channel: AbridgedHrmpChannel = read_entry(
&backend,
&relay_chain::well_known_keys::hrmp_channels(channel_id),
None,
/// Read the [`MessagingStateSnapshot`] from the relay chain state proof.
///
/// Returns an error if anything failed at reading or decoding.
pub fn read_messaging_state_snapshot(&self) -> Result<MessagingStateSnapshot, Error> {
let dmq_mqc_head: relay_chain::Hash = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::dmq_mqc_head(self.para_id),
Some(Default::default()),
)
.map_err(|read_err| Error::HrmpChannel(sender, para_id, read_err))?;
ingress_channels.push((sender, hrmp_channel));
}
.map_err(Error::DmqMqcHead)?;
let mut egress_channels = Vec::with_capacity(egress_channel_index.len());
for recipient in egress_channel_index {
let channel_id = relay_chain::v1::HrmpChannelId {
sender: para_id,
recipient,
};
let hrmp_channel: AbridgedHrmpChannel = read_entry(
&backend,
&relay_chain::well_known_keys::hrmp_channels(channel_id),
None,
let relay_dispatch_queue_size: (u32, u32) = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::relay_dispatch_queue_size(self.para_id),
Some((0, 0)),
)
.map_err(|read_err| Error::HrmpChannel(para_id, recipient, read_err))?;
egress_channels.push((recipient, hrmp_channel));
}
.map_err(Error::RelayDispatchQueueSize)?;
// NOTE that ingress_channels and egress_channels promise to be sorted. We satisfy this property
// by relying on the fact that `ingress_channel_index` and `egress_channel_index` are themselves sorted.
let ingress_channel_index: Vec<ParaId> = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::hrmp_ingress_channel_index(self.para_id),
Some(Vec::new()),
)
.map_err(Error::HrmpIngressChannelIndex)?;
Ok((
host_config,
MessagingStateSnapshot {
let egress_channel_index: Vec<ParaId> = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::hrmp_egress_channel_index(self.para_id),
Some(Vec::new()),
)
.map_err(Error::HrmpEgressChannelIndex)?;
let mut ingress_channels = Vec::with_capacity(ingress_channel_index.len());
for sender in ingress_channel_index {
let channel_id = relay_chain::v1::HrmpChannelId {
sender,
recipient: self.para_id,
};
let hrmp_channel: AbridgedHrmpChannel = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::hrmp_channels(channel_id),
None,
)
.map_err(|read_err| Error::HrmpChannel(sender, self.para_id, read_err))?;
ingress_channels.push((sender, hrmp_channel));
}
let mut egress_channels = Vec::with_capacity(egress_channel_index.len());
for recipient in egress_channel_index {
let channel_id = relay_chain::v1::HrmpChannelId {
sender: self.para_id,
recipient,
};
let hrmp_channel: AbridgedHrmpChannel = read_entry(
&self.trie_backend,
&relay_chain::well_known_keys::hrmp_channels(channel_id),
None,
)
.map_err(|read_err| Error::HrmpChannel(self.para_id, recipient, read_err))?;
egress_channels.push((recipient, hrmp_channel));
}
// NOTE that ingress_channels and egress_channels promise to be sorted. We satisfy this property
// by relying on the fact that `ingress_channel_index` and `egress_channel_index` are themselves sorted.
Ok(MessagingStateSnapshot {
dmq_mqc_head,
relay_dispatch_queue_size,
ingress_channels,
egress_channels,
},
))
})
}
/// Read the [`AbridgedHostConfiguration`] from the relay chain state proof.
///
/// Returns an error if anything failed at reading or decoding.
pub fn read_abridged_host_configuration(&self) -> Result<AbridgedHostConfiguration, Error> {
read_entry(&self.trie_backend, relay_chain::well_known_keys::ACTIVE_CONFIG, None)
.map_err(Error::Config)
}
/// Read the [`Slot`](relay_chain::v1::Slot) from the relay chain state proof.
///
/// The slot is slot of the relay chain block this state proof was extracted from.
///
/// Returns an error if anything failed at reading or decoding.
pub fn read_slot(&self) -> Result<relay_chain::v1::Slot, Error> {
read_entry(&self.trie_backend, relay_chain::well_known_keys::CURRENT_SLOT, None).map_err(Error::Slot)
}
}
@@ -16,14 +16,12 @@
//! The actual implementation of the validate block functionality.
use frame_support::traits::ExecuteBlock;
use sp_runtime::traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor};
use frame_support::traits::{ExecuteBlock, ExtrinsicCall, IsSubType, Get};
use sp_runtime::traits::{Block as BlockT, Extrinsic, HashFor, Header as HeaderT, NumberFor};
use sp_io::KillChildStorageResult;
use sp_std::prelude::*;
use hash_db::{HashDB, EMPTY_PREFIX};
use polkadot_parachain::primitives::{HeadData, ValidationParams, ValidationResult};
use codec::{Decode, Encode};
@@ -32,12 +30,9 @@ use sp_core::storage::ChildInfo;
use sp_externalities::{set_and_run_with_externalities, Externalities};
use sp_trie::MemoryDB;
type Ext<'a, B> = sp_state_machine::Ext<
'a,
HashFor<B>,
NumberFor<B>,
sp_state_machine::TrieBackend<MemoryDB<HashFor<B>>, HashFor<B>>,
>;
type TrieBackend<B> = sp_state_machine::TrieBackend<MemoryDB<HashFor<B>>, HashFor<B>>;
type Ext<'a, B> = sp_state_machine::Ext<'a, HashFor<B>, NumberFor<B>, TrieBackend<B>>;
fn with_externalities<F: FnOnce(&mut dyn Externalities) -> R, R>(f: F) -> R {
sp_externalities::with_externalities(f).expect("Environmental externalities not set.")
@@ -45,9 +40,18 @@ fn with_externalities<F: FnOnce(&mut dyn Externalities) -> R, R>(f: F) -> R {
/// Validate a given parachain block on a validator.
#[doc(hidden)]
pub fn validate_block<B: BlockT, E: ExecuteBlock<B>, PSC: crate::Config>(
pub fn validate_block<
B: BlockT,
E: ExecuteBlock<B>,
PSC: crate::Config,
CI: crate::CheckInherents<B>,
>(
params: ValidationParams,
) -> ValidationResult {
) -> ValidationResult
where
B::Extrinsic: ExtrinsicCall,
<B::Extrinsic as Extrinsic>::Call: IsSubType<crate::Call<PSC>>,
{
let block_data =
cumulus_primitives_core::ParachainBlockData::<B>::decode(&mut &params.block_data.0[..])
.expect("Invalid parachain block data");
@@ -77,9 +81,6 @@ pub fn validate_block<B: BlockT, E: ExecuteBlock<B>, PSC: crate::Config>(
};
let backend = sp_state_machine::TrieBackend::new(db, root);
let mut overlay = sp_state_machine::OverlayedChanges::default();
let mut cache = Default::default();
let mut ext = Ext::<B>::new(&mut overlay, &mut cache, &backend);
let _guard = (
// Replace storage calls with our own implementations
@@ -121,7 +122,38 @@ pub fn validate_block<B: BlockT, E: ExecuteBlock<B>, PSC: crate::Config>(
sp_io::offchain_index::host_clear.replace_implementation(host_offchain_index_clear),
);
set_and_run_with_externalities(&mut ext, || {
let inherent_data = block
.extrinsics()
.iter()
.filter_map(|e| e.call().is_sub_type())
.find_map(|c| match c {
crate::Call::set_validation_data(validation_data) => Some(validation_data.clone()),
_ => None,
})
.expect("Could not find `set_validation_data` inherent");
run_with_externalities::<B, _, _>(&backend, || {
let relay_chain_proof = crate::RelayChainStateProof::new(
PSC::SelfParaId::get(),
inherent_data.validation_data.relay_parent_storage_root,
inherent_data.relay_chain_state.clone(),
)
.expect("Invalid relay chain state proof");
let res = CI::check_inherents(block.extrinsics(), &relay_chain_proof);
if !res.ok() {
if log::log_enabled!(log::Level::Error) {
res.into_errors().for_each(|e| {
log::error!("Checking inherent with identifier `{:?}` failed", e.0)
});
}
panic!("Checking inherents failed");
}
});
run_with_externalities::<B, _, _>(&backend, || {
super::set_and_run_with_validation_params(params, || {
E::execute_block(block);
@@ -143,6 +175,18 @@ pub fn validate_block<B: BlockT, E: ExecuteBlock<B>, PSC: crate::Config>(
})
}
/// Run the given closure with the externalities set.
fn run_with_externalities<B: BlockT, R, F: FnOnce() -> R>(
backend: &TrieBackend<B>,
execute: F,
) -> R {
let mut overlay = sp_state_machine::OverlayedChanges::default();
let mut cache = Default::default();
let mut ext = Ext::<B>::new(&mut overlay, &mut cache, backend);
set_and_run_with_externalities(&mut ext, || execute())
}
fn host_storage_read(key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option<u32> {
match with_externalities(|ext| ext.storage(key)) {
Some(value) => {
@@ -49,64 +49,3 @@ pub(crate) fn with_validation_params<R>(f: impl FnOnce(&ValidationParams) -> R)
fn set_and_run_with_validation_params<R>(mut params: ValidationParams, f: impl FnOnce() -> R) -> R {
VALIDATION_PARAMS::using(&mut params, f)
}
/// Register the `validate_block` function that is used by parachains to validate blocks on a
/// validator.
///
/// Does *nothing* when `std` feature is enabled.
///
/// Expects as parameters the runtime and a block executor.
///
/// # Example
///
/// ```
/// struct BlockExecutor;
/// struct Runtime;
///
/// cumulus_pallet_parachain_system::register_validate_block!(Runtime, BlockExecutor);
///
/// # fn main() {}
/// ```
#[macro_export]
macro_rules! register_validate_block {
($runtime:ty, $block_executor:ty $( , )? ) => {
$crate::register_validate_block_impl!($runtime, $block_executor);
};
}
/// The actual implementation of `register_validate_block` for `no_std`.
#[cfg(not(feature = "std"))]
#[doc(hidden)]
#[macro_export]
macro_rules! register_validate_block_impl {
($runtime:ty, $block_executor:ty) => {
#[doc(hidden)]
mod parachain_validate_block {
use super::*;
#[no_mangle]
unsafe fn validate_block(arguments: *const u8, arguments_len: usize) -> u64 {
let params = $crate::validate_block::polkadot_parachain::load_params(
arguments,
arguments_len,
);
let res = $crate::validate_block::implementation::validate_block::<
<$runtime as $crate::validate_block::GetRuntimeBlockType>::RuntimeBlock,
$block_executor,
$runtime,
>(params);
$crate::validate_block::polkadot_parachain::write_result(&res)
}
}
};
}
/// The actual implementation of `register_validate_block` for `std`.
#[cfg(feature = "std")]
#[doc(hidden)]
#[macro_export]
macro_rules! register_validate_block_impl {
($runtime:ty, $block_executor:ty) => {};
}
@@ -94,8 +94,8 @@ fn build_block_with_witness(
client: &Client,
extra_extrinsics: Vec<UncheckedExtrinsic>,
parent_head: Header,
sproof_builder: RelayStateSproofBuilder,
) -> TestBlockData {
let sproof_builder = RelayStateSproofBuilder::default();
let (relay_parent_storage_root, _) = sproof_builder.clone().into_state_root_and_proof();
let block_id = BlockId::Hash(client.info().best_hash);
let mut validation_data = PersistedValidationData {
@@ -137,7 +137,7 @@ fn validate_block_no_extra_extrinsics() {
block,
witness,
validation_data,
} = build_block_with_witness(&client, vec![], parent_head.clone());
} = build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header.clone(), extrinsics, witness);
@@ -167,7 +167,12 @@ fn validate_block_with_extra_extrinsics() {
block,
witness,
validation_data,
} = build_block_with_witness(&client, extra_extrinsics, parent_head.clone());
} = build_block_with_witness(
&client,
extra_extrinsics,
parent_head.clone(),
Default::default(),
);
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header.clone(), extrinsics, witness);
@@ -192,7 +197,7 @@ fn validate_block_invalid_parent_hash() {
block,
witness,
validation_data,
} = build_block_with_witness(&client, vec![], parent_head.clone());
} = build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
let (mut header, extrinsics) = block.deconstruct();
header.set_parent_hash(Hash::from_low_u64_be(1));
@@ -212,18 +217,44 @@ fn validate_block_fails_on_invalid_validation_data() {
let (client, longest_chain) = create_test_client();
let parent_head = longest_chain.best_chain().expect("Best block exists");
let TestBlockData {
block,
witness,
..
} = build_block_with_witness(&client, vec![], parent_head.clone());
let TestBlockData { block, witness, .. } =
build_block_with_witness(&client, vec![], parent_head.clone(), Default::default());
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header, extrinsics, witness);
call_validate_block(
call_validate_block(parent_head, block_data, Hash::random()).expect("Calls `validate_block`");
}
#[test]
#[should_panic(expected = "Calls `validate_block`: Other(\"Trap: Trap { kind: Unreachable }\")")]
fn check_inherent_fails_on_validate_block_as_expected() {
let _ = env_logger::try_init();
let (client, longest_chain) = create_test_client();
let parent_head = longest_chain.best_chain().expect("Best block exists");
let TestBlockData {
block,
witness,
validation_data,
} = build_block_with_witness(
&client,
vec![],
parent_head.clone(),
RelayStateSproofBuilder {
current_slot: 1337.into(),
..Default::default()
},
);
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header.clone(), extrinsics, witness);
let res_header = call_validate_block(
parent_head,
block_data,
Hash::random(),
validation_data.relay_parent_storage_root,
)
.expect("Calls `validate_block`");
assert_eq!(header, res_header);
}