1
// Copyright 2025 Moonbeam Foundation.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Precompile for verifying relay entries against a relay block number.
18

            
19
#![cfg_attr(not(feature = "std"), no_std)]
20

            
21
use core::marker::PhantomData;
22
use cumulus_primitives_core::relay_chain::BlockNumber as RelayBlockNumber;
23
use fp_evm::{PrecompileFailure, PrecompileHandle};
24
use frame_support::{ensure, traits::ConstU32};
25
use pallet_precompile_benchmarks::WeightInfo as TWeightInfo;
26
use precompile_utils::prelude::*;
27
use sp_core::H256;
28
use sp_std::vec::Vec;
29
use storage_proof_primitives::*;
30

            
31
#[cfg(test)]
32
mod mock;
33
#[cfg(test)]
34
mod tests;
35

            
36
pub const CALL_DATA_LIMIT: u32 = 2u32.pow(16);
37
pub const ARRAY_LIMIT: u32 = 2048;
38
pub const KEY_LENGTH_LIMIT: u32 = 256;
39

            
40
pub type GetCallDataLimit = ConstU32<CALL_DATA_LIMIT>;
41
pub type GetKeyLengthLimit = ConstU32<KEY_LENGTH_LIMIT>;
42
pub type GetArrayLimit = ConstU32<ARRAY_LIMIT>;
43

            
44
pub type RawKey = BoundedBytes<GetKeyLengthLimit>;
45

            
46
/// Relay Data Verifier precompile.
47
pub struct RelayDataVerifierPrecompile<Runtime, WeightInfo>(PhantomData<(Runtime, WeightInfo)>);
48

            
49
90
#[precompile_utils::precompile]
50
impl<Runtime, WeightInfo> RelayDataVerifierPrecompile<Runtime, WeightInfo>
51
where
52
	Runtime: frame_system::Config + pallet_relay_storage_roots::Config + pallet_evm::Config,
53
	WeightInfo: TWeightInfo,
54
{
55
	/// Verify the storage entry using the provided relay block number and proof. Return the value
56
	/// of the storage entry if the proof is valid and the entry exists.
57
	#[precompile::public("verifyEntry(uint32,(bytes32,bytes[]),bytes)")]
58
	#[precompile::public("verify_entry(uint32,(bytes32,bytes[]),bytes)")]
59
4
	fn verify_entry(
60
4
		handle: &mut impl PrecompileHandle,
61
4
		relay_block_number: RelayBlockNumber,
62
4
		proof: ReadProof,
63
4
		key: RawKey,
64
4
	) -> EvmResult<UnboundedBytes> {
65
4
		// Charge gas for storage proof verification
66
4
		let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
67
4
		handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
68

            
69
		// Get the storage root of the relay block
70
4
		let storage_root = Self::get_storage_root(handle, relay_block_number)?;
71

            
72
		// One read per key
73
3
		handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
74

            
75
		// Read and return the value associated ton the key
76
3
		verify_entry(storage_root, proof.to_raw_proof(), key.as_bytes())
77
3
			.map_err(map_err)
78
3
			.map(UnboundedBytes::from)
79
4
	}
80

            
81
	/// Verify the storage entries using the provided relay block number and proof. Return the
82
	/// values of the storage entries in the same order of keys, if the proof is valid and the
83
	/// entries exist.
84
	#[precompile::public("verifyEntries(uint32,(bytes32,bytes[]),bytes[])")]
85
	#[precompile::public("verify_entries(uint32,(bytes32,bytes[]),bytes[])")]
86
5
	fn verify_entries(
87
5
		handle: &mut impl PrecompileHandle,
88
5
		relay_block_number: RelayBlockNumber,
89
5
		proof: ReadProof,
90
5
		keys: BoundedVec<RawKey, GetArrayLimit>,
91
5
	) -> EvmResult<BoundedVec<UnboundedBytes, GetArrayLimit>> {
92
5
		ensure!(keys.len() > 0, revert("Keys must not be empty"));
93

            
94
		//  Charge gas for storage proof verification
95
4
		let weight = WeightInfo::verify_entry(proof.proof.len() as u32);
96
4
		handle.record_external_cost(Some(weight.ref_time()), Some(0), Some(0))?;
97

            
98
		// Get the storage root of the relay block
99
4
		let storage_root = Self::get_storage_root(handle, relay_block_number)?;
100

            
101
		// Charge one db read per key
102
3
		handle.record_cost(
103
3
			(keys.len() as u64).saturating_mul(RuntimeHelper::<Runtime>::db_read_gas_cost()),
104
3
		)?;
105

            
106
		// Read and return the values associated ton the keys
107
3
		let keys = Vec::from(keys);
108
8
		let keys: Vec<_> = keys.iter().map(|x| x.as_bytes()).collect();
109
3
		verify_entries(storage_root, proof.to_raw_proof(), &keys)
110
3
			.map_err(map_err)
111
3
			.map(|x| x.into_iter().map(UnboundedBytes::from).collect::<Vec<_>>())
112
3
			.map(|x| BoundedVec::from(x))
113
5
	}
114

            
115
	#[precompile::public("latestRelayBlockNumber()")]
116
	#[precompile::public("latest_relay_block_number()")]
117
	#[precompile::view]
118
4
	fn latest_relay_block(handle: &mut impl PrecompileHandle) -> EvmResult<RelayBlockNumber> {
119
4
		let weight = WeightInfo::latest_relay_block();
120
4
		handle.record_external_cost(Some(weight.ref_time()), Some(weight.proof_size()), Some(0))?;
121

            
122
4
		pallet_relay_storage_roots::RelayStorageRootKeys::<Runtime>::get()
123
4
			.last()
124
4
			.cloned()
125
4
			.ok_or(revert("No relay block found"))
126
4
	}
127

            
128
8
	fn get_storage_root(
129
8
		handle: &mut impl PrecompileHandle,
130
8
		relay_block_number: RelayBlockNumber,
131
8
	) -> EvmResult<H256> {
132
8
		handle.record_db_read::<Runtime>(84)?;
133
8
		pallet_relay_storage_roots::RelayStorageRoot::<Runtime>::get(relay_block_number)
134
8
			.ok_or(revert("Block number not present"))
135
8
	}
136
}
137

            
138
4
fn map_err(err: ProofError) -> PrecompileFailure {
139
4
	match err {
140
2
		ProofError::RootMismatch => revert("Root Mismatch"),
141
		ProofError::Proof => revert("Invalid Proof"),
142
2
		ProofError::Absent => revert("Value is not present"),
143
		ProofError::BlockNumberNotPresent => revert("Block number not present"),
144
	}
145
4
}
146

            
147
11
#[derive(Clone, Debug, solidity::Codec)]
148
pub struct ReadProof {
149
	// Block Hash used to generate the proof
150
	pub at: H256,
151
	// A storage proof
152
	pub proof: BoundedVec<BoundedBytes<GetCallDataLimit>, GetArrayLimit>,
153
}
154

            
155
impl ReadProof {
156
6
	pub fn to_raw_proof(self) -> Vec<Vec<u8>> {
157
6
		Vec::from(self.proof)
158
6
			.iter()
159
48
			.map(|x| x.as_bytes().to_vec())
160
6
			.collect()
161
6
	}
162
}