Skip to main content

sp_domains_fraud_proof/
storage_proof.rs

1use frame_support::PalletError;
2use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
3use scale_info::TypeInfo;
4use sp_core::H256;
5use sp_core::storage::StorageKey;
6use sp_domains::bundle::OpaqueBundle;
7use sp_domains::proof_provider_and_verifier::{
8    StorageProofVerifier, VerificationError as StorageProofVerificationError,
9};
10use sp_domains::{
11    DomainAllowlistUpdates, DomainId, DomainSudoCall, EvmDomainContractCreationAllowedByCall,
12    RuntimeId, RuntimeObject,
13};
14use sp_runtime::traits::{Block as BlockT, HashingFor, Header as HeaderT, NumberFor, Zero};
15use sp_std::marker::PhantomData;
16use sp_std::vec::Vec;
17use sp_trie::StorageProof;
18use subspace_core_primitives::Randomness;
19use subspace_runtime_primitives::{Balance, Moment};
20
21#[cfg(feature = "std")]
22use sc_client_api::ProofProvider;
23
24#[cfg(feature = "std")]
25#[derive(Debug, thiserror::Error)]
26pub enum GenerationError {
27    #[error("Failed to generate storage proof")]
28    StorageProof,
29    #[error("Failed to get storage key")]
30    StorageKey,
31}
32
33#[derive(Debug, PartialEq, Eq, Encode, Decode, PalletError, TypeInfo, DecodeWithMemTracking)]
34pub enum VerificationError {
35    InvalidBundleStorageProof,
36    RuntimeCodeNotFound,
37    UnexpectedDomainRuntimeUpgrade,
38    InvalidInherentExtrinsicStorageProof(StorageProofVerificationError),
39    SuccessfulBundlesStorageProof(StorageProofVerificationError),
40    DomainAllowlistUpdatesStorageProof(StorageProofVerificationError),
41    DomainRuntimeUpgradesStorageProof(StorageProofVerificationError),
42    RuntimeRegistryStorageProof(StorageProofVerificationError),
43    DigestStorageProof(StorageProofVerificationError),
44    BlockFeesStorageProof(StorageProofVerificationError),
45    TransfersStorageProof(StorageProofVerificationError),
46    ExtrinsicStorageProof(StorageProofVerificationError),
47    DomainSudoCallStorageProof(StorageProofVerificationError),
48    EvmDomainContractCreationAllowedByCallStorageProof(StorageProofVerificationError),
49    MmrRootStorageProof(StorageProofVerificationError),
50}
51
52#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
53pub enum FraudProofStorageKeyRequest<Number> {
54    InvalidInherentExtrinsicData,
55    SuccessfulBundles(DomainId),
56    DomainAllowlistUpdates(DomainId),
57    DomainRuntimeUpgrades,
58    RuntimeRegistry(RuntimeId),
59    DomainSudoCall(DomainId),
60    EvmDomainContractCreationAllowedByCall(DomainId),
61    MmrRoot(Number),
62}
63
64impl<Number> FraudProofStorageKeyRequest<Number> {
65    fn into_error(self, err: StorageProofVerificationError) -> VerificationError {
66        match self {
67            Self::InvalidInherentExtrinsicData => {
68                VerificationError::InvalidInherentExtrinsicStorageProof(err)
69            }
70            Self::SuccessfulBundles(_) => VerificationError::SuccessfulBundlesStorageProof(err),
71            Self::DomainAllowlistUpdates(_) => {
72                VerificationError::DomainAllowlistUpdatesStorageProof(err)
73            }
74            Self::DomainRuntimeUpgrades => {
75                VerificationError::DomainRuntimeUpgradesStorageProof(err)
76            }
77            Self::RuntimeRegistry(_) => VerificationError::RuntimeRegistryStorageProof(err),
78            FraudProofStorageKeyRequest::DomainSudoCall(_) => {
79                VerificationError::DomainSudoCallStorageProof(err)
80            }
81            FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(_) => {
82                VerificationError::EvmDomainContractCreationAllowedByCallStorageProof(err)
83            }
84            Self::MmrRoot(_) => VerificationError::MmrRootStorageProof(err),
85        }
86    }
87}
88
89/// Trait to get storage keys in the runtime i.e. when verifying the storage proof
90pub trait FraudProofStorageKeyProvider<Number> {
91    fn storage_key(req: FraudProofStorageKeyRequest<Number>) -> Vec<u8>;
92}
93
94impl<Number> FraudProofStorageKeyProvider<Number> for () {
95    fn storage_key(_req: FraudProofStorageKeyRequest<Number>) -> Vec<u8> {
96        Default::default()
97    }
98}
99
100/// Trait to get storage keys in the client i.e. when generating the storage proof
101pub trait FraudProofStorageKeyProviderInstance<Number> {
102    fn storage_key(&self, req: FraudProofStorageKeyRequest<Number>) -> Option<Vec<u8>>;
103}
104
105macro_rules! impl_storage_proof {
106    ($name:ident) => {
107        impl From<StorageProof> for $name {
108            fn from(sp: StorageProof) -> Self {
109                $name(sp)
110            }
111        }
112        impl From<$name> for StorageProof {
113            fn from(p: $name) -> StorageProof {
114                p.0
115            }
116        }
117    };
118}
119
120pub trait BasicStorageProof<Block: BlockT>:
121    Into<StorageProof> + From<StorageProof> + Clone
122{
123    type StorageValue: Decode;
124    type Key = ();
125
126    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>>;
127
128    #[cfg(feature = "std")]
129    fn generate<
130        PP: ProofProvider<Block>,
131        SKPI: FraudProofStorageKeyProviderInstance<NumberFor<Block>>,
132    >(
133        proof_provider: &PP,
134        block_hash: Block::Hash,
135        key: Self::Key,
136        storage_key_provider: &SKPI,
137    ) -> Result<Self, GenerationError> {
138        let storage_key = storage_key_provider
139            .storage_key(Self::storage_key_request(key))
140            .ok_or(GenerationError::StorageKey)?;
141        let storage_proof = proof_provider
142            .read_proof(block_hash, &mut [storage_key.as_slice()].into_iter())
143            .map_err(|_| GenerationError::StorageProof)?;
144        Ok(storage_proof.into())
145    }
146
147    fn verify<SKP: FraudProofStorageKeyProvider<NumberFor<Block>>>(
148        self,
149        key: Self::Key,
150        state_root: &Block::Hash,
151    ) -> Result<Self::StorageValue, VerificationError> {
152        let storage_key_req = Self::storage_key_request(key);
153        let storage_key = SKP::storage_key(storage_key_req.clone());
154        StorageProofVerifier::<HashingFor<Block>>::get_decoded_value::<Self::StorageValue>(
155            state_root,
156            self.into(),
157            StorageKey(storage_key),
158        )
159        .map_err(|err| storage_key_req.into_error(err))
160    }
161}
162
163#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
164pub struct SuccessfulBundlesProof(StorageProof);
165
166impl_storage_proof!(SuccessfulBundlesProof);
167impl<Block: BlockT> BasicStorageProof<Block> for SuccessfulBundlesProof {
168    type StorageValue = Vec<H256>;
169    type Key = DomainId;
170    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
171        FraudProofStorageKeyRequest::SuccessfulBundles(key)
172    }
173}
174
175#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
176pub struct DomainChainsAllowlistUpdateStorageProof(StorageProof);
177
178impl_storage_proof!(DomainChainsAllowlistUpdateStorageProof);
179impl<Block: BlockT> BasicStorageProof<Block> for DomainChainsAllowlistUpdateStorageProof {
180    type StorageValue = DomainAllowlistUpdates;
181    type Key = DomainId;
182    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
183        FraudProofStorageKeyRequest::DomainAllowlistUpdates(key)
184    }
185}
186
187#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
188pub struct DomainSudoCallStorageProof(StorageProof);
189
190impl_storage_proof!(DomainSudoCallStorageProof);
191impl<Block: BlockT> BasicStorageProof<Block> for DomainSudoCallStorageProof {
192    type StorageValue = DomainSudoCall;
193    type Key = DomainId;
194    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
195        FraudProofStorageKeyRequest::DomainSudoCall(key)
196    }
197}
198
199#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
200pub struct EvmDomainContractCreationAllowedByCallStorageProof(StorageProof);
201
202impl_storage_proof!(EvmDomainContractCreationAllowedByCallStorageProof);
203impl<Block: BlockT> BasicStorageProof<Block>
204    for EvmDomainContractCreationAllowedByCallStorageProof
205{
206    type StorageValue = EvmDomainContractCreationAllowedByCall;
207    type Key = DomainId;
208    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
209        FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(key)
210    }
211}
212
213#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
214pub struct DomainRuntimeUpgradesProof(StorageProof);
215
216impl_storage_proof!(DomainRuntimeUpgradesProof);
217impl<Block: BlockT> BasicStorageProof<Block> for DomainRuntimeUpgradesProof {
218    type StorageValue = Vec<RuntimeId>;
219    type Key = ();
220    fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
221        FraudProofStorageKeyRequest::DomainRuntimeUpgrades
222    }
223}
224
225// The domain runtime code with storage proof
226//
227// NOTE: usually we should use the parent consensus block hash to `generate` or `verify` the
228// domain runtime code because the domain's `set_code` extrinsic is always the last extrinsic
229// to execute thus the domain runtime code will take effect in the next domain block, in other
230// word the domain runtime code of the parent consensus block is the one used when constructing
231// the `ExecutionReceipt`.
232#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
233pub struct DomainRuntimeCodeProof(StorageProof);
234
235impl_storage_proof!(DomainRuntimeCodeProof);
236impl<Block: BlockT> BasicStorageProof<Block> for DomainRuntimeCodeProof {
237    type StorageValue = RuntimeObject<NumberFor<Block>, Block::Hash>;
238    type Key = RuntimeId;
239    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
240        FraudProofStorageKeyRequest::RuntimeRegistry(key)
241    }
242}
243
244/// Bundle with proof data for fraud proof.
245#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
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 + Zero,
255    Hash: Encode + Default,
256    DomainHeader: HeaderT,
257    Balance: Encode + Zero + Default,
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, DecodeWithMemTracking)]
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
403#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
404pub struct InvalidInherentExtrinsicDataProof(StorageProof);
405
406impl_storage_proof!(InvalidInherentExtrinsicDataProof);
407impl<Block: BlockT> BasicStorageProof<Block> for InvalidInherentExtrinsicDataProof {
408    type StorageValue = InherentExtrinsicData;
409    fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
410        FraudProofStorageKeyRequest::InvalidInherentExtrinsicData
411    }
412}
413
414#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, DecodeWithMemTracking)]
415pub struct MmrRootStorageProof<MmrHash> {
416    storage_proof: StorageProof,
417    _phantom_data: PhantomData<MmrHash>,
418}
419
420impl<MmrHash> From<StorageProof> for MmrRootStorageProof<MmrHash> {
421    fn from(storage_proof: StorageProof) -> Self {
422        MmrRootStorageProof {
423            storage_proof,
424            _phantom_data: Default::default(),
425        }
426    }
427}
428
429impl<MmrHash> From<MmrRootStorageProof<MmrHash>> for StorageProof {
430    fn from(p: MmrRootStorageProof<MmrHash>) -> StorageProof {
431        p.storage_proof
432    }
433}
434
435impl<Block: BlockT, MmrHash: Decode + Clone> BasicStorageProof<Block>
436    for MmrRootStorageProof<MmrHash>
437{
438    type StorageValue = MmrHash;
439    type Key = NumberFor<Block>;
440    fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
441        FraudProofStorageKeyRequest::MmrRoot(key)
442    }
443}