1#![warn(missing_docs)]
19#![warn(unused_crate_dependencies)]
20
21use std::{marker::PhantomData, sync::Arc};
24
25use codec::{Codec, Decode, Encode};
26use jsonrpsee::{
27 core::{async_trait, RpcResult},
28 proc_macros::rpc,
29 types::{error::ErrorObject, ErrorObjectOwned},
30};
31use serde::{Deserialize, Serialize};
32
33use sp_api::{ApiExt, ProvideRuntimeApi};
34use sp_blockchain::HeaderBackend;
35use sp_core::{
36 offchain::{storage::OffchainDb, OffchainDbExt, OffchainStorage},
37 Bytes,
38};
39use sp_mmr_primitives::{Error as MmrError, LeafProof};
40use sp_runtime::traits::{Block as BlockT, NumberFor};
41
42pub use sp_mmr_primitives::MmrApi as MmrRuntimeApi;
43
44const RUNTIME_ERROR: i32 = 8000;
45const MMR_ERROR: i32 = 8010;
46
47#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
49#[serde(rename_all = "camelCase")]
50pub struct LeavesProof<BlockHash> {
51 pub block_hash: BlockHash,
53 pub leaves: Bytes,
55 pub proof: Bytes,
57}
58
59impl<BlockHash> LeavesProof<BlockHash> {
60 pub fn new<Leaf, MmrHash>(
63 block_hash: BlockHash,
64 leaves: Vec<Leaf>,
65 proof: LeafProof<MmrHash>,
66 ) -> Self
67 where
68 Leaf: Encode,
69 MmrHash: Encode,
70 {
71 Self { block_hash, leaves: Bytes(leaves.encode()), proof: Bytes(proof.encode()) }
72 }
73}
74
75#[rpc(client, server)]
77pub trait MmrApi<BlockHash, BlockNumber, MmrHash> {
78 #[method(name = "mmr_root")]
80 fn mmr_root(&self, at: Option<BlockHash>) -> RpcResult<MmrHash>;
81
82 #[method(name = "mmr_generateProof")]
102 fn generate_proof(
103 &self,
104 block_numbers: Vec<BlockNumber>,
105 best_known_block_number: Option<BlockNumber>,
106 at: Option<BlockHash>,
107 ) -> RpcResult<LeavesProof<BlockHash>>;
108
109 #[method(name = "mmr_verifyProof")]
116 fn verify_proof(&self, proof: LeavesProof<BlockHash>) -> RpcResult<bool>;
117
118 #[method(name = "mmr_verifyProofStateless")]
125 fn verify_proof_stateless(
126 &self,
127 mmr_root: MmrHash,
128 proof: LeavesProof<BlockHash>,
129 ) -> RpcResult<bool>;
130}
131
132pub struct Mmr<Client, Block, S> {
134 client: Arc<Client>,
135 offchain_db: OffchainDb<S>,
136 _marker: PhantomData<Block>,
137}
138
139impl<C, B, S> Mmr<C, B, S> {
140 pub fn new(client: Arc<C>, offchain_storage: S) -> Self {
142 Self { client, _marker: Default::default(), offchain_db: OffchainDb::new(offchain_storage) }
143 }
144}
145
146#[async_trait]
147impl<Client, Block, MmrHash, S> MmrApiServer<<Block as BlockT>::Hash, NumberFor<Block>, MmrHash>
148 for Mmr<Client, (Block, MmrHash), S>
149where
150 Block: BlockT,
151 Client: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
152 Client::Api: MmrRuntimeApi<Block, MmrHash, NumberFor<Block>>,
153 MmrHash: Codec + Send + Sync + 'static,
154 S: OffchainStorage + 'static,
155{
156 fn mmr_root(&self, at: Option<<Block as BlockT>::Hash>) -> RpcResult<MmrHash> {
157 let block_hash = at.unwrap_or_else(||
158 self.client.info().best_hash);
160 let api = self.client.runtime_api();
161 let mmr_root = api
162 .mmr_root(block_hash)
163 .map_err(runtime_error_into_rpc_error)?
164 .map_err(mmr_error_into_rpc_error)?;
165 Ok(mmr_root)
166 }
167
168 fn generate_proof(
169 &self,
170 block_numbers: Vec<NumberFor<Block>>,
171 best_known_block_number: Option<NumberFor<Block>>,
172 at: Option<<Block as BlockT>::Hash>,
173 ) -> RpcResult<LeavesProof<<Block as BlockT>::Hash>> {
174 let mut api = self.client.runtime_api();
175 let block_hash = at.unwrap_or_else(||
176 self.client.info().best_hash);
178
179 api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
180
181 let (leaves, proof) = api
182 .generate_proof(block_hash, block_numbers, best_known_block_number)
183 .map_err(runtime_error_into_rpc_error)?
184 .map_err(mmr_error_into_rpc_error)?;
185
186 Ok(LeavesProof::new(block_hash, leaves, proof))
187 }
188
189 fn verify_proof(&self, proof: LeavesProof<<Block as BlockT>::Hash>) -> RpcResult<bool> {
190 let mut api = self.client.runtime_api();
191
192 let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
193
194 let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
195
196 api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
197
198 api.verify_proof(proof.block_hash, leaves, decoded_proof)
199 .map_err(runtime_error_into_rpc_error)?
200 .map_err(mmr_error_into_rpc_error)?;
201
202 Ok(true)
203 }
204
205 fn verify_proof_stateless(
206 &self,
207 mmr_root: MmrHash,
208 proof: LeavesProof<<Block as BlockT>::Hash>,
209 ) -> RpcResult<bool> {
210 let api = self.client.runtime_api();
211
212 let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
213
214 let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
215
216 api.verify_proof_stateless(proof.block_hash, mmr_root, leaves, decoded_proof)
217 .map_err(runtime_error_into_rpc_error)?
218 .map_err(mmr_error_into_rpc_error)?;
219
220 Ok(true)
221 }
222}
223
224fn mmr_error_into_rpc_error(err: MmrError) -> ErrorObjectOwned {
226 let error_code = MMR_ERROR +
227 match err {
228 MmrError::LeafNotFound => 1,
229 MmrError::GenerateProof => 2,
230 MmrError::Verify => 3,
231 MmrError::InvalidNumericOp => 4,
232 MmrError::InvalidBestKnownBlock => 5,
233 _ => 0,
234 };
235
236 ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err)))
237}
238
239fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> ErrorObjectOwned {
241 ErrorObject::owned(RUNTIME_ERROR, "Runtime trapped", Some(format!("{:?}", err)))
242}
243
244fn invalid_params(e: impl std::error::Error) -> ErrorObjectOwned {
245 ErrorObject::owned(
246 jsonrpsee::types::error::ErrorCode::InvalidParams.code(),
247 e.to_string(),
248 None::<()>,
249 )
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use sp_core::H256;
256
257 #[test]
258 fn should_serialize_leaf_proof() {
259 let leaf = vec![1_u8, 2, 3, 4];
261 let proof = LeafProof {
262 leaf_indices: vec![1],
263 leaf_count: 9,
264 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
265 };
266
267 let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf], proof);
268
269 let actual = serde_json::to_string(&leaf_proof).unwrap();
271
272 assert_eq!(
274 actual,
275 r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
276 );
277 }
278
279 #[test]
280 fn should_serialize_leaves_proof() {
281 let leaf_a = vec![1_u8, 2, 3, 4];
283 let leaf_b = vec![2_u8, 2, 3, 4];
284 let proof = LeafProof {
285 leaf_indices: vec![1, 2],
286 leaf_count: 9,
287 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
288 };
289
290 let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf_a, leaf_b], proof);
291
292 let actual = serde_json::to_string(&leaf_proof).unwrap();
294
295 assert_eq!(
297 actual,
298 r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x0810010203041002020304","proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
299 );
300 }
301
302 #[test]
303 fn should_deserialize_leaf_proof() {
304 let expected = LeavesProof {
306 block_hash: H256::repeat_byte(0),
307 leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()),
308 proof: Bytes(
309 LeafProof {
310 leaf_indices: vec![1],
311 leaf_count: 9,
312 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
313 }
314 .encode(),
315 ),
316 };
317
318 let actual: LeavesProof<H256> = serde_json::from_str(r#"{
320 "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
321 "leaves":"0x041001020304",
322 "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
323 }"#).unwrap();
324
325 assert_eq!(actual, expected);
327 }
328
329 #[test]
330 fn should_deserialize_leaves_proof() {
331 let expected = LeavesProof {
333 block_hash: H256::repeat_byte(0),
334 leaves: Bytes(vec![vec![1_u8, 2, 3, 4], vec![2_u8, 2, 3, 4]].encode()),
335 proof: Bytes(
336 LeafProof {
337 leaf_indices: vec![1, 2],
338 leaf_count: 9,
339 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
340 }
341 .encode(),
342 ),
343 };
344
345 let actual: LeavesProof<H256> = serde_json::from_str(r#"{
347 "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
348 "leaves":"0x0810010203041002020304",
349 "proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
350 }"#).unwrap();
351
352 assert_eq!(actual, expected);
354 }
355}