tempo_node/rpc/
fork_schedule.rs1use jsonrpsee::{core::RpcResult, proc_macros::rpc};
2use reth_chainspec::{EthereumHardfork, ForkCondition, Hardforks, Head};
3use reth_primitives_traits::AlloyBlockHeader as _;
4use reth_provider::{BlockNumReader, ChainSpecProvider, HeaderProvider};
5use serde::{Deserialize, Serialize};
6use tempo_chainspec::{TempoChainSpec, hardfork::TempoHardforks};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct ForkSchedule {
12 pub schedule: Vec<ForkInfo>,
14 pub active: String,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21pub struct ForkInfo {
22 pub name: String,
24 pub activation_time: u64,
26 pub active: bool,
28 #[serde(skip_serializing_if = "Option::is_none")]
31 pub fork_id: Option<String>,
32}
33
34#[rpc(server, namespace = "tempo")]
35pub trait TempoForkScheduleApi {
36 #[method(name = "forkSchedule")]
38 async fn fork_schedule(&self) -> RpcResult<ForkSchedule>;
39}
40
41#[derive(Debug, Clone)]
43pub struct TempoForkScheduleRpc<P> {
44 provider: P,
45}
46
47impl<P> TempoForkScheduleRpc<P> {
48 pub fn new(provider: P) -> Self {
50 Self { provider }
51 }
52}
53
54fn internal_err(msg: impl ToString) -> jsonrpsee::types::ErrorObject<'static> {
55 jsonrpsee::types::ErrorObject::owned(-32000, msg.to_string(), None::<()>)
56}
57
58#[async_trait::async_trait]
59impl<P> TempoForkScheduleApiServer for TempoForkScheduleRpc<P>
60where
61 P: ChainSpecProvider<ChainSpec = TempoChainSpec>
62 + BlockNumReader
63 + HeaderProvider
64 + Send
65 + Sync
66 + 'static,
67{
68 async fn fork_schedule(&self) -> RpcResult<ForkSchedule> {
69 let chain_spec = self.provider.chain_spec();
70
71 let best_number = self.provider.best_block_number().map_err(internal_err)?;
72 let header = self
73 .provider
74 .header_by_number(best_number)
75 .map_err(internal_err)?
76 .ok_or_else(|| internal_err("head header not found"))?;
77 let head_timestamp = header.timestamp();
78
79 let schedule = chain_spec
81 .forks_iter()
82 .filter(|(fork, _)| {
83 let name = fork.name();
84 name != "Genesis" && !EthereumHardfork::VARIANTS.iter().any(|h| h.name() == name)
85 })
86 .filter_map(|(fork, condition)| {
87 let ForkCondition::Timestamp(ts) = condition else {
88 return None;
89 };
90 let active = ts <= head_timestamp;
91 let fork_id = active.then(|| {
92 let id = chain_spec.fork_id(&Head {
93 number: best_number,
94 timestamp: ts,
95 ..Default::default()
96 });
97 format!(
98 "0x{}",
99 id.hash
100 .0
101 .iter()
102 .map(|b| format!("{b:02x}"))
103 .collect::<String>()
104 )
105 });
106 Some(ForkInfo {
107 name: fork.name().to_string(),
108 activation_time: ts,
109 active,
110 fork_id,
111 })
112 })
113 .collect();
114
115 let active = chain_spec
116 .tempo_hardfork_at(head_timestamp)
117 .name()
118 .to_string();
119
120 Ok(ForkSchedule { schedule, active })
121 }
122}