sp_subspace_mmr/
host_functions.rs

1use crate::runtime_interface::LeafData;
2use parity_scale_codec::Decode;
3use sp_api::ProvideRuntimeApi;
4use sp_blockchain::HeaderBackend;
5use sp_core::H256;
6pub use sp_mmr_primitives::{EncodableOpaqueLeaf, LeafProof, MmrApi};
7use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Saturating};
8use std::marker::PhantomData;
9use std::sync::Arc;
10use subspace_core_primitives::BlockNumber;
11
12/// Trait to query MMR specific data through host function..
13pub trait SubspaceMmrHostFunctions: Send + Sync {
14    /// Returns the MMR Leaf data for given consensus block hash
15    fn get_mmr_leaf_data(&self, consensus_block_hash: H256) -> Option<LeafData>;
16
17    /// Verifies the mmr proof using consensus chain.
18    fn verify_mmr_proof(&self, leaves: Vec<EncodableOpaqueLeaf>, encoded_proof: Vec<u8>) -> bool;
19
20    /// Returns the consensus block hash for given block number.
21    fn consensus_block_hash(
22        &self,
23        block_number: subspace_core_primitives::BlockNumber,
24    ) -> Option<H256>;
25
26    // Return if the given consensus block is finalized
27    fn is_consensus_block_finalized(&self, block_number: BlockNumber) -> bool;
28}
29
30sp_externalities::decl_extension! {
31    pub struct SubspaceMmrExtension(Arc<dyn SubspaceMmrHostFunctions>);
32}
33
34impl SubspaceMmrExtension {
35    /// Create a new instance of [`SubspaceMmrExtension`].
36    pub fn new(inner: Arc<dyn SubspaceMmrHostFunctions>) -> Self {
37        Self(inner)
38    }
39}
40
41/// Implementation of MMR host function.
42pub struct SubspaceMmrHostFunctionsImpl<Block, Client> {
43    consensus_client: Arc<Client>,
44    confirmation_depth_k: BlockNumber,
45    _phantom: PhantomData<Block>,
46}
47
48impl<Block, Client> SubspaceMmrHostFunctionsImpl<Block, Client> {
49    pub fn new(consensus_client: Arc<Client>, confirmation_depth_k: BlockNumber) -> Self {
50        SubspaceMmrHostFunctionsImpl {
51            consensus_client,
52            confirmation_depth_k,
53            _phantom: Default::default(),
54        }
55    }
56}
57
58impl<Block, Client> SubspaceMmrHostFunctions for SubspaceMmrHostFunctionsImpl<Block, Client>
59where
60    Block: BlockT,
61    Block::Hash: From<H256> + Into<H256>,
62    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
63    Client::Api: MmrApi<Block, H256, NumberFor<Block>>,
64{
65    fn get_mmr_leaf_data(&self, consensus_block_hash: H256) -> Option<LeafData> {
66        let header = self
67            .consensus_client
68            .header(consensus_block_hash.into())
69            .expect(
70                "Database error is fatal in host function, there is no recovery from this; qed",
71            )?;
72
73        Some(LeafData {
74            state_root: H256::from_slice(header.state_root().as_ref()),
75            extrinsics_root: H256::from_slice(header.extrinsics_root().as_ref()),
76        })
77    }
78
79    fn verify_mmr_proof(&self, leaves: Vec<EncodableOpaqueLeaf>, encoded_proof: Vec<u8>) -> bool {
80        // always use the parent hash in case there is a re-org happening
81        let parent_hash = *self
82            .consensus_client
83            .header(self.consensus_client.info().best_hash)
84            .expect("Database error is fatal in host function, there is no recovery from this; qed")
85            .expect("Header must be available. There is no recovery if not available; qed.")
86            .parent_hash();
87        let api = self.consensus_client.runtime_api();
88        let proof = match LeafProof::<H256>::decode(&mut encoded_proof.as_ref()) {
89            Ok(proof) => proof,
90            Err(_) => return false,
91        };
92        api.verify_proof(parent_hash, leaves, proof).expect(
93            "Runtime Api should not fail in host function, there is no recovery from this; qed.",
94        ).is_ok()
95    }
96
97    fn consensus_block_hash(&self, block_number: BlockNumber) -> Option<H256> {
98        let block_number = NumberFor::<Block>::from(block_number);
99        self.consensus_client
100            .hash(block_number)
101            .expect("Header must be available. This is unrecoverable error")
102            .map(|block_hash| block_hash.into())
103    }
104
105    fn is_consensus_block_finalized(&self, block_number: BlockNumber) -> bool {
106        let block_number = NumberFor::<Block>::from(block_number);
107        let last_finalized_block = self
108            .consensus_client
109            .info()
110            .best_number
111            .saturating_sub(self.confirmation_depth_k.into());
112
113        block_number <= last_finalized_block
114    }
115}