diff --git a/Cargo.lock b/Cargo.lock index 4c94515c8..4961c16d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "approx" @@ -3044,11 +3044,37 @@ dependencies = [ "primitive-types", "scale-info", "sp-consensus-aura", + "sp-inherents", "sp-io", "sp-runtime", "sp-trie", ] +[[package]] +name = "ismp-parachain-inherent" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "ismp", + "ismp-parachain", + "ismp-parachain-runtime-api", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-inherents", + "sp-runtime", +] + +[[package]] +name = "ismp-parachain-runtime-api" +version = "0.1.0" +dependencies = [ + "sp-api", +] + [[package]] name = "ismp-primitives" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 062f308e7..554140fef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,12 @@ [workspace] resolver = "2" - members = [ "pallet-ismp/rpc", "pallet-ismp/runtime-api", "pallet-ismp/primitives", "pallet-ismp", - "parachain-consensus", + "parachain/inherent", + "parachain/runtime-api", + "parachain", "ismp-assets" ] diff --git a/pallet-ismp/src/lib.rs b/pallet-ismp/src/lib.rs index 9ed1d8ff3..cbf0af969 100644 --- a/pallet-ismp/src/lib.rs +++ b/pallet-ismp/src/lib.rs @@ -36,22 +36,26 @@ pub use mmr::utils::NodesUtils; use crate::host::Host; use codec::{Decode, Encode}; use core::time::Duration; -use frame_support::{log::debug, traits::Get, RuntimeDebug}; +use frame_support::{dispatch::DispatchResult, log::debug, traits::Get, RuntimeDebug}; use ismp_rs::{ consensus::{ConsensusClientId, StateMachineId}, + handlers::{handle_incoming_message, MessageResult}, host::StateMachine, messaging::CreateConsensusClient, router::{Request, Response}, }; use sp_core::{offchain::StorageKind, H256}; // Re-export pallet items so that they can be accessed from the crate namespace. -use crate::primitives::{IsmpDispatch, IsmpMessage}; +use crate::{ + errors::HandlingError, + mmr::mmr::Mmr, + primitives::{IsmpDispatch, IsmpMessage}, +}; use ismp_primitives::{ mmr::{DataOrHash, Leaf, LeafIndex, NodeIndex}, LeafIndexQuery, }; -use ismp_rs::{host::ISMPHost, router::ISMPRouter}; -use mmr::mmr::Mmr; +use ismp_rs::{host::ISMPHost, messaging::Message, router::ISMPRouter}; pub use pallet::*; use sp_std::prelude::*; @@ -74,7 +78,7 @@ pub mod pallet { use ismp_primitives::mmr::{LeafIndex, NodeIndex}; use ismp_rs::{ consensus::{ConsensusClientId, StateCommitment, StateMachineHeight, StateMachineId}, - handlers::{self, handle_incoming_message, MessageResult}, + handlers::{self}, host::StateMachine, messaging::Message, router::ISMPRouter, @@ -253,65 +257,8 @@ pub mod pallet { #[frame_support::transactional] pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResult { let _ = ensure_signed(origin)?; - // Define a host - let host = Host::::default(); - let mut errors: Vec = vec![]; - - for message in messages { - match handle_incoming_message(&host, message) { - Ok(MessageResult::ConsensusMessage(res)) => { - // check if this is a trusted state machine - let is_trusted_state_machine = host - .challenge_period(res.consensus_client_id.clone()) == - Duration::from_secs(0); - - if is_trusted_state_machine { - for (_, latest_height) in res.state_updates.into_iter() { - Self::deposit_event(Event::::StateMachineUpdated { - state_machine_id: latest_height.id, - latest_height: latest_height.height, - }) - } - } else { - if let Some(pending_updates) = - ConsensusUpdateResults::::get(res.consensus_client_id) - { - for (_, latest_height) in pending_updates.into_iter() { - Self::deposit_event(Event::::StateMachineUpdated { - state_machine_id: latest_height.id, - latest_height: latest_height.height, - }) - } - } - Self::deposit_event(Event::::ChallengePeriodStarted { - consensus_client_id: res.consensus_client_id, - state_machines: res.state_updates.clone(), - }); - - // Store the new update result that have just entered the challenge - // period - ConsensusUpdateResults::::insert( - res.consensus_client_id, - res.state_updates, - ); - } - } - Ok(_) => { - // Do nothing, event should have been deposited by the ismp router - } - Err(err) => { - errors.push(err.into()); - } - } - } - - if !errors.is_empty() { - debug!(target: "ismp-rust", "Handling Errors {:?}", errors); - Self::deposit_event(Event::::HandlingErrors { errors }) - } - - Ok(()) + Self::handle_messages(messages) } /// Create consensus clients @@ -399,6 +346,69 @@ where mmr.generate_proof(leaf_indices) } + /// Provides a way to handle messages. + pub fn handle_messages(messages: Vec) -> DispatchResult { + // Define a host + let host = Host::::default(); + let mut errors: Vec = vec![]; + + for message in messages { + match handle_incoming_message(&host, message) { + Ok(MessageResult::ConsensusMessage(res)) => { + // check if this is a trusted state machine + let is_trusted_state_machine = host + .challenge_period(res.consensus_client_id.clone()) == + Duration::from_secs(0); + + if is_trusted_state_machine { + for (_, latest_height) in res.state_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } else { + if let Some(pending_updates) = + ConsensusUpdateResults::::get(res.consensus_client_id) + { + for (_, latest_height) in pending_updates.into_iter() { + Self::deposit_event(Event::::StateMachineUpdated { + state_machine_id: latest_height.id, + latest_height: latest_height.height, + }) + } + } + + Self::deposit_event(Event::::ChallengePeriodStarted { + consensus_client_id: res.consensus_client_id, + state_machines: res.state_updates.clone(), + }); + + // Store the new update result that have just entered the challenge + // period + ConsensusUpdateResults::::insert( + res.consensus_client_id, + res.state_updates, + ); + } + } + Ok(_) => { + // Do nothing, event should have been deposited by the ismp router + } + Err(err) => { + errors.push(err.into()); + } + } + } + + if !errors.is_empty() { + debug!(target: "ismp-rust", "Handling Errors {:?}", errors); + Self::deposit_event(Event::::HandlingErrors { errors }) + } + + Ok(()) + } + /// Return the on-chain MMR root hash. pub fn mmr_root() -> ::Hash { Self::mmr_root_hash() diff --git a/parachain-consensus/src/lib.rs b/parachain-consensus/src/lib.rs deleted file mode 100644 index 38158df37..000000000 --- a/parachain-consensus/src/lib.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// 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. - -//! ISMP Parachain Consensus Client -//! -//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; -extern crate core; - -pub mod consensus; - -use cumulus_primitives_core::relay_chain; -pub use pallet::*; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use cumulus_primitives_core::relay_chain; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_ismp::Config + parachain_system::Config - { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. - #[pallet::storage] - #[pallet::getter(fn relay_chain_state)] - pub type RelayChainState = - StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; - - #[pallet::event] - pub enum Event {} - - // Pallet implements [`Hooks`] trait to define some logic to execute in some context. - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_finalize(_n: T::BlockNumber) { - let state = RelaychainDataProvider::::current_relay_chain_state(); - if !RelayChainState::::contains_key(state.number) { - RelayChainState::::insert(state.number, state.state_root); - - let digest = sp_runtime::generic::DigestItem::Consensus( - consensus::PARACHAIN_CONSENSUS_ID, - state.number.encode(), - ); - >::deposit_log(digest); - } - } - } - - #[pallet::genesis_config] - pub struct GenesisConfig {} - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig {} - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - // insert empty bytes - pallet_ismp::ConsensusStates::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - Vec::::new(), - ); - - pallet_ismp::ConsensusClientUpdateTime::::insert( - consensus::PARACHAIN_CONSENSUS_ID, - // parachains have no challenge period - 0, - ); - } - } -} - -/// Interface that exposes the relay chain state roots. -pub trait RelayChainOracle { - /// Returns the state root for a given height if it exists. - fn state_root(height: relay_chain::BlockNumber) -> Option; -} - -impl RelayChainOracle for Pallet { - fn state_root(height: relay_chain::BlockNumber) -> Option { - RelayChainState::::get(height) - } -} diff --git a/parachain-consensus/Cargo.toml b/parachain/Cargo.toml similarity index 94% rename from parachain-consensus/Cargo.toml rename to parachain/Cargo.toml index f3022724b..067275931 100644 --- a/parachain-consensus/Cargo.toml +++ b/parachain/Cargo.toml @@ -22,6 +22,7 @@ ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } +sp-inherents = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.40" } @@ -52,6 +53,7 @@ std = [ "sp-consensus-aura/std", "sp-runtime/std", "sp-io/std", + "sp-inherents/std", "primitive-types/std", "ismp-primitives/std", "pallet-ismp/std", diff --git a/parachain/inherent/Cargo.toml b/parachain/inherent/Cargo.toml new file mode 100644 index 000000000..c366eaed5 --- /dev/null +++ b/parachain/inherent/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "ismp-parachain-inherent" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +async-trait = { version = "0.1.63" } +codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] } +anyhow = "1.0.57" + +# Substrate +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } + +# Cumulus +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.400" } + +# polytope-labs +ismp = { git = "ssh://git@github.com/polytope-labs/ismp-rs.git", branch = "main" } +ismp-parachain = { path = "../" } +ismp-parachain-runtime-api = { path = "../runtime-api" } \ No newline at end of file diff --git a/parachain/inherent/src/lib.rs b/parachain/inherent/src/lib.rs new file mode 100644 index 000000000..89f792e77 --- /dev/null +++ b/parachain/inherent/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright (C) 2023 Polytope Labs. +// 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. + +//! ISMP Parachain Consensus Inherent Provider +//! +//! This exports the inherent provider for including ISMP parachain consensus updates as block +//! inherents. + +use codec::Encode; +use cumulus_primitives_core::PersistedValidationData; +use cumulus_relay_chain_interface::{PHash, RelayChainInterface}; +use ismp::messaging::ConsensusMessage; +use ismp_parachain::consensus::{parachain_header_storage_key, ParachainConsensusProof}; +use ismp_parachain_runtime_api::IsmpParachainApi; +use sp_runtime::traits::Block as BlockT; +use std::sync::Arc; + +/// Implements [`InherentDataProvider`] for providing parachain consensus updates as inherents. +pub struct ConsensusInherentProvider(Option); + +impl ConsensusInherentProvider { + /// Create the [`ConsensusInherentProvider`] at the given `relay_parent`. + pub async fn create( + client: Arc, + relay_parent: PHash, + relay_chain_interface: &impl RelayChainInterface, + validation_data: PersistedValidationData, + ) -> Result + where + C: sp_api::ProvideRuntimeApi + sp_blockchain::HeaderBackend, + C::Api: IsmpParachainApi, + B: BlockT, + { + let head = client.info().best_hash; + let para_ids = client.runtime_api().para_ids(head)?; + + if para_ids.is_empty() { + return Ok(ConsensusInherentProvider(None)) + } + + let keys = para_ids.iter().map(|id| parachain_header_storage_key(*id).0).collect(); + let storage_proof = relay_chain_interface + .prove_read(relay_parent, &keys) + .await? + .into_iter_nodes() + .collect(); + + let consensus_proof = ParachainConsensusProof { + para_ids, + relay_height: validation_data.relay_parent_number, + storage_proof, + }; + let message = ConsensusMessage { + consensus_client_id: ismp_parachain::consensus::PARACHAIN_CONSENSUS_ID, + consensus_proof: consensus_proof.encode(), + }; + + Ok(ConsensusInherentProvider(Some(message))) + } +} + +#[async_trait::async_trait] +impl sp_inherents::InherentDataProvider for ConsensusInherentProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + if let Some(ref message) = self.0 { + inherent_data.put_data(ismp_parachain::INHERENT_IDENTIFIER, message)?; + } + + Ok(()) + } + + async fn try_handle_error( + &self, + _: &sp_inherents::InherentIdentifier, + _: &[u8], + ) -> Option> { + None + } +} diff --git a/parachain/runtime-api/Cargo.toml b/parachain/runtime-api/Cargo.toml new file mode 100644 index 000000000..e96f50508 --- /dev/null +++ b/parachain/runtime-api/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ismp-parachain-runtime-api" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } + +[features] +default = ["std"] +std = ["sp-api/std"] diff --git a/parachain/runtime-api/src/lib.rs b/parachain/runtime-api/src/lib.rs new file mode 100644 index 000000000..a3e2ed908 --- /dev/null +++ b/parachain/runtime-api/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Polytope Labs. +// 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. + +//! Runtime API for parachains. + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::vec::Vec; + +sp_api::decl_runtime_apis! { + /// Ismp Parachain Runtime Apis + pub trait IsmpParachainApi { + /// Return all the para_ids this runtime is interested in. Used by the inherent provider + fn para_ids() -> Vec; + } +} diff --git a/parachain-consensus/src/consensus.rs b/parachain/src/consensus.rs similarity index 90% rename from parachain-consensus/src/consensus.rs rename to parachain/src/consensus.rs index ce8135a4e..5bf10f086 100644 --- a/parachain-consensus/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -33,6 +33,7 @@ use ismp::{ use ismp_primitives::mmr::{DataOrHash, Leaf, MmrHasher}; use merkle_mountain_range::MerkleProof; use pallet_ismp::host::Host; +use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use primitive_types::H256; use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; use sp_runtime::{ @@ -105,7 +106,7 @@ const SLOT_DURATION: u64 = 12_000; impl ConsensusClient for ParachainConsensusClient where R: RelayChainOracle, - T: pallet_ismp::Config, + T: pallet_ismp::Config + super::Config, T::BlockNumber: Into, T::Hash: From, { @@ -122,29 +123,40 @@ where )) })?; - let root = R::state_root(update.relay_height).ok_or_else(|| { - Error::ImplementationSpecific(format!( - "Cannot find relay chain height: {}", - update.relay_height - )) - })?; + // first check our oracle's registry + let root = R::state_root(update.relay_height) + // not in our registry? ask parachain_system. + .or_else(|| { + let state = RelaychainDataProvider::::current_relay_chain_state(); + + if state.number == update.relay_height { + Some(state.state_root) + } else { + None + } + }) + // well, we couldn't find it + .ok_or_else(|| { + Error::ImplementationSpecific(format!( + "Cannot find relay chain height: {}", + update.relay_height + )) + })?; let storage_proof = StorageProof::new(update.storage_proof); let mut intermediates = vec![]; - for id in update.para_ids { - let full_key = parachain_header_storage_key(id); - let header = read_proof_check::( - &root, - storage_proof.clone(), - vec![full_key.as_ref()], - ) - .map_err(|e| { + let keys = update.para_ids.iter().map(|id| parachain_header_storage_key(*id).0); + let headers = + read_proof_check::(&root, storage_proof, keys).map_err(|e| { Error::ImplementationSpecific(format!("Error verifying parachain header {e:?}",)) - })? - .remove(full_key.as_ref()) - .flatten() - .ok_or_else(|| { + })?; + + for (key, header) in headers { + let id = codec::Decode::decode(&mut &key[(key.len() - 4)..]).map_err(|e| { + Error::ImplementationSpecific(format!("Error decoding parachain header: {e}")) + })?; + let header = header.ok_or_else(|| { Error::ImplementationSpecific(format!( "Cannot find parachain header for ParaId({id})", )) diff --git a/parachain/src/lib.rs b/parachain/src/lib.rs new file mode 100644 index 000000000..fc547f302 --- /dev/null +++ b/parachain/src/lib.rs @@ -0,0 +1,245 @@ +// Copyright (C) 2023 Polytope Labs. +// 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. + +//! ISMP Parachain Consensus Client +//! +//! This allows parachains communicate over ISMP leveraging the relay chain as a consensus oracle. +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +extern crate core; + +pub mod consensus; + +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::relay_chain; +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use cumulus_primitives_core::relay_chain; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use ismp::{ + consensus::StateMachineId, + host::StateMachine, + messaging::{ConsensusMessage, Message}, + }; + use parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; + use primitive_types::H256; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_ismp::Config + parachain_system::Config + { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + /// Mapping of relay chain heights to it's state root. Gotten from parachain-system. + #[pallet::storage] + #[pallet::getter(fn relay_chain_state)] + pub type RelayChainState = + StorageMap<_, Blake2_128Concat, relay_chain::BlockNumber, relay_chain::Hash, OptionQuery>; + + /// Tracks whether we've already seen the `update_parachain_consensus` inherent + #[pallet::storage] + pub type ConsensusUpdated = StorageValue<_, bool>; + + /// Tracks whether we've already seen the `update_parachain_consensus` inherent + #[pallet::storage] + pub type Parachains = StorageMap<_, Identity, u32, ()>; + + #[pallet::event] + pub enum Event {} + + #[pallet::call] + impl Pallet + where + ::Hash: From, + { + /// Rather than users manually submitting consensus updates for sibling parachains, we + /// instead make it the responsibility of the block builder to insert the consensus + /// updates as an inherent. + #[pallet::call_index(0)] + #[pallet::weight((0, DispatchClass::Mandatory))] + pub fn update_parachain_consensus( + origin: OriginFor, + data: ConsensusMessage, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + assert!( + !>::exists(), + "ValidationData must be updated only once in a block", + ); + + assert_eq!( + data.consensus_client_id, + consensus::PARACHAIN_CONSENSUS_ID, + "Only parachain consensus updates should be passed in the inherents!" + ); + + pallet_ismp::Pallet::::handle_messages(vec![Message::Consensus(data)])?; + + Ok(Pays::No.into()) + } + + /// Add some new parachains to the list of parachains we care about + #[pallet::call_index(1)] + #[pallet::weight(0)] // todo: fix weight + pub fn add_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { + ensure_root(origin)?; + for id in para_ids { + Parachains::::insert(id, ()); + } + + Ok(()) + } + + /// Remove some parachains from the list of parachains we care about + #[pallet::call_index(2)] + #[pallet::weight(0)] // todo: fix weight + pub fn remove_parachain(origin: OriginFor, para_ids: Vec) -> DispatchResult { + ensure_root(origin)?; + for id in para_ids { + Parachains::::remove(id); + } + + Ok(()) + } + } + + // Pallet implements [`Hooks`] trait to define some logic to execute in some context. + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_finalize(_n: T::BlockNumber) { + let state = RelaychainDataProvider::::current_relay_chain_state(); + if !RelayChainState::::contains_key(state.number) { + RelayChainState::::insert(state.number, state.state_root); + + let digest = sp_runtime::generic::DigestItem::Consensus( + consensus::PARACHAIN_CONSENSUS_ID, + state.number.encode(), + ); + + >::deposit_log(digest); + } + } + + fn on_initialize(_n: T::BlockNumber) -> Weight { + // kill the storage, since this is the beginning of a new block. + ConsensusUpdated::::kill(); + + Weight::from_parts(0, 0) + } + } + + /// The identifier for the parachain consensus update inherent. + pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"paraismp"; + + #[pallet::inherent] + impl ProvideInherent for Pallet + where + ::Hash: From, + { + type Call = Call; + type Error = sp_inherents::MakeFatalError<()>; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(data: &InherentData) -> Option { + let data: ConsensusMessage = + data.get_data(&Self::INHERENT_IDENTIFIER).ok().flatten()?; + + Some(Call::update_parachain_consensus { data }) + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::update_parachain_consensus { .. }) + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig { + /// List of parachains to track at genesis + pub parachains: Vec, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { parachains: vec![] } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + // insert empty bytes + pallet_ismp::ConsensusStates::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + Vec::::new(), + ); + + pallet_ismp::ConsensusClientUpdateTime::::insert( + consensus::PARACHAIN_CONSENSUS_ID, + // parachains have no challenge period + 0, + ); + + // insert the parachain ids + for id in &self.parachains { + Parachains::::insert(id, ()); + + let state_id = match T::StateMachine::get() { + StateMachine::Polkadot(_) => StateMachine::Polkadot(*id), + StateMachine::Kusama(_) => StateMachine::Kusama(*id), + _ => panic!("State machine should be configured as a parachain!"), + }; + + // insert the "latest" parachain height + pallet_ismp::LatestStateMachineHeight::::insert( + StateMachineId { + consensus_client: consensus::PARACHAIN_CONSENSUS_ID, + state_id, + }, + 0, + ); + } + } + } +} + +impl Pallet { + /// Returns the list of parachains who's consensus updates will be inserted by the inherent + /// data provider + pub fn para_ids() -> Vec { + Parachains::::iter_keys().collect() + } +} + +/// Interface that exposes the relay chain state roots. +pub trait RelayChainOracle { + /// Returns the state root for a given height if it exists. + fn state_root(height: relay_chain::BlockNumber) -> Option; +} + +impl RelayChainOracle for Pallet { + fn state_root(height: relay_chain::BlockNumber) -> Option { + RelayChainState::::get(height) + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 99c6e11a1..e26d01035 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,3 @@ [toolchain] -channel = "1.66" +channel = "nightly-2022-11-16" +components = [ "rustfmt" ] \ No newline at end of file