sp_domains_fraud_proof/
storage_proof.rs

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