-
Notifications
You must be signed in to change notification settings - Fork 194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(katana-rpc): rpc server builder #2788
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,18 +8,14 @@ pub mod exit; | |||||||||||||||||||||||||
pub mod version; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
use std::future::IntoFuture; | ||||||||||||||||||||||||||
use std::net::SocketAddr; | ||||||||||||||||||||||||||
use std::sync::Arc; | ||||||||||||||||||||||||||
use std::time::Duration; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
use anyhow::Result; | ||||||||||||||||||||||||||
use config::rpc::{ApiKind, RpcConfig}; | ||||||||||||||||||||||||||
use config::Config; | ||||||||||||||||||||||||||
use dojo_metrics::exporters::prometheus::PrometheusRecorder; | ||||||||||||||||||||||||||
use dojo_metrics::{Report, Server as MetricsServer}; | ||||||||||||||||||||||||||
use hyper::{Method, Uri}; | ||||||||||||||||||||||||||
use jsonrpsee::server::middleware::proxy_get_request::ProxyGetRequestLayer; | ||||||||||||||||||||||||||
use jsonrpsee::server::{AllowHosts, ServerBuilder, ServerHandle}; | ||||||||||||||||||||||||||
use hyper::Method; | ||||||||||||||||||||||||||
use jsonrpsee::RpcModule; | ||||||||||||||||||||||||||
use katana_core::backend::gas_oracle::L1GasOracle; | ||||||||||||||||||||||||||
use katana_core::backend::storage::Blockchain; | ||||||||||||||||||||||||||
|
@@ -37,19 +33,19 @@ use katana_pool::ordering::FiFo; | |||||||||||||||||||||||||
use katana_pool::TxPool; | ||||||||||||||||||||||||||
use katana_primitives::block::GasPrices; | ||||||||||||||||||||||||||
use katana_primitives::env::{CfgEnv, FeeTokenAddressses}; | ||||||||||||||||||||||||||
use katana_rpc::cors::Cors; | ||||||||||||||||||||||||||
use katana_rpc::dev::DevApi; | ||||||||||||||||||||||||||
use katana_rpc::metrics::RpcServerMetrics; | ||||||||||||||||||||||||||
use katana_rpc::saya::SayaApi; | ||||||||||||||||||||||||||
use katana_rpc::starknet::forking::ForkedClient; | ||||||||||||||||||||||||||
use katana_rpc::starknet::{StarknetApi, StarknetApiConfig}; | ||||||||||||||||||||||||||
use katana_rpc::torii::ToriiApi; | ||||||||||||||||||||||||||
use katana_rpc::{RpcServer, RpcServerHandle}; | ||||||||||||||||||||||||||
use katana_rpc_api::dev::DevApiServer; | ||||||||||||||||||||||||||
use katana_rpc_api::saya::SayaApiServer; | ||||||||||||||||||||||||||
use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer}; | ||||||||||||||||||||||||||
use katana_rpc_api::torii::ToriiApiServer; | ||||||||||||||||||||||||||
use katana_stage::Sequencing; | ||||||||||||||||||||||||||
use katana_tasks::TaskManager; | ||||||||||||||||||||||||||
use tower_http::cors::{AllowOrigin, CorsLayer}; | ||||||||||||||||||||||||||
use tracing::info; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
use crate::exit::NodeStoppedFuture; | ||||||||||||||||||||||||||
|
@@ -59,7 +55,7 @@ use crate::exit::NodeStoppedFuture; | |||||||||||||||||||||||||
pub struct LaunchedNode { | ||||||||||||||||||||||||||
pub node: Node, | ||||||||||||||||||||||||||
/// Handle to the rpc server. | ||||||||||||||||||||||||||
pub rpc: RpcServer, | ||||||||||||||||||||||||||
pub rpc: RpcServerHandle, | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
impl LaunchedNode { | ||||||||||||||||||||||||||
|
@@ -261,16 +257,21 @@ pub async fn build(mut config: Config) -> Result<Node> { | |||||||||||||||||||||||||
pub async fn spawn<EF: ExecutorFactory>( | ||||||||||||||||||||||||||
node_components: (TxPool, Arc<Backend<EF>>, BlockProducer<EF>, Option<ForkedClient>), | ||||||||||||||||||||||||||
config: RpcConfig, | ||||||||||||||||||||||||||
) -> Result<RpcServer> { | ||||||||||||||||||||||||||
) -> Result<RpcServerHandle> { | ||||||||||||||||||||||||||
let (pool, backend, block_producer, forked_client) = node_components; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let mut methods = RpcModule::new(()); | ||||||||||||||||||||||||||
methods.register_method("health", |_, _| Ok(serde_json::json!({ "health": true })))?; | ||||||||||||||||||||||||||
let mut modules = RpcModule::new(()); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let cors = Cors::new() | ||||||||||||||||||||||||||
.allow_origins(config.cors_origins.clone()) | ||||||||||||||||||||||||||
// Allow `POST` when accessing the resource | ||||||||||||||||||||||||||
.allow_methods([Method::POST, Method::GET]) | ||||||||||||||||||||||||||
.allow_headers([hyper::header::CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if config.apis.contains(&ApiKind::Starknet) { | ||||||||||||||||||||||||||
let cfg = StarknetApiConfig { max_event_page_size: config.max_event_page_size }; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let server = if let Some(client) = forked_client { | ||||||||||||||||||||||||||
let api = if let Some(client) = forked_client { | ||||||||||||||||||||||||||
StarknetApi::new_forked( | ||||||||||||||||||||||||||
backend.clone(), | ||||||||||||||||||||||||||
pool.clone(), | ||||||||||||||||||||||||||
|
@@ -282,68 +283,28 @@ pub async fn spawn<EF: ExecutorFactory>( | |||||||||||||||||||||||||
StarknetApi::new(backend.clone(), pool.clone(), Some(block_producer.clone()), cfg) | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
methods.merge(StarknetApiServer::into_rpc(server.clone()))?; | ||||||||||||||||||||||||||
methods.merge(StarknetWriteApiServer::into_rpc(server.clone()))?; | ||||||||||||||||||||||||||
methods.merge(StarknetTraceApiServer::into_rpc(server))?; | ||||||||||||||||||||||||||
modules.merge(StarknetApiServer::into_rpc(api.clone()))?; | ||||||||||||||||||||||||||
modules.merge(StarknetWriteApiServer::into_rpc(api.clone()))?; | ||||||||||||||||||||||||||
modules.merge(StarknetTraceApiServer::into_rpc(api))?; | ||||||||||||||||||||||||||
Comment on lines
+286
to
+288
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Ohayo sensei! Enhance error handling for module merging. The current implementation might not provide clear error context when module merging fails. Consider wrapping the merge operations with additional context: - modules.merge(StarknetApiServer::into_rpc(api.clone()))?;
- modules.merge(StarknetWriteApiServer::into_rpc(api.clone()))?;
- modules.merge(StarknetTraceApiServer::into_rpc(api))?;
+ modules
+ .merge(StarknetApiServer::into_rpc(api.clone()))
+ .map_err(|e| anyhow::anyhow!("Failed to merge Starknet API: {}", e))?;
+ modules
+ .merge(StarknetWriteApiServer::into_rpc(api.clone()))
+ .map_err(|e| anyhow::anyhow!("Failed to merge Starknet Write API: {}", e))?;
+ modules
+ .merge(StarknetTraceApiServer::into_rpc(api))
+ .map_err(|e| anyhow::anyhow!("Failed to merge Starknet Trace API: {}", e))?; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if config.apis.contains(&ApiKind::Dev) { | ||||||||||||||||||||||||||
methods.merge(DevApi::new(backend.clone(), block_producer.clone()).into_rpc())?; | ||||||||||||||||||||||||||
let api = DevApi::new(backend.clone(), block_producer.clone()); | ||||||||||||||||||||||||||
modules.merge(api.into_rpc())?; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if config.apis.contains(&ApiKind::Torii) { | ||||||||||||||||||||||||||
methods.merge( | ||||||||||||||||||||||||||
ToriiApi::new(backend.clone(), pool.clone(), block_producer.clone()).into_rpc(), | ||||||||||||||||||||||||||
)?; | ||||||||||||||||||||||||||
let api = ToriiApi::new(backend.clone(), pool.clone(), block_producer.clone()); | ||||||||||||||||||||||||||
modules.merge(api.into_rpc())?; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
if config.apis.contains(&ApiKind::Saya) { | ||||||||||||||||||||||||||
methods.merge(SayaApi::new(backend.clone(), block_producer.clone()).into_rpc())?; | ||||||||||||||||||||||||||
let api = SayaApi::new(backend.clone(), block_producer.clone()); | ||||||||||||||||||||||||||
modules.merge(api.into_rpc())?; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let cors = CorsLayer::new() | ||||||||||||||||||||||||||
// Allow `POST` when accessing the resource | ||||||||||||||||||||||||||
.allow_methods([Method::POST, Method::GET]) | ||||||||||||||||||||||||||
.allow_headers([hyper::header::CONTENT_TYPE, "argent-client".parse().unwrap(), "argent-version".parse().unwrap()]); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let cors = | ||||||||||||||||||||||||||
config.cors_origins.clone().map(|allowed_origins| match allowed_origins.as_slice() { | ||||||||||||||||||||||||||
[origin] if origin == "*" => cors.allow_origin(AllowOrigin::mirror_request()), | ||||||||||||||||||||||||||
origins => cors.allow_origin( | ||||||||||||||||||||||||||
origins | ||||||||||||||||||||||||||
.iter() | ||||||||||||||||||||||||||
.map(|o| { | ||||||||||||||||||||||||||
let _ = o.parse::<Uri>().expect("Invalid URI"); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
o.parse().expect("Invalid origin") | ||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||
.collect::<Vec<_>>(), | ||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let middleware = tower::ServiceBuilder::new() | ||||||||||||||||||||||||||
.option_layer(cors) | ||||||||||||||||||||||||||
.layer(ProxyGetRequestLayer::new("/", "health")?) | ||||||||||||||||||||||||||
.timeout(Duration::from_secs(20)); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let server = ServerBuilder::new() | ||||||||||||||||||||||||||
.set_logger(RpcServerMetrics::new(&methods)) | ||||||||||||||||||||||||||
.set_host_filtering(AllowHosts::Any) | ||||||||||||||||||||||||||
.set_middleware(middleware) | ||||||||||||||||||||||||||
.max_connections(config.max_connections) | ||||||||||||||||||||||||||
.build(config.socket_addr()) | ||||||||||||||||||||||||||
.await?; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
let addr = server.local_addr()?; | ||||||||||||||||||||||||||
let handle = server.start(methods)?; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
info!(target: "rpc", %addr, "RPC server started."); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Ok(RpcServer { handle, addr }) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
let server = RpcServer::new().metrics().health_check().cors(cors).module(modules); | ||||||||||||||||||||||||||
let handle = server.start(config.socket_addr()).await?; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
#[derive(Debug)] | ||||||||||||||||||||||||||
pub struct RpcServer { | ||||||||||||||||||||||||||
pub addr: SocketAddr, | ||||||||||||||||||||||||||
pub handle: ServerHandle, | ||||||||||||||||||||||||||
Ok(handle) | ||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohayo sensei! Avoid using
.unwrap()
when parsing header values.Using
.unwrap()
may cause the application to panic if the parsing fails. Consider handling the potential error or using.expect()
with a meaningful message.Apply this diff to handle the parsing result:
📝 Committable suggestion