Skip to main content

forest/rpc/
validation_layer.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use futures::future::Either;
5use jsonrpsee::MethodResponse;
6use jsonrpsee::core::middleware::{Batch, BatchEntry, BatchEntryErr, Notification};
7use jsonrpsee::server::middleware::rpc::RpcServiceT;
8use jsonrpsee::types::ErrorObject;
9use tower::Layer;
10
11use super::json_validator;
12
13/// JSON-RPC error code for invalid request
14const INVALID_REQUEST: i32 = -32600;
15
16/// stateless jsonrpsee layer for validating JSON-RPC requests
17#[derive(Clone, Default)]
18pub(super) struct JsonValidationLayer;
19
20impl<S> Layer<S> for JsonValidationLayer {
21    type Service = Validation<S>;
22
23    fn layer(&self, service: S) -> Self::Service {
24        Validation { service }
25    }
26}
27
28#[derive(Clone)]
29pub(super) struct Validation<S> {
30    service: S,
31}
32
33impl<S> Validation<S> {
34    fn validation_enabled() -> bool {
35        json_validator::is_strict_mode()
36    }
37
38    fn validate_params(params_str: &str) -> Result<(), String> {
39        json_validator::validate_json_for_duplicates(params_str)
40    }
41}
42
43impl<S> RpcServiceT for Validation<S>
44where
45    S: RpcServiceT<MethodResponse = MethodResponse, NotificationResponse = MethodResponse>
46        + Send
47        + Sync
48        + Clone
49        + 'static,
50{
51    type MethodResponse = S::MethodResponse;
52    type NotificationResponse = S::NotificationResponse;
53    type BatchResponse = S::BatchResponse;
54
55    fn call<'a>(
56        &self,
57        req: jsonrpsee::types::Request<'a>,
58    ) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
59        if !Self::validation_enabled() {
60            return Either::Left(self.service.call(req));
61        }
62
63        if let Err(e) =
64            json_validator::validate_json_for_duplicates(req.params().as_str().unwrap_or("[]"))
65        {
66            let err = ErrorObject::owned(INVALID_REQUEST, e, None::<()>);
67            return Either::Right(async move { MethodResponse::error(req.id(), err) });
68        }
69
70        Either::Left(self.service.call(req))
71    }
72
73    fn batch<'a>(
74        &self,
75        mut batch: Batch<'a>,
76    ) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
77        let service = self.service.clone();
78
79        async move {
80            if !Self::validation_enabled() {
81                return service.batch(batch).await;
82            }
83
84            for entry in batch.iter_mut() {
85                if let Ok(batch_entry) = entry {
86                    let params_str = match batch_entry.params() {
87                        Some(p) => p.as_ref().get(),
88                        None => continue,
89                    };
90
91                    if let Err(e) = Self::validate_params(params_str) {
92                        match batch_entry {
93                            BatchEntry::Call(req) => {
94                                let err = ErrorObject::owned(INVALID_REQUEST, e, None::<()>);
95                                let err_entry = BatchEntryErr::new(req.id().clone(), err);
96                                *entry = Err(err_entry);
97                            }
98                            BatchEntry::Notification(_notif) => {
99                                let err = ErrorObject::owned(INVALID_REQUEST, e, None::<()>);
100                                let err_entry = BatchEntryErr::new(jsonrpsee::types::Id::Null, err);
101                                *entry = Err(err_entry);
102                            }
103                        }
104                    }
105                }
106            }
107
108            service.batch(batch).await
109        }
110    }
111
112    fn notification<'a>(
113        &self,
114        n: Notification<'a>,
115    ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
116        let service = self.service.clone();
117
118        async move {
119            if !Self::validation_enabled() {
120                return service.notification(n).await;
121            }
122
123            let params_str = match n.params() {
124                Some(p) => p.as_ref().get(),
125                None => return service.notification(n).await,
126            };
127
128            if let Err(e) = Self::validate_params(params_str) {
129                let err = ErrorObject::owned(INVALID_REQUEST, e, None::<()>);
130                return MethodResponse::error(jsonrpsee::types::Id::Null, err);
131            }
132
133            service.notification(n).await
134        }
135    }
136}