feat: adding rpc interfaces into sequencer and node

This commit is contained in:
Oleksandr Pravdyvyi
2024-09-30 05:49:46 +03:00
parent c95ed90c9a
commit 42cfac8a74
35 changed files with 1390 additions and 20 deletions
+39 -1
View File
@@ -1 +1,39 @@
//ToDo: Add node_rpc module
pub mod net_utils;
pub mod process;
pub mod types;
use rpc_primitives::{
errors::{RpcError, RpcErrorKind},
RpcPollingConfig,
};
use serde::Serialize;
use serde_json::Value;
pub use net_utils::*;
use self::types::err_rpc::RpcErr;
//ToDo: Add necessary fields
pub struct JsonHandler {
pub polling_config: RpcPollingConfig,
}
fn respond<T: Serialize>(val: T) -> Result<Value, RpcErr> {
Ok(serde_json::to_value(val)?)
}
pub fn rpc_error_responce_inverter(err: RpcError) -> RpcError {
let mut content: Option<Value> = None;
if err.error_struct.is_some() {
content = match err.error_struct.clone().unwrap() {
RpcErrorKind::HandlerError(val) | RpcErrorKind::InternalError(val) => Some(val),
RpcErrorKind::RequestValidationError(vall) => Some(serde_json::to_value(vall).unwrap()),
};
}
RpcError {
error_struct: None,
code: err.code,
message: err.message,
data: content,
}
}
+64
View File
@@ -0,0 +1,64 @@
use std::io;
use actix_cors::Cors;
use actix_web::{http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer};
use futures::Future;
use futures::FutureExt;
use log::info;
use rpc_primitives::message::Message;
use rpc_primitives::RpcConfig;
use super::JsonHandler;
pub const SHUTDOWN_TIMEOUT_SECS: u64 = 10;
fn rpc_handler(
message: web::Json<Message>,
handler: web::Data<JsonHandler>,
) -> impl Future<Output = Result<HttpResponse, HttpError>> {
let response = async move {
let message = handler.process(message.0).await?;
Ok(HttpResponse::Ok().json(&message))
};
response.boxed()
}
fn get_cors(cors_allowed_origins: &[String]) -> Cors {
let mut cors = Cors::permissive();
if cors_allowed_origins != ["*".to_string()] {
for origin in cors_allowed_origins {
cors = cors.allowed_origin(origin);
}
}
cors.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600)
}
#[allow(clippy::too_many_arguments)]
pub fn new_http_server(config: RpcConfig) -> io::Result<actix_web::dev::Server> {
let RpcConfig {
addr,
cors_allowed_origins,
polling_config,
limits_config,
} = config;
info!(target:"network", "Starting http server at {}", addr);
let handler = web::Data::new(JsonHandler { polling_config });
// HTTP server
Ok(HttpServer::new(move || {
App::new()
.wrap(get_cors(&cors_allowed_origins))
.app_data(handler.clone())
.app_data(web::JsonConfig::default().limit(limits_config.json_payload_max_size))
.wrap(middleware::Logger::default())
.service(web::resource("/").route(web::post().to(rpc_handler)))
})
.bind(addr)?
.shutdown_timeout(SHUTDOWN_TIMEOUT_SECS)
.disable_signals()
.run())
}
+53
View File
@@ -0,0 +1,53 @@
use actix_web::Error as HttpError;
use serde_json::Value;
use rpc_primitives::{
errors::RpcError,
message::{Message, Request},
parser::RpcRequest,
};
use crate::{
rpc_error_responce_inverter,
types::rpc_structs::{HelloRequest, HelloResponse},
};
use super::{respond, types::err_rpc::RpcErr, JsonHandler};
impl JsonHandler {
pub async fn process(&self, message: Message) -> Result<Message, HttpError> {
let id = message.id();
if let Message::Request(request) = message {
let message_inner = self
.process_request_internal(request)
.await
.map_err(|e| e.0)
.map_err(rpc_error_responce_inverter);
Ok(Message::response(id, message_inner))
} else {
Ok(Message::error(RpcError::parse_error(
"JSON RPC Request format was expected".to_owned(),
)))
}
}
#[allow(clippy::unused_async)]
///Example of request processing
async fn process_temp_hello(&self, request: Request) -> Result<Value, RpcErr> {
let _hello_request = HelloRequest::parse(Some(request.params))?;
let helperstruct = HelloResponse {
greeting: "HELLO_FROM_NODE".to_string(),
};
respond(helperstruct)
}
pub async fn process_request_internal(&self, request: Request) -> Result<Value, RpcErr> {
match request.method.as_ref() {
//Todo : Add handling of more JSON RPC methods
"hello" => self.process_temp_hello(request).await,
_ => Err(RpcErr(RpcError::method_not_found(request.method))),
}
}
}
+47
View File
@@ -0,0 +1,47 @@
use log::debug;
use rpc_primitives::errors::{RpcError, RpcParseError};
pub struct RpcErr(pub RpcError);
pub type RpcErrInternal = anyhow::Error;
pub trait RpcErrKind: 'static {
fn into_rpc_err(self) -> RpcError;
}
impl<T: RpcErrKind> From<T> for RpcErr {
fn from(e: T) -> Self {
Self(e.into_rpc_err())
}
}
macro_rules! standard_rpc_err_kind {
($type_name:path) => {
impl RpcErrKind for $type_name {
fn into_rpc_err(self) -> RpcError {
self.into()
}
}
};
}
standard_rpc_err_kind!(RpcError);
standard_rpc_err_kind!(RpcParseError);
impl RpcErrKind for serde_json::Error {
fn into_rpc_err(self) -> RpcError {
RpcError::serialization_error(&self.to_string())
}
}
impl RpcErrKind for RpcErrInternal {
fn into_rpc_err(self) -> RpcError {
RpcError::new_internal_error(None, &format!("{self:#?}"))
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn from_rpc_err_into_anyhow_err(rpc_err: RpcError) -> anyhow::Error {
debug!("Rpc error cast to anyhow error : err {rpc_err:?}");
anyhow::anyhow!(format!("{rpc_err:#?}"))
}
+3
View File
@@ -0,0 +1,3 @@
pub mod err_rpc;
pub mod parse;
pub mod rpc_structs;
+1
View File
@@ -0,0 +1 @@
+16
View File
@@ -0,0 +1,16 @@
use rpc_primitives::errors::RpcParseError;
use rpc_primitives::parse_request;
use rpc_primitives::parser::parse_params;
use rpc_primitives::parser::RpcRequest;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize, Debug)]
pub struct HelloRequest {}
parse_request!(HelloRequest);
#[derive(Serialize, Deserialize, Debug)]
pub struct HelloResponse {
pub greeting: String,
}