The great refactor (#325)

* Move pallets to pallets folder and rename them

* Move genesis file to service

* Rename primitives to primitives-core

* Delete cumulus-runtime

* Move stuff to client folder and rename
This commit is contained in:
Bastian Köcher
2021-02-10 13:07:21 +01:00
committed by GitHub
parent a4998998a9
commit 119e0859b9
48 changed files with 436 additions and 547 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,198 @@
// 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 codec::{Encode, Decode};
use cumulus_primitives_core::{relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId};
use hash_db::{HashDB, EMPTY_PREFIX};
use sp_runtime::traits::HashFor;
use sp_state_machine::{Backend, TrieBackend};
use sp_trie::StorageProof;
use sp_std::vec::Vec;
/// A snapshot of some messaging related state of relay chain pertaining to the current parachain.
///
/// This data is essential for making sure that the parachain is aware of current resource use on
/// the relay chain and that the candidates produced for this parachain do not exceed any of these
/// limits.
#[derive(Clone, Encode, Decode)]
pub struct MessagingStateSnapshot {
/// The current message queue chain head for downward message queue.
///
/// If the value is absent on the relay chain this will be set to all zeros.
pub dmq_mqc_head: relay_chain::Hash,
/// The current capacity of the upward message queue of the current parachain on the relay chain.
///
/// The capacity is represented by a tuple that consist of the `count` of the messages and the
/// `total_size` expressed as the sum of byte sizes of all messages in the queue.
pub relay_dispatch_queue_size: (u32, u32),
/// Information about all the inbound HRMP channels.
///
/// These are structured as a list of tuples. The para id in the tuple specifies the sender
/// of the channel. Obviously, the recipient is the current parachain.
///
/// The channels are sorted by the sender para id ascension.
pub ingress_channels: Vec<(ParaId, AbridgedHrmpChannel)>,
/// Information about all the outbound HRMP channels.
///
/// These are structured as a list of tuples. The para id in the tuple specifies the recipient
/// of the channel. Obviously, the sender is the current parachain.
///
/// The channels are sorted by the recipient para id ascension.
pub egress_channels: Vec<(ParaId, AbridgedHrmpChannel)>,
}
#[derive(Debug)]
pub enum Error {
/// The provided proof was created against unexpected storage root.
RootMismatch,
/// The host configuration cannot be extracted.
Config(ReadEntryErr),
/// The DMQ MQC head cannot be extracted.
DmqMqcHead(ReadEntryErr),
/// Relay dispatch queue cannot be extracted.
RelayDispatchQueueSize(ReadEntryErr),
/// The hrmp inress channel index cannot be extracted.
HrmpIngressChannelIndex(ReadEntryErr),
/// The hrmp egress channel index cannot be extracted.
HrmpEgressChannelIndex(ReadEntryErr),
/// The channel identified by the sender and receiver cannot be extracted.
HrmpChannel(ParaId, ParaId, ReadEntryErr),
}
#[derive(Debug)]
pub enum ReadEntryErr {
/// The value cannot be extracted from the proof.
Proof,
/// The value cannot be decoded.
Decode,
/// The value is expected to be present on the relay chain, but it doesn't exist.
Absent,
}
/// Read an entry given by the key and try to decode it. If the value specified by the key according
/// to the proof is empty, the `fallback` value will be returned.
///
/// Returns `Err` in case the backend can't return the value under the specific key (likely due to
/// a malformed proof), in case the decoding fails, or in case where the value is empty in the relay
/// chain state and no fallback was provided.
fn read_entry<T, B>(backend: &B, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>
where
T: Decode,
B: Backend<HashFor<relay_chain::Block>>,
{
backend
.storage(key)
.map_err(|_| ReadEntryErr::Proof)?
.map(|raw_entry| T::decode(&mut &raw_entry[..]).map_err(|_| ReadEntryErr::Decode))
.transpose()?
.or(fallback)
.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(
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);
}
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,
)
.map_err(|read_err| Error::HrmpChannel(sender, 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: para_id,
recipient,
};
let hrmp_channel: AbridgedHrmpChannel = read_entry(
&backend,
&relay_chain::well_known_keys::hrmp_channels(channel_id),
None,
)
.map_err(|read_err| Error::HrmpChannel(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((
host_config,
MessagingStateSnapshot {
dmq_mqc_head,
relay_dispatch_queue_size,
ingress_channels,
egress_channels,
},
))
}
@@ -0,0 +1,490 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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/>.
//! The actual implementation of the validate block functionality.
use frame_executive::ExecuteBlock;
use sp_runtime::traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor};
use sp_std::{boxed::Box, vec::Vec};
use hash_db::{HashDB, EMPTY_PREFIX};
use polkadot_parachain::primitives::{HeadData, ValidationCode, ValidationParams, ValidationResult};
use codec::{Decode, Encode};
use cumulus_primitives_core::{
well_known_keys::{
HRMP_OUTBOUND_MESSAGES, HRMP_WATERMARK, NEW_VALIDATION_CODE, PROCESSED_DOWNWARD_MESSAGES,
UPWARD_MESSAGES, VALIDATION_DATA,
},
OutboundHrmpMessage, PersistedValidationData, UpwardMessage,
};
use sp_core::storage::{ChildInfo, TrackedStorageKey};
use sp_externalities::{
set_and_run_with_externalities, Error, Extension, ExtensionStore, Externalities,
};
use sp_std::any::{Any, TypeId};
use sp_trie::MemoryDB;
type StorageValue = Vec<u8>;
type StorageKey = Vec<u8>;
type Ext<'a, B> = sp_state_machine::Ext<
'a,
HashFor<B>,
NumberFor<B>,
sp_state_machine::TrieBackend<MemoryDB<HashFor<B>>, HashFor<B>>,
>;
fn with_externalities<F: FnOnce(&mut dyn Externalities) -> R, R>(f: F) -> R {
sp_externalities::with_externalities(f).expect("Environmental externalities not set.")
}
/// Implement `Encode` by forwarding the stored raw vec.
struct EncodeOpaqueValue(Vec<u8>);
impl Encode for EncodeOpaqueValue {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&self.0)
}
}
/// Validate a given parachain block on a validator.
#[doc(hidden)]
pub fn validate_block<B: BlockT, E: ExecuteBlock<B>>(params: ValidationParams) -> ValidationResult {
let block_data =
cumulus_primitives_core::ParachainBlockData::<B>::decode(&mut &params.block_data.0[..])
.expect("Invalid parachain block data");
let parent_head =
B::Header::decode(&mut &params.parent_head.0[..]).expect("Invalid parent head");
let (header, extrinsics, storage_proof) = block_data.deconstruct();
let head_data = HeadData(header.encode());
let block = B::new(header, extrinsics);
assert!(
parent_head.hash() == *block.header().parent_hash(),
"Invalid parent hash",
);
let db = storage_proof.into_memory_db();
let root = parent_head.state_root().clone();
if !HashDB::<HashFor<B>, _>::contains(&db, &root, EMPTY_PREFIX) {
panic!("Witness data does not contain given storage root.");
}
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 = WitnessExt::<B> {
inner: Ext::<B>::new(&mut overlay, &mut cache, &backend),
params: &params,
};
let _guard = (
// Replace storage calls with our own implementations
sp_io::storage::host_read.replace_implementation(host_storage_read),
sp_io::storage::host_set.replace_implementation(host_storage_set),
sp_io::storage::host_get.replace_implementation(host_storage_get),
sp_io::storage::host_exists.replace_implementation(host_storage_exists),
sp_io::storage::host_clear.replace_implementation(host_storage_clear),
sp_io::storage::host_root.replace_implementation(host_storage_root),
sp_io::storage::host_clear_prefix.replace_implementation(host_storage_clear_prefix),
sp_io::storage::host_changes_root.replace_implementation(host_storage_changes_root),
sp_io::storage::host_append.replace_implementation(host_storage_append),
sp_io::storage::host_next_key.replace_implementation(host_storage_next_key),
sp_io::storage::host_start_transaction
.replace_implementation(host_storage_start_transaction),
sp_io::storage::host_rollback_transaction
.replace_implementation(host_storage_rollback_transaction),
sp_io::storage::host_commit_transaction
.replace_implementation(host_storage_commit_transaction),
sp_io::default_child_storage::host_get
.replace_implementation(host_default_child_storage_get),
sp_io::default_child_storage::host_read
.replace_implementation(host_default_child_storage_read),
sp_io::default_child_storage::host_set
.replace_implementation(host_default_child_storage_set),
sp_io::default_child_storage::host_clear
.replace_implementation(host_default_child_storage_clear),
sp_io::default_child_storage::host_storage_kill
.replace_implementation(host_default_child_storage_storage_kill),
sp_io::default_child_storage::host_exists
.replace_implementation(host_default_child_storage_exists),
sp_io::default_child_storage::host_clear_prefix
.replace_implementation(host_default_child_storage_clear_prefix),
sp_io::default_child_storage::host_root
.replace_implementation(host_default_child_storage_root),
sp_io::default_child_storage::host_next_key
.replace_implementation(host_default_child_storage_next_key),
sp_io::offchain_index::host_set.replace_implementation(host_offchain_index_set),
sp_io::offchain_index::host_clear.replace_implementation(host_offchain_index_clear),
);
set_and_run_with_externalities(&mut ext, || {
E::execute_block(block);
});
// If in the course of block execution new validation code was set, insert
// its scheduled upgrade so we can validate that block number later.
let new_validation_code = overlay
.storage(NEW_VALIDATION_CODE)
.flatten()
.map(|slice| slice.to_vec())
.map(ValidationCode);
// Extract potential upward messages from the storage.
let upward_messages = match overlay.storage(UPWARD_MESSAGES).flatten() {
Some(encoded) => Vec::<UpwardMessage>::decode(&mut &encoded[..])
.expect("Upward messages vec is not correctly encoded in the storage!"),
None => Vec::new(),
};
let processed_downward_messages = overlay
.storage(PROCESSED_DOWNWARD_MESSAGES)
.flatten()
.map(|v| {
Decode::decode(&mut &v[..])
.expect("Processed downward message count is not correctly encoded in the storage")
})
.unwrap_or_default();
let validation_data: PersistedValidationData = overlay
.storage(VALIDATION_DATA)
.flatten()
.and_then(|v| Decode::decode(&mut &v[..]).ok())
.expect("`PersistedValidationData` is required to be placed into the storage!");
let horizontal_messages = match overlay.storage(HRMP_OUTBOUND_MESSAGES).flatten() {
Some(encoded) => Vec::<OutboundHrmpMessage>::decode(&mut &encoded[..])
.expect("Outbound HRMP messages vec is not correctly encoded in the storage!"),
None => Vec::new(),
};
let hrmp_watermark = overlay
.storage(HRMP_WATERMARK)
.flatten()
.map(|v| Decode::decode(&mut &v[..]).expect("HRMP watermark is not encoded correctly"))
.unwrap_or(validation_data.relay_parent_number);
ValidationResult {
head_data,
new_validation_code,
upward_messages,
processed_downward_messages,
horizontal_messages,
hrmp_watermark,
}
}
/// The storage implementation used when validating a block that is using the
/// witness data as source.
struct WitnessExt<'a, B: BlockT> {
inner: Ext<'a, B>,
params: &'a ValidationParams,
}
impl<'a, B: BlockT> WitnessExt<'a, B> {
/// Checks that the encoded `PersistedValidationData` in `data` is correct.
///
/// Should be removed with: https://github.com/paritytech/cumulus/issues/217
/// When removed `WitnessExt` could also be removed.
fn check_validation_data(&self, mut data: &[u8]) {
let validation_data =
PersistedValidationData::decode(&mut data).expect("Invalid `PersistedValidationData`");
assert_eq!(self.params.parent_head, validation_data.parent_head,);
assert_eq!(
self.params.relay_parent_number,
validation_data.relay_parent_number,
);
assert_eq!(
self.params.relay_parent_storage_root,
validation_data.relay_parent_storage_root,
);
}
}
impl<'a, B: BlockT> Externalities for WitnessExt<'a, B> {
fn storage(&self, key: &[u8]) -> Option<StorageValue> {
self.inner.storage(key)
}
fn set_offchain_storage(&mut self, key: &[u8], value: Option<&[u8]>) {
self.inner.set_offchain_storage(key, value)
}
fn storage_hash(&self, key: &[u8]) -> Option<Vec<u8>> {
self.inner.storage_hash(key)
}
fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageValue> {
self.inner.child_storage(child_info, key)
}
fn child_storage_hash(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
self.inner.child_storage_hash(child_info, key)
}
fn exists_storage(&self, key: &[u8]) -> bool {
self.inner.exists_storage(key)
}
fn exists_child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> bool {
self.inner.exists_child_storage(child_info, key)
}
fn next_storage_key(&self, key: &[u8]) -> Option<StorageKey> {
self.inner.next_storage_key(key)
}
fn next_child_storage_key(&self, child_info: &ChildInfo, key: &[u8]) -> Option<StorageKey> {
self.inner.next_child_storage_key(child_info, key)
}
fn place_storage(&mut self, key: StorageKey, value: Option<StorageValue>) {
if let Some(value) = value.as_ref() {
if key == VALIDATION_DATA {
self.check_validation_data(value);
}
}
self.inner.place_storage(key, value)
}
fn place_child_storage(
&mut self,
child_info: &ChildInfo,
key: StorageKey,
value: Option<StorageValue>,
) {
self.inner.place_child_storage(child_info, key, value)
}
fn kill_child_storage(&mut self, child_info: &ChildInfo, limit: Option<u32>) -> bool {
self.inner.kill_child_storage(child_info, limit)
}
fn clear_prefix(&mut self, prefix: &[u8]) {
self.inner.clear_prefix(prefix)
}
fn clear_child_prefix(&mut self, child_info: &ChildInfo, prefix: &[u8]) {
self.inner.clear_child_prefix(child_info, prefix)
}
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.inner.storage_append(key, value)
}
fn storage_root(&mut self) -> Vec<u8> {
self.inner.storage_root()
}
fn child_storage_root(&mut self, child_info: &ChildInfo) -> Vec<u8> {
self.inner.child_storage_root(child_info)
}
fn storage_changes_root(&mut self, parent_hash: &[u8]) -> Result<Option<Vec<u8>>, ()> {
self.inner.storage_changes_root(parent_hash)
}
fn storage_start_transaction(&mut self) {
self.inner.storage_start_transaction()
}
fn storage_rollback_transaction(&mut self) -> Result<(), ()> {
self.inner.storage_rollback_transaction()
}
fn storage_commit_transaction(&mut self) -> Result<(), ()> {
self.inner.storage_commit_transaction()
}
fn wipe(&mut self) {
self.inner.wipe()
}
fn commit(&mut self) {
self.inner.commit()
}
fn read_write_count(&self) -> (u32, u32, u32, u32) {
self.inner.read_write_count()
}
fn reset_read_write_count(&mut self) {
self.inner.reset_read_write_count()
}
fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
self.inner.get_whitelist()
}
fn set_whitelist(&mut self, new: Vec<TrackedStorageKey>) {
self.inner.set_whitelist(new)
}
}
impl<'a, B: BlockT> ExtensionStore for WitnessExt<'a, B> {
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
self.inner.extension_by_type_id(type_id)
}
fn register_extension_with_type_id(
&mut self,
type_id: TypeId,
extension: Box<dyn Extension>,
) -> Result<(), Error> {
self.inner
.register_extension_with_type_id(type_id, extension)
}
fn deregister_extension_by_type_id(&mut self, type_id: TypeId) -> Result<(), Error> {
self.inner.deregister_extension_by_type_id(type_id)
}
}
fn host_storage_read(key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option<u32> {
match with_externalities(|ext| ext.storage(key)) {
Some(value) => {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = sp_std::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
Some(value.len() as u32)
}
None => None,
}
}
fn host_storage_set(key: &[u8], value: &[u8]) {
with_externalities(|ext| ext.place_storage(key.to_vec(), Some(value.to_vec())))
}
fn host_storage_get(key: &[u8]) -> Option<Vec<u8>> {
with_externalities(|ext| ext.storage(key).clone())
}
fn host_storage_exists(key: &[u8]) -> bool {
with_externalities(|ext| ext.exists_storage(key))
}
fn host_storage_clear(key: &[u8]) {
with_externalities(|ext| ext.place_storage(key.to_vec(), None))
}
fn host_storage_root() -> Vec<u8> {
with_externalities(|ext| ext.storage_root())
}
fn host_storage_clear_prefix(prefix: &[u8]) {
with_externalities(|ext| ext.clear_prefix(prefix))
}
fn host_storage_changes_root(parent_hash: &[u8]) -> Option<Vec<u8>> {
with_externalities(|ext| ext.storage_changes_root(parent_hash).ok().flatten())
}
fn host_storage_append(key: &[u8], value: Vec<u8>) {
with_externalities(|ext| ext.storage_append(key.to_vec(), value))
}
fn host_storage_next_key(key: &[u8]) -> Option<Vec<u8>> {
with_externalities(|ext| ext.next_storage_key(key))
}
fn host_storage_start_transaction() {
with_externalities(|ext| ext.storage_start_transaction())
}
fn host_storage_rollback_transaction() {
with_externalities(|ext| ext.storage_rollback_transaction().ok())
.expect("No open transaction that can be rolled back.");
}
fn host_storage_commit_transaction() {
with_externalities(|ext| ext.storage_commit_transaction().ok())
.expect("No open transaction that can be committed.");
}
fn host_default_child_storage_get(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.child_storage(&child_info, key))
}
fn host_default_child_storage_read(
storage_key: &[u8],
key: &[u8],
value_out: &mut [u8],
value_offset: u32,
) -> Option<u32> {
let child_info = ChildInfo::new_default(storage_key);
match with_externalities(|ext| ext.child_storage(&child_info, key)) {
Some(value) => {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = sp_std::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
Some(value.len() as u32)
}
None => None,
}
}
fn host_default_child_storage_set(storage_key: &[u8], key: &[u8], value: &[u8]) {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| {
ext.place_child_storage(&child_info, key.to_vec(), Some(value.to_vec()))
})
}
fn host_default_child_storage_clear(storage_key: &[u8], key: &[u8]) {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.place_child_storage(&child_info, key.to_vec(), None))
}
fn host_default_child_storage_storage_kill(storage_key: &[u8], limit: Option<u32>) -> bool {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.kill_child_storage(&child_info, limit))
}
fn host_default_child_storage_exists(storage_key: &[u8], key: &[u8]) -> bool {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.exists_child_storage(&child_info, key))
}
fn host_default_child_storage_clear_prefix(storage_key: &[u8], prefix: &[u8]) {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.clear_child_prefix(&child_info, prefix))
}
fn host_default_child_storage_root(storage_key: &[u8]) -> Vec<u8> {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.child_storage_root(&child_info))
}
fn host_default_child_storage_next_key(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
with_externalities(|ext| ext.next_child_storage_key(&child_info, key))
}
fn host_offchain_index_set(_key: &[u8], _value: &[u8]) {}
fn host_offchain_index_clear(_key: &[u8]) {}
@@ -0,0 +1,85 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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/>.
//! A module that enables a runtime to work as parachain.
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub mod implementation;
#[cfg(test)]
mod tests;
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub use polkadot_parachain;
/// 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 block and the block executor.
///
/// # Example
///
/// ```
/// struct Block;
/// struct BlockExecutor;
///
/// cumulus_pallet_parachain_system::register_validate_block!(Block, BlockExecutor);
///
/// # fn main() {}
/// ```
#[macro_export]
macro_rules! register_validate_block {
($block:ty, $block_executor:ty) => {
$crate::register_validate_block_impl!($block, $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 {
($block: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::<
$block,
$block_executor,
>(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 {
($block:ty, $block_executor:ty) => {};
}
@@ -0,0 +1,186 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 cumulus_primitives_core::{PersistedValidationData, ParachainBlockData};
use cumulus_test_client::{
runtime::{Block, Hash, Header, UncheckedExtrinsic, WASM_BINARY},
transfer, Client, DefaultTestClientBuilderExt, InitBlockBuilder, LongestChain,
TestClientBuilder, TestClientBuilderExt,
};
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use polkadot_parachain::primitives::{BlockData, HeadData, ValidationParams, ValidationResult};
use sc_executor::{
error::Result, sp_wasm_interface::HostFunctions, WasmExecutionMethod, WasmExecutor,
};
use sp_blockchain::HeaderBackend;
use sp_consensus::SelectChain;
use sp_core::traits::CallInWasm;
use sp_io::TestExternalities;
use sp_keyring::AccountKeyring::*;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT},
};
use codec::{Decode, Encode};
fn call_validate_block(
parent_head: Header,
block_data: ParachainBlockData<Block>,
relay_parent_storage_root: Hash,
) -> Result<Header> {
let mut ext = TestExternalities::default();
let mut ext_ext = ext.ext();
let params = ValidationParams {
block_data: BlockData(block_data.encode()),
parent_head: HeadData(parent_head.encode()),
relay_parent_number: 1,
relay_parent_storage_root,
}
.encode();
let executor = WasmExecutor::new(
WasmExecutionMethod::Interpreted,
Some(1024),
sp_io::SubstrateHostFunctions::host_functions(),
1,
);
executor
.call_in_wasm(
&WASM_BINARY.expect("You need to build the WASM binaries to run the tests!"),
None,
"validate_block",
&params,
&mut ext_ext,
sp_core::traits::MissingHostFunctions::Disallow,
)
.map(|v| ValidationResult::decode(&mut &v[..]).expect("Decode `ValidationResult`."))
.map(|v| Header::decode(&mut &v.head_data.0[..]).expect("Decode `Header`."))
.map_err(|err| err.into())
}
fn create_test_client() -> (Client, LongestChain) {
TestClientBuilder::new()
// NOTE: this allows easier debugging
.set_execution_strategy(sc_client_api::ExecutionStrategy::NativeWhenPossible)
.build_with_longest_chain()
}
struct TestBlockData {
block: Block,
witness: sp_trie::StorageProof,
relay_parent_storage_root: Hash,
}
fn build_block_with_witness(
client: &Client,
extra_extrinsics: Vec<UncheckedExtrinsic>,
parent_head: Header,
) -> 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 builder = client.init_block_builder_at(
&block_id,
Some(PersistedValidationData {
relay_parent_number: 1,
parent_head: parent_head.encode().into(),
..Default::default()
}),
sproof_builder,
);
extra_extrinsics
.into_iter()
.for_each(|e| builder.push(e).unwrap());
let built_block = builder.build().expect("Creates block");
TestBlockData {
block: built_block.block,
witness: built_block
.proof
.expect("We enabled proof recording before."),
relay_parent_storage_root,
}
}
#[test]
fn validate_block_no_extra_extrinsics() {
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,
relay_parent_storage_root,
} = build_block_with_witness(&client, vec![], parent_head.clone());
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header.clone(), extrinsics, witness);
let res_header = call_validate_block(parent_head, block_data, relay_parent_storage_root)
.expect("Calls `validate_block`");
assert_eq!(header, res_header);
}
#[test]
fn validate_block_with_extra_extrinsics() {
let _ = env_logger::try_init();
let (client, longest_chain) = create_test_client();
let parent_head = longest_chain.best_chain().expect("Best block exists");
let extra_extrinsics = vec![
transfer(&client, Alice, Bob, 69),
transfer(&client, Bob, Charlie, 100),
transfer(&client, Charlie, Alice, 500),
];
let TestBlockData {
block,
witness,
relay_parent_storage_root,
} = build_block_with_witness(&client, extra_extrinsics, parent_head.clone());
let (header, extrinsics) = block.deconstruct();
let block_data = ParachainBlockData::new(header.clone(), extrinsics, witness);
let res_header = call_validate_block(parent_head, block_data, relay_parent_storage_root)
.expect("Calls `validate_block`");
assert_eq!(header, res_header);
}
#[test]
#[should_panic(expected = "Calls `validate_block`: Other(\"Trap: Trap { kind: Unreachable }\")")]
fn validate_block_invalid_parent_hash() {
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,
relay_parent_storage_root,
} = build_block_with_witness(&client, vec![], parent_head.clone());
let (mut header, extrinsics) = block.deconstruct();
header.set_parent_hash(Hash::from_low_u64_be(1));
let block_data = ParachainBlockData::new(header, extrinsics, witness);
call_validate_block(parent_head, block_data, relay_parent_storage_root)
.expect("Calls `validate_block`");
}