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::{AncestryProof as MmrAncestryProof, 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_generateAncestryProof")]
128 fn generate_ancestry_proof(
129 &self,
130 prev_block_number: BlockNumber,
131 best_known_block_number: Option<BlockNumber>,
132 at: Option<BlockHash>,
133 ) -> RpcResult<MmrAncestryProof<MmrHash>>;
134
135 #[method(name = "mmr_verifyProof")]
142 fn verify_proof(&self, proof: LeavesProof<BlockHash>) -> RpcResult<bool>;
143
144 #[method(name = "mmr_verifyProofStateless")]
151 fn verify_proof_stateless(
152 &self,
153 mmr_root: MmrHash,
154 proof: LeavesProof<BlockHash>,
155 ) -> RpcResult<bool>;
156}
157
158pub struct Mmr<Client, Block, S> {
160 client: Arc<Client>,
161 offchain_db: OffchainDb<S>,
162 _marker: PhantomData<Block>,
163}
164
165impl<C, B, S> Mmr<C, B, S> {
166 pub fn new(client: Arc<C>, offchain_storage: S) -> Self {
168 Self { client, _marker: Default::default(), offchain_db: OffchainDb::new(offchain_storage) }
169 }
170}
171
172#[async_trait]
173impl<Client, Block, MmrHash, S> MmrApiServer<<Block as BlockT>::Hash, NumberFor<Block>, MmrHash>
174 for Mmr<Client, (Block, MmrHash), S>
175where
176 Block: BlockT,
177 Client: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
178 Client::Api: MmrRuntimeApi<Block, MmrHash, NumberFor<Block>>,
179 MmrHash: Codec + Send + Sync + 'static,
180 S: OffchainStorage + 'static,
181{
182 fn mmr_root(&self, at: Option<<Block as BlockT>::Hash>) -> RpcResult<MmrHash> {
183 let block_hash = at.unwrap_or_else(||
184 self.client.info().best_hash);
186 let api = self.client.runtime_api();
187 let mmr_root = api
188 .mmr_root(block_hash)
189 .map_err(runtime_error_into_rpc_error)?
190 .map_err(mmr_error_into_rpc_error)?;
191 Ok(mmr_root)
192 }
193
194 fn generate_proof(
195 &self,
196 block_numbers: Vec<NumberFor<Block>>,
197 best_known_block_number: Option<NumberFor<Block>>,
198 at: Option<<Block as BlockT>::Hash>,
199 ) -> RpcResult<LeavesProof<<Block as BlockT>::Hash>> {
200 let mut api = self.client.runtime_api();
201 let block_hash = at.unwrap_or_else(||
202 self.client.info().best_hash);
204
205 api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
206
207 let (leaves, proof) = api
208 .generate_proof(block_hash, block_numbers, best_known_block_number)
209 .map_err(runtime_error_into_rpc_error)?
210 .map_err(mmr_error_into_rpc_error)?;
211
212 Ok(LeavesProof::new(block_hash, leaves, proof))
213 }
214
215 fn generate_ancestry_proof(
216 &self,
217 prev_block_number: NumberFor<Block>,
218 best_known_block_number: Option<NumberFor<Block>>,
219 at: Option<<Block as BlockT>::Hash>,
220 ) -> RpcResult<MmrAncestryProof<MmrHash>> {
221 let mut api = self.client.runtime_api();
222 let block_hash = at.unwrap_or_else(||
223 self.client.info().best_hash);
225
226 api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
227
228 let proof = api
229 .generate_ancestry_proof(block_hash, prev_block_number, best_known_block_number)
230 .map_err(runtime_error_into_rpc_error)?
231 .map_err(mmr_error_into_rpc_error)?;
232
233 Ok(proof)
234 }
235
236 fn verify_proof(&self, proof: LeavesProof<<Block as BlockT>::Hash>) -> RpcResult<bool> {
237 let mut api = self.client.runtime_api();
238
239 let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
240
241 let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
242
243 api.register_extension(OffchainDbExt::new(self.offchain_db.clone()));
244
245 api.verify_proof(proof.block_hash, leaves, decoded_proof)
246 .map_err(runtime_error_into_rpc_error)?
247 .map_err(mmr_error_into_rpc_error)?;
248
249 Ok(true)
250 }
251
252 fn verify_proof_stateless(
253 &self,
254 mmr_root: MmrHash,
255 proof: LeavesProof<<Block as BlockT>::Hash>,
256 ) -> RpcResult<bool> {
257 let api = self.client.runtime_api();
258
259 let leaves = Decode::decode(&mut &proof.leaves.0[..]).map_err(invalid_params)?;
260
261 let decoded_proof = Decode::decode(&mut &proof.proof.0[..]).map_err(invalid_params)?;
262
263 api.verify_proof_stateless(proof.block_hash, mmr_root, leaves, decoded_proof)
264 .map_err(runtime_error_into_rpc_error)?
265 .map_err(mmr_error_into_rpc_error)?;
266
267 Ok(true)
268 }
269}
270
271fn mmr_error_into_rpc_error(err: MmrError) -> ErrorObjectOwned {
273 let error_code = MMR_ERROR +
274 match err {
275 MmrError::LeafNotFound => 1,
276 MmrError::GenerateProof => 2,
277 MmrError::Verify => 3,
278 MmrError::InvalidNumericOp => 4,
279 MmrError::InvalidBestKnownBlock => 5,
280 _ => 0,
281 };
282
283 ErrorObject::owned(error_code, err.to_string(), Some(format!("{:?}", err)))
284}
285
286fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> ErrorObjectOwned {
288 ErrorObject::owned(RUNTIME_ERROR, "Runtime trapped", Some(format!("{:?}", err)))
289}
290
291fn invalid_params(e: impl std::error::Error) -> ErrorObjectOwned {
292 ErrorObject::owned(
293 jsonrpsee::types::error::ErrorCode::InvalidParams.code(),
294 e.to_string(),
295 None::<()>,
296 )
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use sp_core::H256;
303
304 #[test]
305 fn should_serialize_leaf_proof() {
306 let leaf = vec![1_u8, 2, 3, 4];
308 let proof = LeafProof {
309 leaf_indices: vec![1],
310 leaf_count: 9,
311 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
312 };
313
314 let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf], proof);
315
316 let actual = serde_json::to_string(&leaf_proof).unwrap();
318
319 assert_eq!(
321 actual,
322 r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x041001020304","proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
323 );
324 }
325
326 #[test]
327 fn should_serialize_leaves_proof() {
328 let leaf_a = vec![1_u8, 2, 3, 4];
330 let leaf_b = vec![2_u8, 2, 3, 4];
331 let proof = LeafProof {
332 leaf_indices: vec![1, 2],
333 leaf_count: 9,
334 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
335 };
336
337 let leaf_proof = LeavesProof::new(H256::repeat_byte(0), vec![leaf_a, leaf_b], proof);
338
339 let actual = serde_json::to_string(&leaf_proof).unwrap();
341
342 assert_eq!(
344 actual,
345 r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaves":"0x0810010203041002020304","proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
346 );
347 }
348
349 #[test]
350 fn should_deserialize_leaf_proof() {
351 let expected = LeavesProof {
353 block_hash: H256::repeat_byte(0),
354 leaves: Bytes(vec![vec![1_u8, 2, 3, 4]].encode()),
355 proof: Bytes(
356 LeafProof {
357 leaf_indices: vec![1],
358 leaf_count: 9,
359 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
360 }
361 .encode(),
362 ),
363 };
364
365 let actual: LeavesProof<H256> = serde_json::from_str(r#"{
367 "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
368 "leaves":"0x041001020304",
369 "proof":"0x04010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
370 }"#).unwrap();
371
372 assert_eq!(actual, expected);
374 }
375
376 #[test]
377 fn should_deserialize_leaves_proof() {
378 let expected = LeavesProof {
380 block_hash: H256::repeat_byte(0),
381 leaves: Bytes(vec![vec![1_u8, 2, 3, 4], vec![2_u8, 2, 3, 4]].encode()),
382 proof: Bytes(
383 LeafProof {
384 leaf_indices: vec![1, 2],
385 leaf_count: 9,
386 items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
387 }
388 .encode(),
389 ),
390 };
391
392 let actual: LeavesProof<H256> = serde_json::from_str(r#"{
394 "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
395 "leaves":"0x0810010203041002020304",
396 "proof":"0x080100000000000000020000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
397 }"#).unwrap();
398
399 assert_eq!(actual, expected);
401 }
402}