arb_rpc/
arb_api.rs

1//! Arbitrum `arb_` RPC namespace.
2//!
3//! Implements the `arb_` JSON-RPC methods: maintenance status,
4//! health checks, and block metadata queries.
5
6use std::sync::Arc;
7
8use alloy_consensus::BlockHeader;
9use alloy_primitives::B256;
10use alloy_rpc_types_eth::BlockNumberOrTag;
11use jsonrpsee::{core::RpcResult, proc_macros::rpc};
12use reth_provider::{BlockNumReader, BlockReaderIdExt, HeaderProvider};
13
14use crate::{ArbBlockInfo, ArbMaintenanceStatus};
15
16/// Arbitrum `arb_` RPC namespace.
17#[rpc(server, namespace = "arb")]
18pub trait ArbApi {
19    /// Returns the maintenance status of the node.
20    #[method(name = "maintenanceStatus")]
21    fn maintenance_status(&self) -> RpcResult<ArbMaintenanceStatus>;
22
23    /// Checks publisher health. Returns an error if unhealthy.
24    #[method(name = "checkPublisherHealth")]
25    fn check_publisher_health(&self) -> RpcResult<()>;
26
27    /// Returns block info for the given block number.
28    #[method(name = "getBlockInfo")]
29    async fn get_block_info(&self, block_num: u64) -> RpcResult<ArbBlockInfo>;
30}
31
32/// Implementation of the `arb_` RPC namespace.
33pub struct ArbApiHandler<Provider> {
34    provider: Provider,
35    /// Current maintenance mode status.
36    maintenance_status: Arc<parking_lot::RwLock<ArbMaintenanceStatus>>,
37}
38
39impl<Provider> ArbApiHandler<Provider> {
40    /// Create a new `ArbApiHandler`.
41    pub fn new(provider: Provider) -> Self {
42        Self {
43            provider,
44            maintenance_status: Arc::new(parking_lot::RwLock::new(ArbMaintenanceStatus::default())),
45        }
46    }
47
48    /// Returns a reference to the maintenance status lock for external updates.
49    pub fn maintenance_status_handle(&self) -> Arc<parking_lot::RwLock<ArbMaintenanceStatus>> {
50        self.maintenance_status.clone()
51    }
52}
53
54#[async_trait::async_trait]
55impl<Provider> ArbApiServer for ArbApiHandler<Provider>
56where
57    Provider: BlockNumReader + BlockReaderIdExt + HeaderProvider + 'static,
58{
59    fn maintenance_status(&self) -> RpcResult<ArbMaintenanceStatus> {
60        Ok(self.maintenance_status.read().clone())
61    }
62
63    fn check_publisher_health(&self) -> RpcResult<()> {
64        // Sequencer health is always OK for a full node.
65        Ok(())
66    }
67
68    async fn get_block_info(&self, block_num: u64) -> RpcResult<ArbBlockInfo> {
69        let header = self
70            .provider
71            .sealed_header_by_number_or_tag(BlockNumberOrTag::Number(block_num))
72            .map_err(|e| {
73                jsonrpsee::types::ErrorObject::owned(
74                    jsonrpsee::types::error::INTERNAL_ERROR_CODE,
75                    e.to_string(),
76                    None::<()>,
77                )
78            })?
79            .ok_or_else(|| {
80                jsonrpsee::types::ErrorObject::owned(
81                    jsonrpsee::types::error::INVALID_PARAMS_CODE,
82                    format!("block {block_num} not found"),
83                    None::<()>,
84                )
85            })?;
86
87        let mix = header.mix_hash().unwrap_or_default();
88        let extra = header.extra_data();
89
90        let send_count = u64::from_be_bytes(mix.0[0..8].try_into().unwrap_or_default());
91        let l1_block_number = u64::from_be_bytes(mix.0[8..16].try_into().unwrap_or_default());
92        let arbos_format_version = u64::from_be_bytes(mix.0[16..24].try_into().unwrap_or_default());
93
94        let send_root = if extra.len() >= 32 {
95            B256::from_slice(&extra[..32])
96        } else {
97            B256::ZERO
98        };
99
100        Ok(ArbBlockInfo {
101            l1_block_number,
102            arbos_format_version,
103            send_count,
104            send_root,
105        })
106    }
107}