Skip to main content

anvil_polkadot/substrate_node/
rpc.rs

1use crate::substrate_node::service::{Backend, Client, TransactionPoolHandle};
2use jsonrpsee::RpcModule;
3use polkadot_sdk::{
4    parachains_common::opaque::Block,
5    sc_chain_spec::ChainSpec,
6    sc_client_api::{Backend as ClientBackend, HeaderBackend},
7    sc_client_db::{BlocksPruning, PruningMode},
8    sc_network_types::{self, multiaddr::Multiaddr},
9    sc_rpc::{
10        author::AuthorApiServer,
11        chain::ChainApiServer,
12        offchain::OffchainApiServer,
13        state::{ChildStateApiServer, StateApiServer},
14        system::{Request, SystemApiServer, SystemInfo},
15    },
16    sc_rpc_api::DenyUnsafe,
17    sc_rpc_spec_v2::{
18        archive::ArchiveApiServer,
19        chain_head::ChainHeadApiServer,
20        chain_spec::ChainSpecApiServer,
21        transaction::{TransactionApiServer, TransactionBroadcastApiServer},
22    },
23    sc_service::{
24        self, Configuration, RpcHandlers, SpawnTaskHandle, TaskManager,
25        error::Error as ServiceError,
26    },
27    sc_utils::mpsc::{TracingUnboundedSender, tracing_unbounded},
28    sp_keystore::KeystorePtr,
29    substrate_frame_rpc_system::SystemApiServer as _,
30};
31use std::sync::Arc;
32
33pub fn spawn_rpc_server(
34    genesis_number: u64,
35    task_manager: &mut TaskManager,
36    client: Arc<Client>,
37    mut config: Configuration,
38    transaction_pool: Arc<TransactionPoolHandle>,
39    keystore: KeystorePtr,
40    backend: Arc<Backend>,
41) -> Result<RpcHandlers, ServiceError> {
42    let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc", 10_000);
43
44    let rpc_id_provider = config.rpc.id_provider.take();
45
46    let gen_rpc_module = || {
47        gen_rpc_module(
48            genesis_number,
49            task_manager.spawn_handle(),
50            client.clone(),
51            transaction_pool.clone(),
52            keystore.clone(),
53            system_rpc_tx.clone(),
54            config.impl_name.clone(),
55            config.impl_version.clone(),
56            config.chain_spec.as_ref(),
57            &config.state_pruning,
58            config.blocks_pruning,
59            backend.clone(),
60        )
61    };
62
63    let rpc_server_handle = sc_service::start_rpc_servers(
64        &config.rpc,
65        config.prometheus_registry(),
66        &config.tokio_handle,
67        gen_rpc_module,
68        rpc_id_provider,
69    )?;
70
71    let listen_addrs = rpc_server_handle
72        .listen_addrs()
73        .iter()
74        .map(|socket_addr| {
75            let mut multiaddr: Multiaddr = socket_addr.ip().into();
76            multiaddr.push(sc_network_types::multiaddr::Protocol::Tcp(socket_addr.port()));
77            multiaddr
78        })
79        .collect();
80
81    let in_memory_rpc = {
82        let mut module = gen_rpc_module()?;
83        module.extensions_mut().insert(DenyUnsafe::No);
84        module
85    };
86
87    let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc), listen_addrs);
88
89    task_manager.keep_alive((config.base_path, rpc_server_handle, system_rpc_rx));
90
91    Ok(in_memory_rpc_handle)
92}
93
94// Re-implement RPC module generation without the check on the genesis block number.
95// The code is identical to the one in
96// https://github.com/paritytech/polkadot-sdk/blob/9e0636567bebf312b065ca3acb285a8b32499df7/substrate/client/service/src/builder.rs#L754
97// apart from the creation of the RPC builder inside the function and the genesis number check.
98#[allow(clippy::too_many_arguments)]
99fn gen_rpc_module(
100    genesis_number: u64,
101    spawn_handle: SpawnTaskHandle,
102    client: Arc<Client>,
103    transaction_pool: Arc<TransactionPoolHandle>,
104    keystore: KeystorePtr,
105    system_rpc_tx: TracingUnboundedSender<Request<Block>>,
106    impl_name: String,
107    impl_version: String,
108    chain_spec: &dyn ChainSpec,
109    state_pruning: &Option<PruningMode>,
110    blocks_pruning: BlocksPruning,
111    backend: Arc<Backend>,
112) -> Result<RpcModule<()>, ServiceError> {
113    // Different from the original code, we create the RPC builder inside the function.
114    let rpc_builder = {
115        let client = client.clone();
116        let pool = transaction_pool.clone();
117
118        Box::new(move |_| {
119            let rpc_builder_ext: Result<_, ServiceError> = Ok(
120                polkadot_sdk::substrate_frame_rpc_system::System::new(client.clone(), pool.clone())
121                    .into_rpc(),
122            );
123            rpc_builder_ext
124        })
125    };
126
127    let system_info = SystemInfo {
128        chain_name: chain_spec.name().into(),
129        impl_name,
130        impl_version,
131        properties: chain_spec.properties(),
132        chain_type: chain_spec.chain_type(),
133    };
134
135    let mut rpc_api = RpcModule::new(());
136    let task_executor = Arc::new(spawn_handle);
137
138    let (chain, state, child_state) = {
139        let chain =
140            polkadot_sdk::sc_rpc::chain::new_full(client.clone(), task_executor.clone()).into_rpc();
141        let (state, child_state) =
142            polkadot_sdk::sc_rpc::state::new_full(client.clone(), task_executor.clone(), None);
143        let state = state.into_rpc();
144        let child_state = child_state.into_rpc();
145
146        (chain, state, child_state)
147    };
148
149    const MAX_TRANSACTION_PER_CONNECTION: usize = 16;
150
151    let transaction_broadcast_rpc_v2 =
152        polkadot_sdk::sc_rpc_spec_v2::transaction::TransactionBroadcast::new(
153            client.clone(),
154            transaction_pool.clone(),
155            task_executor.clone(),
156            MAX_TRANSACTION_PER_CONNECTION,
157        )
158        .into_rpc();
159
160    let transaction_v2 = polkadot_sdk::sc_rpc_spec_v2::transaction::Transaction::new(
161        client.clone(),
162        transaction_pool.clone(),
163        task_executor.clone(),
164        None,
165    )
166    .into_rpc();
167
168    let chain_head_v2 = polkadot_sdk::sc_rpc_spec_v2::chain_head::ChainHead::new(
169        client.clone(),
170        backend.clone(),
171        task_executor.clone(),
172        // Defaults to sensible limits for the `ChainHead`.
173        polkadot_sdk::sc_rpc_spec_v2::chain_head::ChainHeadConfig::default(),
174    )
175    .into_rpc();
176
177    let is_archive_node = state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false)
178        && blocks_pruning.is_archive();
179    // Different from the original code, we use the genesis number to get the genesis hash.
180    let Some(genesis_hash) = client.hash(genesis_number as u32).ok().flatten() else {
181        return Err(ServiceError::Application(
182            format!("Genesis hash not found for genesis block number {genesis_number}").into(),
183        ));
184    };
185    if is_archive_node {
186        let archive_v2 = polkadot_sdk::sc_rpc_spec_v2::archive::Archive::new(
187            client.clone(),
188            backend.clone(),
189            genesis_hash,
190            task_executor.clone(),
191        )
192        .into_rpc();
193        rpc_api.merge(archive_v2).map_err(|e| ServiceError::Application(e.into()))?;
194    }
195
196    let chain_spec_v2 = polkadot_sdk::sc_rpc_spec_v2::chain_spec::ChainSpec::new(
197        chain_spec.name().into(),
198        genesis_hash,
199        chain_spec.properties(),
200    )
201    .into_rpc();
202
203    let author = polkadot_sdk::sc_rpc::author::Author::new(
204        client,
205        transaction_pool,
206        keystore,
207        task_executor.clone(),
208    )
209    .into_rpc();
210
211    let system = polkadot_sdk::sc_rpc::system::System::new(system_info, system_rpc_tx).into_rpc();
212
213    if let Some(storage) = backend.offchain_storage() {
214        let offchain = polkadot_sdk::sc_rpc::offchain::Offchain::new(storage).into_rpc();
215
216        rpc_api.merge(offchain).map_err(|e| ServiceError::Application(e.into()))?;
217    }
218
219    // Part of the RPC v2 spec.
220    rpc_api.merge(transaction_v2).map_err(|e| ServiceError::Application(e.into()))?;
221    rpc_api.merge(transaction_broadcast_rpc_v2).map_err(|e| ServiceError::Application(e.into()))?;
222    rpc_api.merge(chain_head_v2).map_err(|e| ServiceError::Application(e.into()))?;
223    rpc_api.merge(chain_spec_v2).map_err(|e| ServiceError::Application(e.into()))?;
224
225    // Part of the old RPC spec.
226    rpc_api.merge(chain).map_err(|e| ServiceError::Application(e.into()))?;
227    rpc_api.merge(author).map_err(|e| ServiceError::Application(e.into()))?;
228    rpc_api.merge(system).map_err(|e| ServiceError::Application(e.into()))?;
229    rpc_api.merge(state).map_err(|e| ServiceError::Application(e.into()))?;
230    rpc_api.merge(child_state).map_err(|e| ServiceError::Application(e.into()))?;
231    // Additional [`RpcModule`]s defined in the node to fit the specific blockchain
232    let extra_rpcs = rpc_builder(task_executor)?;
233    rpc_api.merge(extra_rpcs).map_err(|e| ServiceError::Application(e.into()))?;
234
235    Ok(rpc_api)
236}