pallet_evm_precompile_relay_verifier/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
20
21use core::marker::PhantomData;
22use cumulus_primitives_core::relay_chain::BlockNumber as RelayBlockNumber;
23use fp_evm::{PrecompileFailure, PrecompileHandle};
24use frame_support::{ensure, traits::ConstU32};
25use pallet_precompile_benchmarks::WeightInfo as TWeightInfo;
26use precompile_utils::prelude::*;
27use sp_core::H256;
28use sp_std::vec::Vec;
29use storage_proof_primitives::*;
30
31#[cfg(test)]
32mod mock;
33#[cfg(test)]
34mod tests;
35
36pub const CALL_DATA_LIMIT: u32 = 2u32.pow(16);
37pub const ARRAY_LIMIT: u32 = 2048;
38pub const KEY_LENGTH_LIMIT: u32 = 256;
39
40pub type GetCallDataLimit = ConstU32<CALL_DATA_LIMIT>;
41pub type GetKeyLengthLimit = ConstU32<KEY_LENGTH_LIMIT>;
42pub type GetArrayLimit = ConstU32<ARRAY_LIMIT>;
43
44pub type RawKey = BoundedBytes<GetKeyLengthLimit>;
45
46pub struct RelayDataVerifierPrecompile<Runtime, WeightInfo>(PhantomData<(Runtime, WeightInfo)>);
48
49#[precompile_utils::precompile]
50impl<Runtime, WeightInfo> RelayDataVerifierPrecompile<Runtime, WeightInfo>
51where
52 Runtime: frame_system::Config + pallet_relay_storage_roots::Config + pallet_evm::Config,
53 WeightInfo: TWeightInfo,
54{
55 #[precompile::public("verifyEntry(uint32,(bytes32,bytes[]),bytes)")]
58 #[precompile::public("verify_entry(uint32,(bytes32,bytes[]),bytes)")]
59 fn verify_entry(
60 handle: &mut impl PrecompileHandle,
61 relay_block_number: RelayBlockNumber,
62 proof: ReadProof,
63 key: RawKey,
64 ) -> EvmResult<UnboundedBytes> {
65 let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
67 handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
68
69 let storage_root = Self::get_storage_root(handle, relay_block_number)?;
71
72 handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
74
75 verify_entry(storage_root, proof.to_raw_proof(), key.as_bytes())
77 .map_err(map_err)
78 .map(UnboundedBytes::from)
79 }
80
81 #[precompile::public("verifyEntries(uint32,(bytes32,bytes[]),bytes[])")]
85 #[precompile::public("verify_entries(uint32,(bytes32,bytes[]),bytes[])")]
86 fn verify_entries(
87 handle: &mut impl PrecompileHandle,
88 relay_block_number: RelayBlockNumber,
89 proof: ReadProof,
90 keys: BoundedVec<RawKey, GetArrayLimit>,
91 ) -> EvmResult<BoundedVec<UnboundedBytes, GetArrayLimit>> {
92 ensure!(keys.len() > 0, revert("Keys must not be empty"));
93
94 let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
96 handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
97
98 let storage_root = Self::get_storage_root(handle, relay_block_number)?;
100
101 handle.record_cost(
103 (keys.len() as u64).saturating_mul(RuntimeHelper::<Runtime>::db_read_gas_cost()),
104 )?;
105
106 let keys = Vec::from(keys);
108 let keys: Vec<_> = keys.iter().map(|x| x.as_bytes()).collect();
109 verify_entries(storage_root, proof.to_raw_proof(), &keys)
110 .map_err(map_err)
111 .map(|x| x.into_iter().map(UnboundedBytes::from).collect::<Vec<_>>())
112 .map(|x| BoundedVec::from(x))
113 }
114
115 #[precompile::public("latestRelayBlockNumber()")]
116 #[precompile::public("latest_relay_block_number()")]
117 #[precompile::view]
118 fn latest_relay_block(handle: &mut impl PrecompileHandle) -> EvmResult<RelayBlockNumber> {
119 let weight = WeightInfo::latest_relay_block();
120 handle.record_external_cost(Some(weight.ref_time()), Some(weight.proof_size()), Some(0))?;
121
122 pallet_relay_storage_roots::RelayStorageRootKeys::<Runtime>::get()
123 .last()
124 .cloned()
125 .ok_or(revert("No relay block found"))
126 }
127
128 fn get_storage_root(
129 handle: &mut impl PrecompileHandle,
130 relay_block_number: RelayBlockNumber,
131 ) -> EvmResult<H256> {
132 handle.record_db_read::<Runtime>(84)?;
133 pallet_relay_storage_roots::RelayStorageRoot::<Runtime>::get(relay_block_number)
134 .ok_or(revert("Block number not present"))
135 }
136}
137
138fn map_err(err: ProofError) -> PrecompileFailure {
139 match err {
140 ProofError::RootMismatch => revert("Root Mismatch"),
141 ProofError::Proof => revert("Invalid Proof"),
142 ProofError::Absent => revert("Value is not present"),
143 ProofError::BlockNumberNotPresent => revert("Block number not present"),
144 }
145}
146
147#[derive(Clone, Debug, solidity::Codec)]
148pub struct ReadProof {
149 pub at: H256,
151 pub proof: BoundedVec<BoundedBytes<GetCallDataLimit>, GetArrayLimit>,
153}
154
155impl ReadProof {
156 pub fn to_raw_proof(self) -> Vec<Vec<u8>> {
157 Vec::from(self.proof)
158 .iter()
159 .map(|x| x.as_bytes().to_vec())
160 .collect()
161 }
162}