diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 41f9e8a67..23b550c92 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -52,7 +52,7 @@ mod storage_types; // crates.io use finality_grandpa::voter_set::VoterSet; // darwinia-network -use bp_header_chain::{justification::GrandpaJustification, InitializationData}; +use bp_header_chain::{justification::GrandpaJustification, HeaderChain, InitializationData}; use bp_runtime::{ BlockNumberOf, BoundedStorageValue, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule, }; @@ -67,6 +67,8 @@ use sp_std::{boxed::Box, convert::TryInto}; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; +/// Bridged chain from the pallet configuration. +pub type BridgedChain = >::BridgedChain; /// Block number of the bridged chain. pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; /// Block hash of the bridged chain. @@ -385,8 +387,6 @@ pub mod pallet { TooManyRequests, /// The header being imported is older than the best finalized header known to the pallet. OldHeader, - /// The header is unknown to the pallet. - UnknownHeader, /// The scheduled authority set change found in the header is unsupported by the pallet. /// /// This is the case for non-standard (e.g forced) authority set changes. @@ -395,8 +395,6 @@ pub mod pallet { NotInitialized, /// The pallet has already been initialized. AlreadyInitialized, - /// The storage proof doesn't contains storage root. So it is invalid for given header. - StorageRootMismatch, /// Too many authorities in the set. TooManyAuthoritiesInSet, /// Too large header. @@ -584,9 +582,6 @@ pub use pallet::*; impl, I: 'static> Pallet { /// Get the best finalized header the pallet knows of. - /// - /// Returns a dummy header if there is no best header. This can only happen - /// if the pallet has not been initialized yet. pub fn best_finalized() -> Option> { let (_, hash) = >::get()?; >::get(hash).map(|h| h.into_inner()) @@ -596,21 +591,14 @@ impl, I: 'static> Pallet { pub fn is_known_header(hash: BridgedBlockHash) -> bool { >::contains_key(hash) } +} - /// Verify that the passed storage proof is valid, given it is crafted using - /// known finalized header. If the proof is valid, then the `parse` callback - /// is called and the function returns its result. - pub fn parse_finalized_storage_proof( - hash: BridgedBlockHash, - storage_proof: sp_trie::StorageProof, - parse: impl FnOnce(bp_runtime::StorageProofChecker>) -> R, - ) -> Result { - let header = >::get(hash).ok_or(Error::::UnknownHeader)?; - let storage_proof_checker = - bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) - .map_err(|_| Error::::StorageRootMismatch)?; - - Ok(parse(storage_proof_checker)) +/// Bridge GRANDPA pallet as header chain. +pub type GrandpaChainHeaders = Pallet; + +impl, I: 'static> HeaderChain> for GrandpaChainHeaders { + fn finalized_header(hash: HashOf>) -> Option>> { + ImportedHeaders::::get(hash).map(|h| h.into_inner()) } } @@ -1122,7 +1110,7 @@ mod tests { sp_trie::StorageProof::new(vec![]), |_| (), ), - Error::::UnknownHeader, + bp_header_chain::HeaderChainError::UnknownHeader, ); }); } diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index bc4d3d4d4..c15ebf8d3 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -11,6 +11,7 @@ codec = { package = "parity-scale-codec", version = "3.1", default-features scale-info = { version = "2.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } # darwinia-network +bp-header-chain = { default-features = false, path = "../../primitives/parachains" } bp-parachains = { default-features = false, path = "../../primitives/parachains" } bp-polkadot-core = { default-features = false, path = "../../primitives/polkadot-core" } bp-runtime = { default-features = false, path = "../../primitives/runtime" } @@ -39,6 +40,7 @@ std = [ "scale-info/std", "serde", # darwinia-network + "bp-header-chain/std", "bp-parachains/std", "bp-polkadot-core/std", "bp-runtime/std", diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index 03fe6241a..9d9cb0586 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -37,10 +37,13 @@ pub use weights_ext::WeightInfoExt; mod extension; +// crates.io +use codec::Decode; // darwinia-network +use bp_header_chain::HeaderChain; use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo}; -use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::StorageProofError; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{HashOf, HeaderOf, Parachain, StorageProofError}; // paritytech use frame_support::{dispatch::PostDispatchInfo, traits::Contains}; use sp_runtime::traits::Header as HeaderT; @@ -411,27 +414,6 @@ pub mod pallet { ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) } - /// Verify that the passed storage proof is valid, given it is crafted using - /// known finalized header. If the proof is valid, then the `parse` callback - /// is called and the function returns its result. - pub fn parse_finalized_storage_proof( - parachain: ParaId, - hash: ParaHash, - storage_proof: sp_trie::StorageProof, - decode_state_root: impl FnOnce(ParaHead) -> Option, - parse: impl FnOnce(bp_runtime::StorageProofChecker) -> R, - ) -> Result { - let para_head = - Self::parachain_head(parachain, hash).ok_or(Error::::UnknownParaHead)?; - let state_root = - decode_state_root(para_head).ok_or(Error::::FailedToExtractStateRoot)?; - let storage_proof_checker = - bp_runtime::StorageProofChecker::new(state_root, storage_proof) - .map_err(|_| Error::::StorageRootMismatch)?; - - Ok(parse(storage_proof_checker)) - } - /// Read parachain head from storage proof. fn read_parachain_head( storage: &bp_runtime::StorageProofChecker, @@ -619,6 +601,18 @@ pub mod pallet { } pub use pallet::*; +/// Single parachain header chain adapter. +pub struct ParachainHeaders(PhantomData<(T, I, C)>); + +impl, I: 'static, C: Parachain> HeaderChain + for ParachainHeaders +{ + fn finalized_header(hash: HashOf) -> Option> { + Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) + .and_then(|head| Decode::decode(&mut &head.0[..]).ok()) + } +} + #[cfg(test)] mod tests { // crates.io diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index 957699f8d..9ffb6c0ee 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -20,6 +20,7 @@ sp-core = { default-features = false, git = "https://github.com/pari sp-finality-grandpa = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.33" } [dev-dependencies] # crates.io @@ -43,4 +44,5 @@ std = [ "sp-finality-grandpa/std", "sp-runtime/std", "sp-std/std", + "sp-trie/std", ] diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 0a9b8188f..11944063e 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -30,11 +30,50 @@ use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; // darwinia-network -use bp_runtime::BasicOperatingMode; +use bp_runtime::{BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofChecker}; // paritytech +use frame_support::PalletError; use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; use sp_std::boxed::Box; +use sp_trie::StorageProof; + +/// Header chain error. +#[derive(Clone, Copy, Decode, Encode, Eq, PalletError, PartialEq, RuntimeDebug, TypeInfo)] +pub enum HeaderChainError { + /// Header with given hash is missing from the chain. + UnknownHeader, + /// The storage proof doesn't contains storage root. + StorageRootMismatch, +} + +impl From for &'static str { + fn from(err: HeaderChainError) -> &'static str { + match err { + HeaderChainError::UnknownHeader => "UnknownHeader", + HeaderChainError::StorageRootMismatch => "StorageRootMismatch", + } + } +} + +/// Substrate header chain, abstracted from the way it is stored. +pub trait HeaderChain { + /// Returns finalized header by its hash. + fn finalized_header(hash: HashOf) -> Option>; + /// Parse storage proof using finalized header. + fn parse_finalized_storage_proof( + hash: HashOf, + storage_proof: StorageProof, + parse: impl FnOnce(StorageProofChecker>) -> R, + ) -> Result { + let header = Self::finalized_header(hash).ok_or(HeaderChainError::UnknownHeader)?; + let storage_proof_checker = + bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) + .map_err(|_| HeaderChainError::StorageRootMismatch)?; + + Ok(parse(storage_proof_checker)) + } +} /// A type that can be used as a parameter in a dispatchable function. /// @@ -82,6 +121,7 @@ pub struct InitializationData { /// A trait that provides helper methods for querying the consensus log. pub trait ConsensusLogReader { + /// Returns true if digest contains item that schedules authorities set change. fn schedules_authorities_change(digest: &Digest) -> bool; } diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs index 0414f2614..51b62e03d 100644 --- a/primitives/runtime/src/chain.rs +++ b/primitives/runtime/src/chain.rs @@ -30,6 +30,12 @@ use sp_std::{fmt::Debug, hash::Hash, prelude::*, str::FromStr}; // darwinia-network use crate::HeaderIdProvider; +/// Minimal parachain representation that may be used from no_std environment. +pub trait Parachain: Chain { + /// Parachain identifier. + const PARACHAIN_ID: u32; +} + /// Block number used by the chain. pub type BlockNumberOf = ::BlockNumber; diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index e4c36f470..46bec268c 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -26,7 +26,7 @@ mod storage_types; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, - HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, + HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; #[cfg(feature = "std")]