#![cfg_attr(not(feature = "std"), no_std)]
use core::marker::PhantomData;
use cumulus_primitives_core::relay_chain::BlockNumber as RelayBlockNumber;
use fp_evm::{PrecompileFailure, PrecompileHandle};
use frame_support::{ensure, traits::ConstU32};
use pallet_precompile_benchmarks::WeightInfo as TWeightInfo;
use precompile_utils::prelude::*;
use sp_core::H256;
use sp_std::vec::Vec;
use storage_proof_primitives::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub const CALL_DATA_LIMIT: u32 = 2u32.pow(16);
pub const ARRAY_LIMIT: u32 = 2048;
pub const KEY_LENGTH_LIMIT: u32 = 256;
pub type GetCallDataLimit = ConstU32<CALL_DATA_LIMIT>;
pub type GetKeyLengthLimit = ConstU32<KEY_LENGTH_LIMIT>;
pub type GetArrayLimit = ConstU32<ARRAY_LIMIT>;
pub type RawKey = BoundedBytes<GetKeyLengthLimit>;
pub struct RelayDataVerifierPrecompile<Runtime, WeightInfo>(PhantomData<(Runtime, WeightInfo)>);
#[precompile_utils::precompile]
impl<Runtime, WeightInfo> RelayDataVerifierPrecompile<Runtime, WeightInfo>
where
Runtime: frame_system::Config
+ pallet_relay_storage_roots::Config
+ pallet_evm::Config
+ pallet_precompile_benchmarks::Config,
WeightInfo: TWeightInfo,
{
#[precompile::public("verifyEntry(uint32,(bytes32,bytes[]),bytes)")]
#[precompile::public("verify_entry(uint32,(bytes32,bytes[]),bytes)")]
fn verify_entry(
handle: &mut impl PrecompileHandle,
relay_block_number: RelayBlockNumber,
proof: ReadProof,
key: RawKey,
) -> EvmResult<UnboundedBytes> {
let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
let storage_root = Self::get_storage_root(handle, relay_block_number)?;
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
verify_entry(storage_root, proof.to_raw_proof(), key.as_bytes())
.map_err(map_err)
.map(UnboundedBytes::from)
}
#[precompile::public("verifyEntries(uint32,(bytes32,bytes[]),bytes[])")]
#[precompile::public("verify_entries(uint32,(bytes32,bytes[]),bytes[])")]
fn verify_entries(
handle: &mut impl PrecompileHandle,
relay_block_number: RelayBlockNumber,
proof: ReadProof,
keys: BoundedVec<RawKey, GetArrayLimit>,
) -> EvmResult<BoundedVec<UnboundedBytes, GetArrayLimit>> {
ensure!(keys.len() > 0, revert("Keys must not be empty"));
let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
let storage_root = Self::get_storage_root(handle, relay_block_number)?;
handle.record_cost(
(keys.len() as u64).saturating_mul(RuntimeHelper::<Runtime>::db_read_gas_cost()),
)?;
let keys = Vec::from(keys);
let keys: Vec<_> = keys.iter().map(|x| x.as_bytes()).collect();
verify_entries(storage_root, proof.to_raw_proof(), &keys)
.map_err(map_err)
.map(|x| x.into_iter().map(UnboundedBytes::from).collect::<Vec<_>>())
.map(|x| BoundedVec::from(x))
}
#[precompile::public("latestRelayBlockNumber()")]
#[precompile::public("latest_relay_block_number()")]
#[precompile::view]
fn latest_relay_block(handle: &mut impl PrecompileHandle) -> EvmResult<RelayBlockNumber> {
let weight = WeightInfo::latest_relay_block();
handle.record_external_cost(Some(weight.ref_time()), Some(weight.proof_size()), Some(0))?;
pallet_relay_storage_roots::RelayStorageRootKeys::<Runtime>::get()
.last()
.cloned()
.ok_or(revert("No relay block found"))
}
fn get_storage_root(
handle: &mut impl PrecompileHandle,
relay_block_number: RelayBlockNumber,
) -> EvmResult<H256> {
handle.record_db_read::<Runtime>(84)?;
pallet_relay_storage_roots::RelayStorageRoot::<Runtime>::get(relay_block_number)
.ok_or(revert("Block number not present"))
}
}
fn map_err(err: ProofError) -> PrecompileFailure {
match err {
ProofError::RootMismatch => revert("Root Mismatch"),
ProofError::Proof => revert("Invalid Proof"),
ProofError::Absent => revert("Value is not present"),
ProofError::BlockNumberNotPresent => revert("Block number not present"),
}
}
#[derive(Clone, Debug, solidity::Codec)]
pub struct ReadProof {
pub at: H256,
pub proof: BoundedVec<BoundedBytes<GetCallDataLimit>, GetArrayLimit>,
}
impl ReadProof {
pub fn to_raw_proof(self) -> Vec<Vec<u8>> {
Vec::from(self.proof)
.iter()
.map(|x| x.as_bytes().to_vec())
.collect()
}
}