sp_domains_fraud_proof/
storage_proof.rs

1use frame_support::PalletError;
2use parity_scale_codec::{Decode, Encode};
3use scale_info::TypeInfo;
4use sp_core::storage::StorageKey;
5use sp_core::H256;
6use sp_domains::proof_provider_and_verifier::{
7    StorageProofVerifier, VerificationError as StorageProofVerificationError,
8};
9use sp_domains::{
10    DomainAllowlistUpdates, DomainId, DomainSudoCall, EvmDomainContractCreationAllowedByCall,
11    OpaqueBundle, RuntimeId, RuntimeObject,
12};
13use sp_runtime::traits::{Block as BlockT, HashingFor, Header as HeaderT, NumberFor};
14use sp_runtime_interface::pass_by;
15use sp_runtime_interface::pass_by::PassBy;
16use sp_std::marker::PhantomData;
17use sp_std::vec::Vec;
18use sp_trie::StorageProof;
19use subspace_core_primitives::Randomness;
20use subspace_runtime_primitives::{Balance, Moment};
21
22#[cfg(feature = "std")]
23use sc_client_api::ProofProvider;
24
25#[cfg(feature = "std")]
26#[derive(Debug, thiserror::Error)]
27pub enum GenerationError {
28    #[error("Failed to generate storage proof")]
29    StorageProof,
30    #[error("Failed to get storage key")]
31    StorageKey,
32}
33
34#[derive(Debug, PartialEq, Eq, Encode, Decode, PalletError, TypeInfo)]
35pub enum VerificationError {
36    InvalidBundleStorageProof,
37    RuntimeCodeNotFound,
38    UnexpectedDomainRuntimeUpgrade,
39    InvalidInherentExtrinsicStorageProof(StorageProofVerificationError),
40    SuccessfulBundlesStorageProof(StorageProofVerificationError),
41    DomainAllowlistUpdatesStorageProof(StorageProofVerificationError),
42    DomainRuntimeUpgradesStorageProof(StorageProofVerificationError),
43    RuntimeRegistryStorageProof(StorageProofVerificationError),
44    DigestStorageProof(StorageProofVerificationError),
45    BlockFeesStorageProof(StorageProofVerificationError),
46    TransfersStorageProof(StorageProofVerificationError),
47    ExtrinsicStorageProof(StorageProofVerificationError),
48    DomainSudoCallStorageProof(StorageProofVerificationError),
49    EvmDomainContractCreationAllowedByCallStorageProof(StorageProofVerificationError),
50    MmrRootStorageProof(StorageProofVerificationError),
51}
52
53#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
54pub enum FraudProofStorageKeyRequest<Number> {
55    InvalidInherentExtrinsicData,
56    SuccessfulBundles(DomainId),
57    DomainAllowlistUpdates(DomainId),
58    DomainRuntimeUpgrades,
59    RuntimeRegistry(RuntimeId),
60    DomainSudoCall(DomainId),
61    EvmDomainContractCreationAllowedByCall(DomainId),
62    MmrRoot(Number),
63}
64
65impl<Number> FraudProofStorageKeyRequest<Number> {
66    fn into_error(self, err: StorageProofVerificationError) -> VerificationError {
67        match self {
68            Self::InvalidInherentExtrinsicData => {
69                VerificationError::InvalidInherentExtrinsicStorageProof(err)
70            }
71            Self::SuccessfulBundles(_) => VerificationError::SuccessfulBundlesStorageProof(err),
72            Self::DomainAllowlistUpdates(_) => {
73                VerificationError::DomainAllowlistUpdatesStorageProof(err)
74            }
75            Self::DomainRuntimeUpgrades => {
76                VerificationError::DomainRuntimeUpgradesStorageProof(err)
77            }
78            Self::RuntimeRegistry(_) => VerificationError::RuntimeRegistryStorageProof(err),
79            FraudProofStorageKeyRequest::DomainSudoCall(_) => {
80                VerificationError::DomainSudoCallStorageProof(err)
81            }
82            FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(_) => {
83                VerificationError::EvmDomainContractCreationAllowedByCallStorageProof(err)
84            }
85            Self::MmrRoot(_) => VerificationError::MmrRootStorageProof(err),
86        }
87    }
88}
89
90/// Trait to get storage keys in the runtime i.e. when verifying the storage proof
91pub trait FraudProofStorageKeyProvider<Number> {
92    fn storage_key(req: FraudProofStorageKeyRequest<Number>) -> Vec<u8>;
93}
94
95impl<Number> FraudProofStorageKeyProvider<Number> for () {
96    fn storage_key(_req: FraudProofStorageKeyRequest<Number>) -> Vec<u8> {
97        Default::default()
98    }
99}
100
101/// Trait to get storage keys in the client i.e. when generating the storage proof
102pub trait FraudProofStorageKeyProviderInstance<Number> {
103    fn storage_key(&self, req: FraudProofStorageKeyRequest<Number>) -> Option<Vec<u8>>;
104}
105
106macro_rules! impl_storage_proof {
107    ($name:ident) => {
108        impl From<StorageProof> for $name {
109            fn from(sp: StorageProof) -> Self {
110                $name(sp)
111            }
112        }
113        impl From<$name> for StorageProof {
114            fn from(p: $name) -> StorageProof {
115                p.0
116            }
117        }
118    };
119}
120
121pub trait BasicStorageProof<Block: BlockT>:
122    Into<StorageProof> + From<StorageProof> + Clone
123{
124    type StorageValue: Decode;
125    type Key = ();
126
127    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>>;
128
129    #[cfg(feature = "std")]
130    fn generate<
131        PP: ProofProvider<Block>,
132        SKPI: FraudProofStorageKeyProviderInstance<NumberFor<Block>>,
133    >(
134        proof_provider: &PP,
135        block_hash: Block::Hash,
136        key: Self::Key,
137        storage_key_provider: &SKPI,
138    ) -> Result<Self, GenerationError> {
139        let storage_key = storage_key_provider
140            .storage_key(Self::storage_key_request(key))
141            .ok_or(GenerationError::StorageKey)?;
142        let storage_proof = proof_provider
143            .read_proof(block_hash, &mut [storage_key.as_slice()].into_iter())
144            .map_err(|_| GenerationError::StorageProof)?;
145        Ok(storage_proof.into())
146    }
147
148    fn verify<SKP: FraudProofStorageKeyProvider<NumberFor<Block>>>(
149        self,
150        key: Self::Key,
151        state_root: &Block::Hash,
152    ) -> Result<Self::StorageValue, VerificationError> {
153        let storage_key_req = Self::storage_key_request(key);
154        let storage_key = SKP::storage_key(storage_key_req.clone());
155        StorageProofVerifier::<HashingFor<Block>>::get_decoded_value::<Self::StorageValue>(
156            state_root,
157            self.into(),
158            StorageKey(storage_key),
159        )
160        .map_err(|err| storage_key_req.into_error(err))
161    }
162}
163
164#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
165pub struct SuccessfulBundlesProof(StorageProof);
166
167impl_storage_proof!(SuccessfulBundlesProof);
168impl<Block: BlockT> BasicStorageProof<Block> for SuccessfulBundlesProof {
169    type StorageValue = Vec<H256>;
170    type Key = DomainId;
171    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
172        FraudProofStorageKeyRequest::SuccessfulBundles(key)
173    }
174}
175
176#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
177pub struct DomainChainsAllowlistUpdateStorageProof(StorageProof);
178
179impl_storage_proof!(DomainChainsAllowlistUpdateStorageProof);
180impl<Block: BlockT> BasicStorageProof<Block> for DomainChainsAllowlistUpdateStorageProof {
181    type StorageValue = DomainAllowlistUpdates;
182    type Key = DomainId;
183    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
184        FraudProofStorageKeyRequest::DomainAllowlistUpdates(key)
185    }
186}
187
188#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
189pub struct DomainSudoCallStorageProof(StorageProof);
190
191impl_storage_proof!(DomainSudoCallStorageProof);
192impl<Block: BlockT> BasicStorageProof<Block> for DomainSudoCallStorageProof {
193    type StorageValue = DomainSudoCall;
194    type Key = DomainId;
195    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
196        FraudProofStorageKeyRequest::DomainSudoCall(key)
197    }
198}
199
200#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
201pub struct EvmDomainContractCreationAllowedByCallStorageProof(StorageProof);
202
203impl_storage_proof!(EvmDomainContractCreationAllowedByCallStorageProof);
204impl<Block: BlockT> BasicStorageProof<Block>
205    for EvmDomainContractCreationAllowedByCallStorageProof
206{
207    type StorageValue = EvmDomainContractCreationAllowedByCall;
208    type Key = DomainId;
209    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
210        FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(key)
211    }
212}
213
214#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
215pub struct DomainRuntimeUpgradesProof(StorageProof);
216
217impl_storage_proof!(DomainRuntimeUpgradesProof);
218impl<Block: BlockT> BasicStorageProof<Block> for DomainRuntimeUpgradesProof {
219    type StorageValue = Vec<RuntimeId>;
220    type Key = ();
221    fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
222        FraudProofStorageKeyRequest::DomainRuntimeUpgrades
223    }
224}
225
226// The domain runtime code with storage proof
227//
228// NOTE: usually we should use the parent consensus block hash to `generate` or `verify` the
229// domain runtime code because the domain's `set_code` extrinsic is always the last extrinsic
230// to execute thus the domain runtime code will take effect in the next domain block, in other
231// word the domain runtime code of the parent consensus block is the one used when constructing
232// the `ExecutionReceipt`.
233#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
234pub struct DomainRuntimeCodeProof(StorageProof);
235
236impl_storage_proof!(DomainRuntimeCodeProof);
237impl<Block: BlockT> BasicStorageProof<Block> for DomainRuntimeCodeProof {
238    type StorageValue = RuntimeObject<NumberFor<Block>, Block::Hash>;
239    type Key = RuntimeId;
240    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
241        FraudProofStorageKeyRequest::RuntimeRegistry(key)
242    }
243}
244
245#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
246pub struct OpaqueBundleWithProof<Number, Hash, DomainHeader: HeaderT, Balance> {
247    pub bundle: OpaqueBundle<Number, Hash, DomainHeader, Balance>,
248    pub bundle_index: u32,
249    pub bundle_storage_proof: SuccessfulBundlesProof,
250}
251
252impl<Number, Hash, DomainHeader, Balance> OpaqueBundleWithProof<Number, Hash, DomainHeader, Balance>
253where
254    Number: Encode,
255    Hash: Encode,
256    DomainHeader: HeaderT,
257    Balance: Encode,
258{
259    #[cfg(feature = "std")]
260    #[allow(clippy::let_and_return)]
261    pub fn generate<
262        Block: BlockT,
263        PP: ProofProvider<Block>,
264        SKP: FraudProofStorageKeyProviderInstance<NumberFor<Block>>,
265    >(
266        storage_key_provider: &SKP,
267        proof_provider: &PP,
268        domain_id: DomainId,
269        block_hash: Block::Hash,
270        bundle: OpaqueBundle<Number, Hash, DomainHeader, Balance>,
271        bundle_index: u32,
272    ) -> Result<Self, GenerationError> {
273        let bundle_storage_proof = SuccessfulBundlesProof::generate(
274            proof_provider,
275            block_hash,
276            domain_id,
277            storage_key_provider,
278        )?;
279
280        Ok(OpaqueBundleWithProof {
281            bundle,
282            bundle_index,
283            bundle_storage_proof,
284        })
285    }
286
287    /// Verify if the `bundle` does commit to the given `state_root`
288    pub fn verify<Block: BlockT, SKP: FraudProofStorageKeyProvider<NumberFor<Block>>>(
289        &self,
290        domain_id: DomainId,
291        state_root: &Block::Hash,
292    ) -> Result<(), VerificationError> {
293        let successful_bundles_at: Vec<H256> =
294            <SuccessfulBundlesProof as BasicStorageProof<Block>>::verify::<SKP>(
295                self.bundle_storage_proof.clone(),
296                domain_id,
297                state_root,
298            )?;
299
300        successful_bundles_at
301            .get(self.bundle_index as usize)
302            .filter(|b| **b == self.bundle.hash())
303            .ok_or(VerificationError::InvalidBundleStorageProof)?;
304
305        Ok(())
306    }
307}
308
309/// A proof of a single domain runtime upgrade (or that there wasn't an upgrade).
310#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
311pub struct MaybeDomainRuntimeUpgradedProof {
312    /// A list of domain runtime upgrades for a block.
313    pub domain_runtime_upgrades: DomainRuntimeUpgradesProof,
314
315    /// The new domain runtime code, if the domain runtime was upgraded.
316    pub new_domain_runtime_code: Option<DomainRuntimeCodeProof>,
317}
318
319impl MaybeDomainRuntimeUpgradedProof {
320    /// Generate the `MaybeDomainRuntimeUpgradedProof`.
321    /// It is the caller's responsibility to check if the domain runtime is upgraded at
322    /// `block_hash`. If it is, the `maybe_runtime_id` should be `Some`.
323    #[cfg(feature = "std")]
324    #[allow(clippy::let_and_return)]
325    pub fn generate<
326        Block: BlockT,
327        PP: ProofProvider<Block>,
328        SKP: FraudProofStorageKeyProviderInstance<NumberFor<Block>>,
329    >(
330        storage_key_provider: &SKP,
331        proof_provider: &PP,
332        block_hash: Block::Hash,
333        maybe_runtime_id: Option<RuntimeId>,
334    ) -> Result<Self, GenerationError> {
335        let domain_runtime_upgrades = DomainRuntimeUpgradesProof::generate(
336            proof_provider,
337            block_hash,
338            (),
339            storage_key_provider,
340        )?;
341        let new_domain_runtime_code = if let Some(runtime_id) = maybe_runtime_id {
342            Some(DomainRuntimeCodeProof::generate(
343                proof_provider,
344                block_hash,
345                runtime_id,
346                storage_key_provider,
347            )?)
348        } else {
349            None
350        };
351        Ok(MaybeDomainRuntimeUpgradedProof {
352            domain_runtime_upgrades,
353            new_domain_runtime_code,
354        })
355    }
356
357    pub fn verify<Block: BlockT, SKP: FraudProofStorageKeyProvider<NumberFor<Block>>>(
358        &self,
359        runtime_id: RuntimeId,
360        state_root: &Block::Hash,
361    ) -> Result<Option<Vec<u8>>, VerificationError> {
362        let domain_runtime_upgrades =
363            <DomainRuntimeUpgradesProof as BasicStorageProof<Block>>::verify::<SKP>(
364                self.domain_runtime_upgrades.clone(),
365                (),
366                state_root,
367            )?;
368        let runtime_upgraded = domain_runtime_upgrades.contains(&runtime_id);
369
370        match (runtime_upgraded, self.new_domain_runtime_code.as_ref()) {
371            (true, None) | (false, Some(_)) => {
372                Err(VerificationError::UnexpectedDomainRuntimeUpgrade)
373            }
374            (false, None) => Ok(None),
375            (true, Some(runtime_code_proof)) => {
376                let mut runtime_obj = <DomainRuntimeCodeProof as BasicStorageProof<Block>>::verify::<
377                    SKP,
378                >(
379                    runtime_code_proof.clone(), runtime_id, state_root
380                )?;
381                let code = runtime_obj
382                    .raw_genesis
383                    .take_runtime_code()
384                    .ok_or(VerificationError::RuntimeCodeNotFound)?;
385                Ok(Some(code))
386            }
387        }
388    }
389}
390
391#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
392pub struct InherentExtrinsicData {
393    /// Extrinsics shuffling seed, derived from block randomness
394    pub extrinsics_shuffling_seed: Randomness,
395
396    /// Block timestamp
397    pub timestamp: Moment,
398
399    /// Transaction byte fee, derived from dynamic cost of storage and the consensus chain byte fee
400    pub consensus_transaction_byte_fee: Balance,
401}
402
403impl PassBy for InherentExtrinsicData {
404    type PassBy = pass_by::Codec<Self>;
405}
406
407#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
408pub struct InvalidInherentExtrinsicDataProof(StorageProof);
409
410impl_storage_proof!(InvalidInherentExtrinsicDataProof);
411impl<Block: BlockT> BasicStorageProof<Block> for InvalidInherentExtrinsicDataProof {
412    type StorageValue = InherentExtrinsicData;
413    fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
414        FraudProofStorageKeyRequest::InvalidInherentExtrinsicData
415    }
416}
417
418#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
419pub struct MmrRootStorageProof<MmrHash> {
420    storage_proof: StorageProof,
421    _phantom_data: PhantomData<MmrHash>,
422}
423
424impl<MmrHash> From<StorageProof> for MmrRootStorageProof<MmrHash> {
425    fn from(storage_proof: StorageProof) -> Self {
426        MmrRootStorageProof {
427            storage_proof,
428            _phantom_data: Default::default(),
429        }
430    }
431}
432
433impl<MmrHash> From<MmrRootStorageProof<MmrHash>> for StorageProof {
434    fn from(p: MmrRootStorageProof<MmrHash>) -> StorageProof {
435        p.storage_proof
436    }
437}
438
439impl<Block: BlockT, MmrHash: Decode + Clone> BasicStorageProof<Block>
440    for MmrRootStorageProof<MmrHash>
441{
442    type StorageValue = MmrHash;
443    type Key = NumberFor<Block>;
444    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
445        FraudProofStorageKeyRequest::MmrRoot(key)
446    }
447}