diff --git a/Cargo.lock b/Cargo.lock index 5dc68ac..7e152da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2645,8 +2645,6 @@ dependencies = [ "ismp", "log", "parity-scale-codec", - "serde", - "sp-consensus-aura", "sp-core 7.0.0", "sp-finality-grandpa", "sp-io 7.0.0", @@ -3251,6 +3249,7 @@ dependencies = [ "sp-io 7.0.0", "sp-runtime 7.0.0", "sp-trie 7.0.0", + "state-machine-primitives", ] [[package]] @@ -3283,12 +3282,15 @@ name = "ismp-primitives" version = "0.1.0" dependencies = [ "ckb-merkle-mountain-range", + "frame-support", "frame-system", "ismp", "parity-scale-codec", "primitive-types", "scale-info", "serde", + "sp-consensus-aura", + "sp-core 7.0.0", "sp-runtime 7.0.0", ] @@ -4950,6 +4952,7 @@ dependencies = [ "sp-io 7.0.0", "sp-runtime 7.0.0", "sp-trie 7.0.0", + "state-machine-primitives", ] [[package]] @@ -9847,6 +9850,25 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "state-machine-primitives" +version = "0.1.0" +dependencies = [ + "ckb-merkle-mountain-range", + "frame-support", + "frame-system", + "ismp", + "ismp-primitives", + "pallet-ismp", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index dcb75c8..24aadec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "pallet-ismp/rpc", "pallet-ismp/runtime-api", "pallet-ismp/primitives", + "pallet-ismp/primitives/state-machine", "pallet-ismp", "parachain/inherent", "parachain/runtime-api", diff --git a/grandpa/Cargo.toml b/grandpa/Cargo.toml index e79cf70..9c8995d 100644 --- a/grandpa/Cargo.toml +++ b/grandpa/Cargo.toml @@ -31,6 +31,7 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot- cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "release-v0.9.420", default-features = false } ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } +state-machine-primitives = { path = "../pallet-ismp/primitives/state-machine", default-features = false } [features] default = ["std"] @@ -52,4 +53,5 @@ std = [ "verifier/std", "merkle-mountain-range/std", "ismp-primitives/std", + "state-machine-primitives/std" ] diff --git a/grandpa/primitives/Cargo.toml b/grandpa/primitives/Cargo.toml index e46f4e2..d0203f8 100644 --- a/grandpa/primitives/Cargo.toml +++ b/grandpa/primitives/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # crates.io -serde = { version = "1.0.136", features = ["derive"], optional = true } anyhow = { version = "1.0.64", default-features = false } hash-db = { version = "0.16.0", default-features = false } derive_more = { version = "0.99.17", default-features = false, features = ["display"] } @@ -21,7 +20,6 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } #polytope ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } @@ -36,12 +34,10 @@ std = [ "codec/std", "sp-core/std", "sp-runtime/std", - "sp-consensus-aura/std", "sp-io/std", "frame-support/std", "sp-finality-grandpa/std", "sp-std/std", "sp-trie/std", "ismp/std", - "serde", ] diff --git a/grandpa/primitives/src/lib.rs b/grandpa/primitives/src/lib.rs index 2a7f67d..771d1c0 100644 --- a/grandpa/primitives/src/lib.rs +++ b/grandpa/primitives/src/lib.rs @@ -23,13 +23,11 @@ extern crate alloc; use alloc::collections::BTreeMap; use codec::{Decode, Encode}; -use core::{fmt::Debug, time::Duration}; -use frame_support::sp_runtime::Digest; -use ismp::{error::Error, host::StateMachine}; -use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use core::fmt::Debug; +use ismp::host::StateMachine; use sp_core::{sp_std, H256}; use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthoritySignature}; -use sp_runtime::{traits::Header, DigestItem}; +use sp_runtime::traits::Header; use sp_std::prelude::*; use sp_storage::StorageKey; @@ -106,36 +104,6 @@ pub struct ParachainHeadersWithFinalityProof { pub parachain_headers: BTreeMap, } -/// Hashing algorithm for the state proof -#[derive(Debug, Encode, Decode, Clone)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub enum HashAlgorithm { - /// For chains that use keccak as their hashing algo - Keccak, - /// For chains that use blake2 as their hashing algo - Blake2, -} - -/// Holds the relevant data needed for state proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct SubstrateStateProof { - /// Algorithm to use for state proof verification - pub hasher: HashAlgorithm, - /// Storage proof for the parachain headers - pub storage_proof: Vec>, -} - -/// Holds the relevant data needed for request/response proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct MembershipProof { - /// Size of the mmr at the time this proof was generated - pub mmr_size: u64, - /// Leaf indices for the proof - pub leaf_indices: Vec, - /// Mmr proof - pub proof: Vec, -} - /// This returns the storage key for a parachain header on the relay chain. pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec(); @@ -144,38 +112,3 @@ pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { storage_key.extend_from_slice(&encoded_para_id); StorageKey(storage_key) } - -/// Fetches the overlay(ismp) root and timestamp from the header digest -pub fn fetch_overlay_root_and_timestamp( - digest: &Digest, - slot_duration: u64, -) -> Result<(u64, H256), Error> { - let (mut timestamp, mut overlay_root) = (0, H256::default()); - - for digest in digest.logs.iter() { - match digest { - DigestItem::PreRuntime(consensus_engine_id, value) - if *consensus_engine_id == AURA_ENGINE_ID => - { - let slot = Slot::decode(&mut &value[..]) - .map_err(|e| Error::ImplementationSpecific(format!("Cannot slot: {e:?}")))?; - timestamp = Duration::from_millis(*slot * slot_duration).as_secs(); - } - DigestItem::Consensus(consensus_engine_id, value) - if *consensus_engine_id == ISMP_ID => - { - if value.len() != 32 { - Err(Error::ImplementationSpecific( - "Header contains an invalid ismp root".into(), - ))? - } - - overlay_root = H256::from_slice(&value); - } - // don't really care about the rest - _ => {} - }; - } - - Ok((timestamp, overlay_root)) -} diff --git a/grandpa/src/consensus.rs b/grandpa/src/consensus.rs index d88737a..3da590c 100644 --- a/grandpa/src/consensus.rs +++ b/grandpa/src/consensus.rs @@ -22,11 +22,11 @@ use ismp::{ host::{IsmpHost, StateMachine}, messaging::StateCommitmentHeight, }; +use ismp_primitives::fetch_overlay_root_and_timestamp; use primitive_types::H256; -use primitives::{ - fetch_overlay_root_and_timestamp, ConsensusState, ParachainHeadersWithFinalityProof, -}; +use primitives::{ConsensusState, ParachainHeadersWithFinalityProof}; use sp_runtime::traits::Header; +use state_machine_primitives::SubstrateStateMachine; use verifier::{ verify_grandpa_finality_proof, verify_parachain_headers_with_grandpa_finality_proof, }; @@ -183,6 +183,6 @@ where } fn state_machine(&self, _id: StateMachine) -> Result, Error> { - todo!() + Ok(Box::new(SubstrateStateMachine::::default())) } } diff --git a/grandpa/src/consensus_message.rs b/grandpa/src/consensus_message.rs index a2c0a94..40b0f8a 100644 --- a/grandpa/src/consensus_message.rs +++ b/grandpa/src/consensus_message.rs @@ -13,7 +13,6 @@ // See the License for the specific lang use alloc::collections::BTreeMap; use codec::{Decode, Encode}; -use ismp::consensus::StateMachineId; use primitives::{FinalityProof, ParachainHeaderProofs}; use sp_core::H256; use sp_runtime::traits::BlakeTwo256; @@ -34,8 +33,6 @@ pub enum ConsensusMessage { pub struct StandaloneChainMessage { /// finality proof pub finality_proof: FinalityProof, - /// state machine id - pub state_machine_id: StateMachineId, } #[derive(Clone, Debug, Encode, Decode)] diff --git a/pallet-ismp/primitives/Cargo.toml b/pallet-ismp/primitives/Cargo.toml index 3fc71c5..fc5cb16 100644 --- a/pallet-ismp/primitives/Cargo.toml +++ b/pallet-ismp/primitives/Cargo.toml @@ -18,6 +18,9 @@ codec = { package = "parity-scale-codec", version = "3.1.3", default-features = primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.136", features = ["derive"], optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-consensus-aura = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } [features] default = ["std"] @@ -29,5 +32,8 @@ std = [ "sp-runtime/std", "primitive-types/std", "scale-info/std", - "serde" + "serde", + "frame-support/std", + "sp-core/std", + "sp-consensus-aura/std", ] diff --git a/pallet-ismp/primitives/src/lib.rs b/pallet-ismp/primitives/src/lib.rs index 40e8d67..862eed2 100644 --- a/pallet-ismp/primitives/src/lib.rs +++ b/pallet-ismp/primitives/src/lib.rs @@ -19,10 +19,19 @@ #![deny(missing_docs)] //! Primitives for the MMR implementation -use ismp::host::StateMachine; +use codec::{Decode, Encode}; +use core::{fmt::Debug, time::Duration}; +use frame_support::sp_runtime::Digest; +use ismp::{error::Error, host::StateMachine}; +use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use sp_core::H256; +use sp_runtime::DigestItem; pub mod mmr; +/// The `ConsensusEngineId` of ISMP digest in the parachain header. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + /// Queries a request leaf in the mmr #[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] @@ -34,3 +43,68 @@ pub struct LeafIndexQuery { /// The request nonce pub nonce: u64, } + +/// Hashing algorithm for the state proof +#[derive(Debug, Encode, Decode, Clone)] +#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] +pub enum HashAlgorithm { + /// For chains that use keccak as their hashing algo + Keccak, + /// For chains that use blake2 as their hashing algo + Blake2, +} + +/// Holds the relevant data needed for state proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct SubstrateStateProof { + /// Algorithm to use for state proof verification + pub hasher: HashAlgorithm, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Holds the relevant data needed for request/response proof verification +#[derive(Debug, Encode, Decode, Clone)] +pub struct MembershipProof { + /// Size of the mmr at the time this proof was generated + pub mmr_size: u64, + /// Leaf indices for the proof + pub leaf_indices: Vec, + /// Mmr proof + pub proof: Vec, +} + +/// Fetches the overlay(ismp) root and timestamp from the header digest +pub fn fetch_overlay_root_and_timestamp( + digest: &Digest, + slot_duration: u64, +) -> Result<(u64, H256), Error> { + let (mut timestamp, mut overlay_root) = (0, H256::default()); + + for digest in digest.logs.iter() { + match digest { + DigestItem::PreRuntime(consensus_engine_id, value) + if *consensus_engine_id == AURA_ENGINE_ID => + { + let slot = Slot::decode(&mut &value[..]) + .map_err(|e| Error::ImplementationSpecific(format!("Cannot slot: {e:?}")))?; + timestamp = Duration::from_millis(*slot * slot_duration).as_secs(); + } + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if value.len() != 32 { + Err(Error::ImplementationSpecific( + "Header contains an invalid ismp root".into(), + ))? + } + + overlay_root = H256::from_slice(&value); + } + // don't really care about the rest + _ => {} + }; + } + + Ok((timestamp, overlay_root)) +} diff --git a/pallet-ismp/primitives/state-machine/Cargo.toml b/pallet-ismp/primitives/state-machine/Cargo.toml new file mode 100644 index 0000000..94d5043 --- /dev/null +++ b/pallet-ismp/primitives/state-machine/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "state-machine-primitives" +version = "0.1.0" +edition = "2021" +authors = ["Polytope Labs "] + +[dependencies] +# substrate +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +# polytope labs +ismp = { git = "https://github.com/polytope-labs/ismp-rs", branch = "main", default-features = false } + +# crates.io +merkle-mountain-range = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false } +primitive-types = { version = "0.12.1", default-features = false } +serde = { version = "1.0.136", features = ["derive"], optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + + +ismp-primitives = { path = "..", default-features = false } +pallet-ismp = { path = "../..", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-system/std", + "ismp/std", + "merkle-mountain-range/std", + "codec/std", + "sp-runtime/std", + "primitive-types/std", + "scale-info/std", + "serde", + "frame-support/std", + "sp-core/std", + "pallet-ismp/std", + "ismp-primitives/std", + "sp-trie/std" +] diff --git a/pallet-ismp/primitives/state-machine/src/lib.rs b/pallet-ismp/primitives/state-machine/src/lib.rs new file mode 100644 index 0000000..18d31ce --- /dev/null +++ b/pallet-ismp/primitives/state-machine/src/lib.rs @@ -0,0 +1,165 @@ +// 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. + +//! The state machine implementation in Substrate +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::all)] +#![deny(missing_docs)] + +extern crate alloc; + +use alloc::{collections::BTreeMap, format, vec, vec::Vec}; +use codec::Decode; +use core::marker::PhantomData; +use ismp::{ + consensus::{StateCommitment, StateMachineClient}, + error::Error, + host::IsmpHost, + messaging::Proof, + router::{Request, RequestResponse}, + util::hash_request, +}; +use ismp_primitives::{ + mmr::{DataOrHash, Leaf, MmrHasher}, + HashAlgorithm, MembershipProof, SubstrateStateProof, +}; +use merkle_mountain_range::MerkleProof; +use pallet_ismp::host::Host; +use primitive_types::H256; +use sp_runtime::traits::{BlakeTwo256, Keccak256}; +use sp_trie::{LayoutV0, StorageProof, Trie, TrieDBBuilder}; + +/// The parachain and grandpa consensus client implementation for ISMP. +pub struct SubstrateStateMachine(PhantomData); + +impl Default for SubstrateStateMachine { + fn default() -> Self { + Self(PhantomData) + } +} + +impl StateMachineClient for SubstrateStateMachine +where + T: pallet_ismp::Config, + T::BlockNumber: Into, + T::Hash: From, +{ + fn verify_membership( + &self, + _host: &dyn IsmpHost, + item: RequestResponse, + state: StateCommitment, + proof: &Proof, + ) -> Result<(), Error> { + let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { + Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) + })?; + let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); + let proof = + MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); + let leaves: Vec<(u64, DataOrHash)> = match item { + RequestResponse::Request(req) => membership + .leaf_indices + .into_iter() + .zip(req.into_iter()) + .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) + .collect(), + RequestResponse::Response(res) => membership + .leaf_indices + .into_iter() + .zip(res.into_iter()) + .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) + .collect(), + }; + let root = state + .overlay_root + .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; + + let calc_root = proof + .calculate_root(leaves.clone()) + .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; + let valid = calc_root.hash::>() == root.into(); + + if !valid { + Err(Error::ImplementationSpecific("Invalid membership proof".into()))? + } + + Ok(()) + } + + fn state_trie_key(&self, requests: Vec) -> Vec> { + let mut keys = vec![]; + + for req in requests { + match req { + Request::Post(post) => { + let request = Request::Post(post); + let commitment = hash_request::>(&request).0.to_vec(); + keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); + } + Request::Get(_) => continue, + } + } + + keys + } + + fn verify_state_proof( + &self, + _host: &dyn IsmpHost, + keys: Vec>, + root: StateCommitment, + proof: &Proof, + ) -> Result, Option>>, Error> { + let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) + .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; + + let data = match state_proof.hasher { + HashAlgorithm::Keccak => { + let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); + let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); + keys.into_iter() + .map(|key| { + let value = trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + })?; + Ok((key, value)) + }) + .collect::, _>>()? + } + HashAlgorithm::Blake2 => { + let db = + StorageProof::new(state_proof.storage_proof).into_memory_db::(); + + let trie = + TrieDBBuilder::>::new(&db, &root.state_root).build(); + keys.into_iter() + .map(|key| { + let value = trie.get(&key).map_err(|e| { + Error::ImplementationSpecific(format!( + "Error reading state proof: {e:?}" + )) + })?; + Ok((key, value)) + }) + .collect::, _>>()? + } + }; + + Ok(data) + } +} diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 02d8a76..b2d3a50 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -36,6 +36,7 @@ cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branc # local ismp-primitives = { path = "../pallet-ismp/primitives", default-features = false } pallet-ismp = { path = "../pallet-ismp", default-features = false } +state-machine-primitives = { path = "../pallet-ismp/primitives/state-machine", default-features = false } [dev-dependencies] @@ -61,4 +62,5 @@ std = [ "pallet-ismp/std", "hash-db/std", "serde", + "state-machine-primitives/std" ] diff --git a/parachain/src/consensus.rs b/parachain/src/consensus.rs index 26c59a7..8b7c76b 100644 --- a/parachain/src/consensus.rs +++ b/parachain/src/consensus.rs @@ -17,7 +17,7 @@ use core::{marker::PhantomData, time::Duration}; -use alloc::{boxed::Box, collections::BTreeMap, format, vec, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, format, vec::Vec}; use codec::{Decode, Encode}; use core::fmt::Debug; use ismp::{ @@ -26,44 +26,31 @@ use ismp::{ }, error::Error, host::{IsmpHost, StateMachine}, - messaging::{Proof, StateCommitmentHeight}, - router::{Request, RequestResponse}, - util::hash_request, + messaging::StateCommitmentHeight, }; -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::{ app_crypto::sp_core::storage::StorageKey, generic::Header, - traits::{BlakeTwo256, Header as _, Keccak256}, + traits::{BlakeTwo256, Header as _}, DigestItem, }; use sp_trie::{HashDBT, LayoutV0, StorageProof, Trie, TrieDBBuilder, EMPTY_PREFIX}; +use state_machine_primitives::SubstrateStateMachine; use crate::RelayChainOracle; /// The parachain consensus client implementation for ISMP. pub struct ParachainConsensusClient(PhantomData<(T, R)>); -/// The parachain state machine implementation for ISMP. -pub struct ParachainStateMachine(PhantomData); - impl Default for ParachainConsensusClient { fn default() -> Self { Self(PhantomData) } } -impl Default for ParachainStateMachine { - fn default() -> Self { - Self(PhantomData) - } -} - /// Information necessary to prove the sibling parachain's finalization to this /// parachain. #[derive(Debug, Encode, Decode)] @@ -76,36 +63,6 @@ pub struct ParachainConsensusProof { pub storage_proof: Vec>, } -/// Hashing algorithm for the state proof -#[derive(Debug, Encode, Decode, Clone)] -#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))] -pub enum HashAlgorithm { - /// For chains that use keccak as their hashing algo - Keccak, - /// For chains that use blake2 as their hashing algo - Blake2, -} - -/// Holds the relevant data needed for state proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct SubstrateStateProof { - /// Algorithm to use for state proof verification - pub hasher: HashAlgorithm, - /// Storage proof for the parachain headers - pub storage_proof: Vec>, -} - -/// Holds the relevant data needed for request/response proof verification -#[derive(Debug, Encode, Decode, Clone)] -pub struct MembershipProof { - /// Size of the mmr at the time this proof was generated - pub mmr_size: u64, - /// Leaf indices for the proof - pub leaf_indices: Vec, - /// Mmr proof - pub proof: Vec, -} - /// The `ConsensusEngineId` of ISMP digest in the parachain header. pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; @@ -247,124 +204,9 @@ where } fn state_machine(&self, _id: StateMachine) -> Result, Error> { - Ok(Box::new(ParachainStateMachine::::default())) + Ok(Box::new(SubstrateStateMachine::::default())) } } - -impl StateMachineClient for ParachainStateMachine -where - T: pallet_ismp::Config + super::Config, - T::BlockNumber: Into, - T::Hash: From, -{ - fn verify_membership( - &self, - _host: &dyn IsmpHost, - item: RequestResponse, - state: StateCommitment, - proof: &Proof, - ) -> Result<(), Error> { - let membership = MembershipProof::decode(&mut &*proof.proof).map_err(|e| { - Error::ImplementationSpecific(format!("Cannot decode membership proof: {e:?}")) - })?; - let nodes = membership.proof.into_iter().map(|h| DataOrHash::Hash(h.into())).collect(); - let proof = - MerkleProof::, MmrHasher>>::new(membership.mmr_size, nodes); - let leaves: Vec<(u64, DataOrHash)> = match item { - RequestResponse::Request(req) => membership - .leaf_indices - .into_iter() - .zip(req.into_iter()) - .map(|(pos, req)| (pos, DataOrHash::Data(Leaf::Request(req)))) - .collect(), - RequestResponse::Response(res) => membership - .leaf_indices - .into_iter() - .zip(res.into_iter()) - .map(|(pos, res)| (pos, DataOrHash::Data(Leaf::Response(res)))) - .collect(), - }; - let root = state - .overlay_root - .ok_or_else(|| Error::ImplementationSpecific("ISMP root should not be None".into()))?; - - let calc_root = proof - .calculate_root(leaves.clone()) - .map_err(|e| Error::ImplementationSpecific(format!("Error verifying mmr: {e:?}")))?; - let valid = calc_root.hash::>() == root.into(); - - if !valid { - Err(Error::ImplementationSpecific("Invalid membership proof".into()))? - } - - Ok(()) - } - - fn state_trie_key(&self, requests: Vec) -> Vec> { - let mut keys = vec![]; - - for req in requests { - match req { - Request::Post(post) => { - let request = Request::Post(post); - let commitment = hash_request::>(&request).0.to_vec(); - keys.push(pallet_ismp::RequestReceipts::::hashed_key_for(commitment)); - } - Request::Get(_) => continue, - } - } - - keys - } - - fn verify_state_proof( - &self, - _host: &dyn IsmpHost, - keys: Vec>, - root: StateCommitment, - proof: &Proof, - ) -> Result, Option>>, Error> { - let state_proof: SubstrateStateProof = codec::Decode::decode(&mut &*proof.proof) - .map_err(|e| Error::ImplementationSpecific(format!("failed to decode proof: {e:?}")))?; - - let data = match state_proof.hasher { - HashAlgorithm::Keccak => { - let db = StorageProof::new(state_proof.storage_proof).into_memory_db::(); - let trie = TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - HashAlgorithm::Blake2 => { - let db = - StorageProof::new(state_proof.storage_proof).into_memory_db::(); - - let trie = - TrieDBBuilder::>::new(&db, &root.state_root).build(); - keys.into_iter() - .map(|key| { - let value = trie.get(&key).map_err(|e| { - Error::ImplementationSpecific(format!( - "Error reading state proof: {e:?}" - )) - })?; - Ok((key, value)) - }) - .collect::, _>>()? - } - }; - - Ok(data) - } -} - /// This returns the storage key for a parachain header on the relay chain. pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { let mut storage_key = frame_support::storage::storage_prefix(b"Paras", b"Heads").to_vec();