moonbeam_service/
client.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16pub use moonbeam_core_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Header, Index};
17use sc_client_api::{Backend as BackendT, BlockchainEvents, KeysIter, MerkleValue, PairsIter};
18use sp_api::{CallApiAt, ProvideRuntimeApi};
19use sp_blockchain::HeaderBackend;
20use sp_consensus::BlockStatus;
21use sp_runtime::{
22	generic::SignedBlock,
23	traits::{BlakeTwo256, Block as BlockT, NumberFor},
24	Justifications,
25};
26use sp_storage::{ChildInfo, StorageData, StorageKey};
27use std::sync::Arc;
28
29/// A set of APIs that polkadot-like runtimes must implement.
30///
31/// This trait has no methods or associated type. It is a concise marker for all the trait bounds
32/// that it contains.
33pub trait RuntimeApiCollection:
34	sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
35	+ sp_api::ApiExt<Block>
36	+ sp_block_builder::BlockBuilder<Block>
37	+ substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
38	+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
39	+ sp_api::Metadata<Block>
40	+ sp_offchain::OffchainWorkerApi<Block>
41	+ sp_session::SessionKeys<Block>
42	+ fp_rpc::ConvertTransactionRuntimeApi<Block>
43	+ fp_rpc::EthereumRuntimeRPCApi<Block>
44	+ moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
45	+ moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>
46	+ nimbus_primitives::NimbusApi<Block>
47	+ cumulus_primitives_core::CollectCollationInfo<Block>
48	+ session_keys_primitives::VrfApi<Block>
49	+ async_backing_primitives::UnincludedSegmentApi<Block>
50	+ xcm_runtime_apis::fees::XcmPaymentApi<Block>
51{
52}
53
54impl<Api> RuntimeApiCollection for Api where
55	Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block>
56		+ sp_api::ApiExt<Block>
57		+ sp_block_builder::BlockBuilder<Block>
58		+ substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
59		+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
60		+ sp_api::Metadata<Block>
61		+ sp_offchain::OffchainWorkerApi<Block>
62		+ sp_session::SessionKeys<Block>
63		+ fp_rpc::ConvertTransactionRuntimeApi<Block>
64		+ fp_rpc::EthereumRuntimeRPCApi<Block>
65		+ moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
66		+ moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>
67		+ nimbus_primitives::NimbusApi<Block>
68		+ cumulus_primitives_core::CollectCollationInfo<Block>
69		+ session_keys_primitives::VrfApi<Block>
70		+ async_backing_primitives::UnincludedSegmentApi<Block>
71		+ xcm_runtime_apis::fees::XcmPaymentApi<Block>
72{
73}
74
75/// Config that abstracts over all available client implementations.
76///
77/// For a concrete type there exists [`Client`].
78pub trait AbstractClient<Block, Backend>:
79	BlockchainEvents<Block>
80	+ Sized
81	+ Send
82	+ Sync
83	+ ProvideRuntimeApi<Block>
84	+ HeaderBackend<Block>
85	+ CallApiAt<Block, StateBackend = Backend::State>
86where
87	Block: BlockT,
88	Backend: BackendT<Block>,
89	Backend::State: sc_client_api::backend::StateBackend<BlakeTwo256>,
90	Self::Api: RuntimeApiCollection,
91{
92}
93
94impl<Block, Backend, Client> AbstractClient<Block, Backend> for Client
95where
96	Block: BlockT,
97	Backend: BackendT<Block>,
98	Backend::State: sc_client_api::backend::StateBackend<BlakeTwo256>,
99	Client: BlockchainEvents<Block>
100		+ ProvideRuntimeApi<Block>
101		+ HeaderBackend<Block>
102		+ Sized
103		+ Send
104		+ Sync
105		+ CallApiAt<Block, StateBackend = Backend::State>,
106	Client::Api: RuntimeApiCollection,
107{
108}
109
110/// Execute something with the client instance.
111///
112/// As there exist multiple chains inside Moonbeam, like Moonbeam itself, Moonbase,
113/// Moonriver etc, there can exist different kinds of client types. As these
114/// client types differ in the generics that are being used, we can not easily
115/// return them from a function. For returning them from a function there exists
116/// [`Client`]. However, the problem on how to use this client instance still
117/// exists. This trait "solves" it in a dirty way. It requires a type to
118/// implement this trait and than the [`execute_with_client`](ExecuteWithClient:
119/// :execute_with_client) function can be called with any possible client
120/// instance.
121///
122/// In a perfect world, we could make a closure work in this way.
123pub trait ExecuteWithClient {
124	/// The return type when calling this instance.
125	type Output;
126
127	/// Execute whatever should be executed with the given client instance.
128	fn execute_with_client<Client, Api, Backend>(self, client: Arc<Client>) -> Self::Output
129	where
130		Backend: sc_client_api::Backend<Block>,
131		Backend::State: sc_client_api::backend::StateBackend<BlakeTwo256>,
132		Api: crate::RuntimeApiCollection,
133		Client: AbstractClient<Block, Backend, Api = Api> + 'static;
134}
135
136/// A handle to a Moonbeam client instance.
137///
138/// The Moonbeam service supports multiple different runtimes (Moonbase, Moonbeam
139/// itself, etc). As each runtime has a specialized client, we need to hide them
140/// behind a trait. This is this trait.
141///
142/// When wanting to work with the inner client, you need to use `execute_with`.
143pub trait ClientHandle {
144	/// Execute the given something with the client.
145	fn execute_with<T: ExecuteWithClient>(&self, t: T) -> T::Output;
146}
147
148/// A client instance of Moonbeam.
149#[derive(Clone)]
150pub enum Client {
151	#[cfg(feature = "moonbeam-native")]
152	Moonbeam(Arc<crate::FullClient<moonbeam_runtime::RuntimeApi>>),
153	#[cfg(feature = "moonriver-native")]
154	Moonriver(Arc<crate::FullClient<moonriver_runtime::RuntimeApi>>),
155	#[cfg(feature = "moonbase-native")]
156	Moonbase(Arc<crate::FullClient<moonbase_runtime::RuntimeApi>>),
157}
158
159#[cfg(feature = "moonbeam-native")]
160impl From<Arc<crate::FullClient<moonbeam_runtime::RuntimeApi>>> for Client {
161	fn from(client: Arc<crate::FullClient<moonbeam_runtime::RuntimeApi>>) -> Self {
162		Self::Moonbeam(client)
163	}
164}
165
166#[cfg(feature = "moonriver-native")]
167impl From<Arc<crate::FullClient<moonriver_runtime::RuntimeApi>>> for Client {
168	fn from(client: Arc<crate::FullClient<moonriver_runtime::RuntimeApi>>) -> Self {
169		Self::Moonriver(client)
170	}
171}
172
173#[cfg(feature = "moonbase-native")]
174impl From<Arc<crate::FullClient<moonbase_runtime::RuntimeApi>>> for Client {
175	fn from(client: Arc<crate::FullClient<moonbase_runtime::RuntimeApi>>) -> Self {
176		Self::Moonbase(client)
177	}
178}
179
180impl ClientHandle for Client {
181	fn execute_with<T: ExecuteWithClient>(&self, t: T) -> T::Output {
182		match self {
183			#[cfg(feature = "moonbeam-native")]
184			Self::Moonbeam(client) => T::execute_with_client::<_, _, crate::FullBackend>(t, client.clone()),
185			#[cfg(feature = "moonriver-native")]
186			Self::Moonriver(client) => T::execute_with_client::<_, _, crate::FullBackend>(t, client.clone()),
187			#[cfg(feature = "moonbase-native")]
188			Self::Moonbase(client) => T::execute_with_client::<_, _, crate::FullBackend>(t, client.clone()),
189		}
190	}
191}
192
193macro_rules! match_client {
194	($self:ident, $method:ident($($param:ident),*)) => {
195		match $self {
196			#[cfg(feature = "moonbeam-native")]
197			Self::Moonbeam(client) => client.$method($($param),*),
198			#[cfg(feature = "moonriver-native")]
199			Self::Moonriver(client) => client.$method($($param),*),
200			#[cfg(feature = "moonbase-native")]
201			Self::Moonbase(client) => client.$method($($param),*),
202		}
203	};
204}
205
206impl sc_client_api::UsageProvider<Block> for Client {
207	fn usage_info(&self) -> sc_client_api::ClientInfo<Block> {
208		match_client!(self, usage_info())
209	}
210}
211
212impl sc_client_api::BlockBackend<Block> for Client {
213	fn block_body(
214		&self,
215		hash: <Block as BlockT>::Hash,
216	) -> sp_blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
217		match_client!(self, block_body(hash))
218	}
219
220	fn block_indexed_body(
221		&self,
222		hash: <Block as BlockT>::Hash,
223	) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>> {
224		match_client!(self, block_indexed_body(hash))
225	}
226
227	fn block(
228		&self,
229		hash: <Block as BlockT>::Hash,
230	) -> sp_blockchain::Result<Option<SignedBlock<Block>>> {
231		match_client!(self, block(hash))
232	}
233
234	fn block_status(&self, hash: <Block as BlockT>::Hash) -> sp_blockchain::Result<BlockStatus> {
235		match_client!(self, block_status(hash))
236	}
237
238	fn justifications(
239		&self,
240		hash: <Block as BlockT>::Hash,
241	) -> sp_blockchain::Result<Option<Justifications>> {
242		match_client!(self, justifications(hash))
243	}
244
245	fn block_hash(
246		&self,
247		number: NumberFor<Block>,
248	) -> sp_blockchain::Result<Option<<Block as BlockT>::Hash>> {
249		match_client!(self, block_hash(number))
250	}
251
252	fn indexed_transaction(
253		&self,
254		hash: <Block as BlockT>::Hash,
255	) -> sp_blockchain::Result<Option<Vec<u8>>> {
256		match_client!(self, indexed_transaction(hash))
257	}
258
259	fn has_indexed_transaction(
260		&self,
261		hash: <Block as BlockT>::Hash,
262	) -> sp_blockchain::Result<bool> {
263		match_client!(self, has_indexed_transaction(hash))
264	}
265
266	fn requires_full_sync(&self) -> bool {
267		match_client!(self, requires_full_sync())
268	}
269}
270
271impl sc_client_api::StorageProvider<Block, crate::FullBackend> for Client {
272	fn storage(
273		&self,
274		hash: <Block as BlockT>::Hash,
275		key: &StorageKey,
276	) -> sp_blockchain::Result<Option<StorageData>> {
277		match_client!(self, storage(hash, key))
278	}
279
280	fn storage_hash(
281		&self,
282		hash: <Block as BlockT>::Hash,
283		key: &StorageKey,
284	) -> sp_blockchain::Result<Option<<Block as BlockT>::Hash>> {
285		match_client!(self, storage_hash(hash, key))
286	}
287
288	fn storage_keys(
289		&self,
290		hash: <Block as BlockT>::Hash,
291		prefix: Option<&StorageKey>,
292		start_key: Option<&StorageKey>,
293	) -> sp_blockchain::Result<
294		KeysIter<<crate::FullBackend as sc_client_api::Backend<Block>>::State, Block>,
295	> {
296		match_client!(self, storage_keys(hash, prefix, start_key))
297	}
298
299	fn storage_pairs(
300		&self,
301		hash: <Block as BlockT>::Hash,
302		key_prefix: Option<&StorageKey>,
303		start_key: Option<&StorageKey>,
304	) -> sp_blockchain::Result<
305		PairsIter<<crate::FullBackend as sc_client_api::Backend<Block>>::State, Block>,
306	> {
307		match_client!(self, storage_pairs(hash, key_prefix, start_key))
308	}
309
310	fn child_storage(
311		&self,
312		hash: <Block as BlockT>::Hash,
313		child_info: &ChildInfo,
314		key: &StorageKey,
315	) -> sp_blockchain::Result<Option<StorageData>> {
316		match_client!(self, child_storage(hash, child_info, key))
317	}
318
319	fn child_storage_keys(
320		&self,
321		hash: <Block as BlockT>::Hash,
322		child_info: ChildInfo,
323		prefix: Option<&StorageKey>,
324		start_key: Option<&StorageKey>,
325	) -> sp_blockchain::Result<
326		KeysIter<<crate::FullBackend as sc_client_api::Backend<Block>>::State, Block>,
327	> {
328		match_client!(
329			self,
330			child_storage_keys(hash, child_info, prefix, start_key)
331		)
332	}
333
334	fn child_storage_hash(
335		&self,
336		hash: <Block as BlockT>::Hash,
337		child_info: &ChildInfo,
338		key: &StorageKey,
339	) -> sp_blockchain::Result<Option<<Block as BlockT>::Hash>> {
340		match_client!(self, child_storage_hash(hash, child_info, key))
341	}
342
343	fn closest_merkle_value(
344		&self,
345		hash: <Block as BlockT>::Hash,
346		key: &StorageKey,
347	) -> sp_blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
348		match_client!(self, closest_merkle_value(hash, key))
349	}
350
351	fn child_closest_merkle_value(
352		&self,
353		hash: <Block as BlockT>::Hash,
354		child_info: &ChildInfo,
355		key: &StorageKey,
356	) -> sp_blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
357		match_client!(self, child_closest_merkle_value(hash, child_info, key))
358	}
359}
360
361impl sp_blockchain::HeaderBackend<Block> for Client {
362	fn header(&self, hash: Hash) -> sp_blockchain::Result<Option<Header>> {
363		match_client!(self, header(hash))
364	}
365
366	fn info(&self) -> sp_blockchain::Info<Block> {
367		match_client!(self, info())
368	}
369
370	fn status(&self, hash: Hash) -> sp_blockchain::Result<sp_blockchain::BlockStatus> {
371		match_client!(self, status(hash))
372	}
373
374	fn number(&self, hash: Hash) -> sp_blockchain::Result<Option<BlockNumber>> {
375		match_client!(self, number(hash))
376	}
377
378	fn hash(&self, number: NumberFor<Block>) -> sp_blockchain::Result<Option<Hash>> {
379		match_client!(self, hash(number))
380	}
381}