referrerpolicy=no-referrer-when-downgrade

sc_service/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Substrate service. Starts a thread that spins up the network, client, and extrinsic pool.
20//! Manages communication between them.
21
22#![warn(missing_docs)]
23#![recursion_limit = "1024"]
24
25pub mod chain_ops;
26pub mod client;
27pub mod config;
28pub mod error;
29
30mod builder;
31mod metrics;
32mod task_manager;
33
34use crate::config::Multiaddr;
35use std::{
36	collections::HashMap,
37	net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
38};
39
40use codec::{Decode, Encode};
41use futures::{pin_mut, FutureExt, StreamExt};
42use jsonrpsee::RpcModule;
43use log::{debug, error, trace, warn};
44use sc_client_api::{blockchain::HeaderBackend, BlockBackend, BlockchainEvents, ProofProvider};
45use sc_network::{
46	config::MultiaddrWithPeerId, multiaddr::Protocol, service::traits::NetworkService,
47	NetworkBackend, NetworkBlock, NetworkPeers, NetworkStateInfo,
48};
49use sc_network_sync::SyncingService;
50use sc_network_types::PeerId;
51pub use sc_rpc_server::create_rpc_runtime;
52use sc_rpc_server::Server;
53use sc_utils::mpsc::TracingUnboundedReceiver;
54use sp_blockchain::HeaderMetadata;
55use sp_consensus::SyncOracle;
56use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
57
58pub use self::{
59	builder::{
60		build_default_block_downloader, build_default_syncing_engine, build_network,
61		build_network_advanced, build_polkadot_syncing_strategy, default_gap_sync_body_policy,
62		gen_rpc_module, init_telemetry, new_client, new_db_backend, new_full_client,
63		new_full_parts, new_full_parts_record_import, new_full_parts_with_genesis_builder,
64		new_wasm_executor, propagate_transaction_notifications, spawn_tasks,
65		BuildNetworkAdvancedParams, BuildNetworkParams, ClientForService,
66		DefaultSyncingEngineConfig, KeystoreContainer, SpawnTasksParams, TFullBackend,
67		TFullCallExecutor, TFullClient,
68	},
69	client::{ClientConfig, LocalCallExecutor},
70	error::Error,
71	metrics::MetricsService,
72};
73pub use sc_chain_spec::{
74	construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock,
75	GenesisBlockBuilder,
76};
77
78pub use config::{
79	BasePath, BlocksPruning, Configuration, DatabaseSource, PruningMode, Role, RpcMethods, TaskType,
80};
81pub use sc_chain_spec::{
82	ChainSpec, ChainType, Extension as ChainSpecExtension, GenericChainSpec, NoExtension,
83	Properties,
84};
85pub use sc_client_db::PruningFilter;
86
87use crate::config::RpcConfiguration;
88use prometheus_endpoint::Registry;
89pub use sc_consensus::ImportQueue;
90pub use sc_network_sync::WarpSyncConfig;
91#[doc(hidden)]
92pub use sc_network_transactions::config::{TransactionImport, TransactionImportFuture};
93pub use sc_rpc::{RandomIntegerSubscriptionId, RandomStringSubscriptionId};
94pub use sc_tracing::TracingReceiver;
95pub use sc_transaction_pool::TransactionPoolOptions;
96pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool};
97#[doc(hidden)]
98pub use std::{ops::Deref, result::Result, sync::Arc};
99pub use task_manager::{
100	SpawnEssentialTaskHandle, SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME,
101};
102use tokio::runtime::Handle;
103
104const DEFAULT_PROTOCOL_ID: &str = "sup";
105
106/// A running RPC service that can perform in-memory RPC queries.
107#[derive(Clone)]
108pub struct RpcHandlers {
109	// This is legacy and may be removed at some point, it was for WASM stuff before smoldot was a
110	// thing. https://github.com/paritytech/polkadot-sdk/pull/5038#discussion_r1694971805
111	rpc_module: Arc<RpcModule<()>>,
112
113	// This can be used to introspect the port the RPC server is listening on. SDK consumers are
114	// depending on this and it should be supported even if in-memory query support is removed.
115	listen_addresses: Vec<Multiaddr>,
116}
117
118impl RpcHandlers {
119	/// Create PRC handlers instance.
120	pub fn new(rpc_module: Arc<RpcModule<()>>, listen_addresses: Vec<Multiaddr>) -> Self {
121		Self { rpc_module, listen_addresses }
122	}
123
124	/// Starts an RPC query.
125	///
126	/// The query is passed as a string and must be valid JSON-RPC request object.
127	///
128	/// Returns a response and a stream if the call successful, fails if the
129	/// query could not be decoded as a JSON-RPC request object.
130	///
131	/// If the request subscribes you to events, the `stream` can be used to
132	/// retrieve the events.
133	pub async fn rpc_query(
134		&self,
135		json_query: &str,
136	) -> Result<(String, tokio::sync::mpsc::Receiver<String>), serde_json::Error> {
137		// Because `tokio::sync::mpsc::channel` is used under the hood
138		// it will panic if it's set to usize::MAX.
139		//
140		// This limit is used to prevent panics and is large enough.
141		const TOKIO_MPSC_MAX_SIZE: usize = tokio::sync::Semaphore::MAX_PERMITS;
142
143		self.rpc_module.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await
144	}
145
146	/// Provides access to the underlying `RpcModule`
147	pub fn handle(&self) -> Arc<RpcModule<()>> {
148		self.rpc_module.clone()
149	}
150
151	/// Provides access to listen addresses
152	pub fn listen_addresses(&self) -> &[Multiaddr] {
153		&self.listen_addresses[..]
154	}
155}
156
157/// An incomplete set of chain components, but enough to run the chain ops subcommands.
158pub struct PartialComponents<
159	Client,
160	Backend,
161	SelectChain,
162	ImportQueue,
163	TransactionPool: ?Sized,
164	Other,
165> {
166	/// A shared client instance.
167	pub client: Arc<Client>,
168	/// A shared backend instance.
169	pub backend: Arc<Backend>,
170	/// The chain task manager.
171	pub task_manager: TaskManager,
172	/// A keystore container instance.
173	pub keystore_container: KeystoreContainer,
174	/// A chain selection algorithm instance.
175	pub select_chain: SelectChain,
176	/// An import queue.
177	pub import_queue: ImportQueue,
178	/// A shared transaction pool.
179	pub transaction_pool: Arc<TransactionPool>,
180	/// Everything else that needs to be passed into the main build function.
181	pub other: Other,
182}
183
184/// Builds a future that continuously polls the network.
185async fn build_network_future<
186	B: BlockT,
187	C: BlockchainEvents<B>
188		+ HeaderBackend<B>
189		+ BlockBackend<B>
190		+ HeaderMetadata<B, Error = sp_blockchain::Error>
191		+ ProofProvider<B>
192		+ Send
193		+ Sync
194		+ 'static,
195	H: sc_network_common::ExHashT,
196	N: NetworkBackend<B, <B as BlockT>::Hash>,
197>(
198	network: N,
199	client: Arc<C>,
200	sync_service: Arc<SyncingService<B>>,
201	announce_imported_blocks: bool,
202) {
203	let mut imported_blocks_stream = client.import_notification_stream().fuse();
204
205	// Stream of finalized blocks reported by the client.
206	let mut finality_notification_stream = client.finality_notification_stream().fuse();
207
208	let network_run = network.run().fuse();
209	pin_mut!(network_run);
210
211	loop {
212		futures::select! {
213			// List of blocks that the client has imported.
214			notification = imported_blocks_stream.next() => {
215				let notification = match notification {
216					Some(n) => n,
217					// If this stream is shut down, that means the client has shut down, and the
218					// most appropriate thing to do for the network future is to shut down too.
219					None => {
220						warn!("Block import stream has terminated, shutting down the network future. Ignore if the node is stopping.");
221						return
222					},
223				};
224
225				if announce_imported_blocks {
226					sync_service.announce_block(notification.hash, None);
227				}
228
229				if notification.is_new_best {
230					sync_service.new_best_block_imported(
231						notification.hash,
232						*notification.header.number(),
233					);
234				}
235			}
236
237			// List of blocks that the client has finalized.
238			notification = finality_notification_stream.select_next_some() => {
239				sync_service.on_block_finalized(notification.hash, notification.header);
240			}
241
242			// Drive the network. Shut down the network future if `NetworkWorker` has terminated.
243			_ = network_run => {
244				warn!("`NetworkWorker` has terminated, shutting down the network future. Ignore if the node is stopping.");
245				return
246			}
247		}
248	}
249}
250
251/// Builds a future that processes system RPC requests.
252pub async fn build_system_rpc_future<
253	B: BlockT,
254	C: BlockchainEvents<B>
255		+ HeaderBackend<B>
256		+ BlockBackend<B>
257		+ HeaderMetadata<B, Error = sp_blockchain::Error>
258		+ ProofProvider<B>
259		+ Send
260		+ Sync
261		+ 'static,
262	H: sc_network_common::ExHashT,
263>(
264	role: Role,
265	network_service: Arc<dyn NetworkService>,
266	sync_service: Arc<SyncingService<B>>,
267	client: Arc<C>,
268	mut rpc_rx: TracingUnboundedReceiver<sc_rpc::system::Request<B>>,
269	should_have_peers: bool,
270) {
271	// Current best block at initialization, to report to the RPC layer.
272	let starting_block = client.info().best_number;
273
274	loop {
275		// Answer incoming RPC requests.
276		let Some(req) = rpc_rx.next().await else {
277			debug!("RPC requests stream has terminated, shutting down the system RPC future.");
278			return;
279		};
280
281		match req {
282			sc_rpc::system::Request::Health(sender) => match sync_service.peers_info().await {
283				Ok(info) => {
284					let _ = sender.send(sc_rpc::system::Health {
285						peers: info.len(),
286						is_syncing: sync_service.is_major_syncing(),
287						should_have_peers,
288					});
289				},
290				Err(_) => log::error!("`SyncingEngine` shut down"),
291			},
292			sc_rpc::system::Request::LocalPeerId(sender) => {
293				let _ = sender.send(network_service.local_peer_id().to_base58());
294			},
295			sc_rpc::system::Request::LocalListenAddresses(sender) => {
296				let local_peer_id = network_service.local_peer_id();
297				let addresses = network_service
298					.listen_addresses()
299					.iter()
300					.map(|address| with_local_peer_id(address.clone(), local_peer_id).to_string())
301					.collect();
302				let _ = sender.send(addresses);
303			},
304			sc_rpc::system::Request::Peers(sender) => match sync_service.peers_info().await {
305				Ok(info) => {
306					let _ = sender.send(
307						info.into_iter()
308							.map(|(peer_id, p)| sc_rpc::system::PeerInfo {
309								peer_id: peer_id.to_base58(),
310								roles: format!("{:?}", p.roles),
311								best_hash: p.best_hash,
312								best_number: p.best_number,
313							})
314							.collect(),
315					);
316				},
317				Err(_) => log::error!("`SyncingEngine` shut down"),
318			},
319			sc_rpc::system::Request::NetworkState(sender) => {
320				let network_state = network_service.network_state().await;
321				if let Ok(network_state) = network_state {
322					if let Ok(network_state) = serde_json::to_value(network_state) {
323						let _ = sender.send(network_state);
324					}
325				} else {
326					break;
327				}
328			},
329			sc_rpc::system::Request::NetworkAddReservedPeer(peer_addr, sender) => {
330				let result = match MultiaddrWithPeerId::try_from(peer_addr) {
331					Ok(peer) => network_service.add_reserved_peer(peer),
332					Err(err) => Err(err.to_string()),
333				};
334				let x = result.map_err(sc_rpc::system::error::Error::MalformattedPeerArg);
335				let _ = sender.send(x);
336			},
337			sc_rpc::system::Request::NetworkRemoveReservedPeer(peer_id, sender) => {
338				let _ = match peer_id.parse::<PeerId>() {
339					Ok(peer_id) => {
340						network_service.remove_reserved_peer(peer_id);
341						sender.send(Ok(()))
342					},
343					Err(e) => sender.send(Err(sc_rpc::system::error::Error::MalformattedPeerArg(
344						e.to_string(),
345					))),
346				};
347			},
348			sc_rpc::system::Request::NetworkReservedPeers(sender) => {
349				let Ok(reserved_peers) = network_service.reserved_peers().await else {
350					break;
351				};
352
353				let _ =
354					sender.send(reserved_peers.iter().map(|peer_id| peer_id.to_base58()).collect());
355			},
356			sc_rpc::system::Request::NodeRoles(sender) => {
357				use sc_rpc::system::NodeRole;
358
359				let node_role = match role {
360					Role::Authority { .. } => NodeRole::Authority,
361					Role::Full => NodeRole::Full,
362				};
363
364				let _ = sender.send(vec![node_role]);
365			},
366			sc_rpc::system::Request::SyncState(sender) => {
367				use sc_rpc::system::SyncState;
368
369				match sync_service.status().await.map(|status| status.best_seen_block) {
370					Ok(best_seen_block) => {
371						let best_number = client.info().best_number;
372						let _ = sender.send(SyncState {
373							starting_block,
374							current_block: best_number,
375							highest_block: best_seen_block.unwrap_or(best_number),
376						});
377					},
378					Err(_) => log::error!("`SyncingEngine` shut down"),
379				}
380			},
381		}
382	}
383
384	debug!("`NetworkWorker` has terminated, shutting down the system RPC future.");
385}
386
387/// Appends `/p2p/<local peer id>` unless the address already carries one.
388fn with_local_peer_id(address: Multiaddr, local_peer_id: PeerId) -> Multiaddr {
389	// litep2p backend appends local peer id to every listen address it reports,
390	// the libp2p backend does not.
391	match address.iter().last() {
392		Some(Protocol::P2p(_)) => address,
393		_ => address.with(Protocol::P2p(local_peer_id.into())),
394	}
395}
396
397/// Starts RPC servers.
398pub fn start_rpc_servers(
399	rpc_configuration: &RpcConfiguration,
400	registry: Option<&Registry>,
401	tokio_handle: &Handle,
402	rpc_api: RpcModule<()>,
403	rpc_runtime: tokio::runtime::Runtime,
404	rpc_id_provider: Option<Box<dyn sc_rpc_server::SubscriptionIdProvider>>,
405) -> Result<Server, error::Error> {
406	let endpoints: Vec<sc_rpc_server::RpcEndpoint> = if let Some(endpoints) =
407		rpc_configuration.addr.as_ref()
408	{
409		endpoints.clone()
410	} else {
411		let ipv6 =
412			SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, rpc_configuration.port, 0, 0));
413		let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, rpc_configuration.port));
414
415		vec![
416			sc_rpc_server::RpcEndpoint {
417				batch_config: rpc_configuration.batch_config,
418				cors: rpc_configuration.cors.clone(),
419				listen_addr: ipv4,
420				max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity,
421				max_connections: rpc_configuration.max_connections,
422				max_payload_in_mb: rpc_configuration.max_request_size,
423				max_payload_out_mb: rpc_configuration.max_response_size,
424				max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn,
425				rpc_methods: rpc_configuration.methods.into(),
426				rate_limit: rpc_configuration.rate_limit,
427				rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers,
428				rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(),
429				retry_random_port: true,
430				is_optional: false,
431			},
432			sc_rpc_server::RpcEndpoint {
433				batch_config: rpc_configuration.batch_config,
434				cors: rpc_configuration.cors.clone(),
435				listen_addr: ipv6,
436				max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity,
437				max_connections: rpc_configuration.max_connections,
438				max_payload_in_mb: rpc_configuration.max_request_size,
439				max_payload_out_mb: rpc_configuration.max_response_size,
440				max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn,
441				rpc_methods: rpc_configuration.methods.into(),
442				rate_limit: rpc_configuration.rate_limit,
443				rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers,
444				rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(),
445				retry_random_port: true,
446				is_optional: true,
447			},
448		]
449	};
450
451	let metrics = sc_rpc_server::RpcMetrics::new(registry)?;
452
453	let server_config = sc_rpc_server::Config {
454		endpoints,
455		metrics,
456		rpc_api,
457		id_provider: rpc_id_provider,
458		request_logger_limit: rpc_configuration.request_logger_limit,
459		rpc_runtime,
460	};
461
462	// TODO: https://github.com/paritytech/substrate/issues/13773
463	//
464	// `block_in_place` is a hack to allow callers to call `block_on` prior to
465	// calling `start_rpc_servers`.
466	match tokio::task::block_in_place(|| {
467		tokio_handle.block_on(sc_rpc_server::start_server(server_config))
468	}) {
469		Ok(server) => Ok(server),
470		Err(e) => Err(Error::Application(e)),
471	}
472}
473
474/// Transaction pool adapter.
475pub struct TransactionPoolAdapter<C, P: ?Sized> {
476	pool: Arc<P>,
477	client: Arc<C>,
478}
479
480impl<C, P: ?Sized> TransactionPoolAdapter<C, P> {
481	/// Constructs a new instance of [`TransactionPoolAdapter`].
482	pub fn new(pool: Arc<P>, client: Arc<C>) -> Self {
483		Self { pool, client }
484	}
485}
486
487/// Get transactions for propagation.
488///
489/// Function extracted to simplify the test and prevent creating `ServiceFactory`.
490fn transactions_to_propagate<Pool, B, H, E>(pool: &Pool) -> Vec<(H, Arc<B::Extrinsic>)>
491where
492	Pool: TransactionPool<Block = B, Hash = H, Error = E> + ?Sized,
493	B: BlockT,
494	H: std::hash::Hash + Eq + sp_runtime::traits::Member + sp_runtime::traits::MaybeSerialize,
495	E: IntoPoolError + From<sc_transaction_pool_api::error::Error>,
496{
497	pool.ready()
498		.filter(|t| t.is_propagable())
499		.map(|t| {
500			let hash = t.hash().clone();
501			let ex = t.data().clone();
502			(hash, ex)
503		})
504		.collect()
505}
506
507impl<B, H, C, Pool, E> sc_network_transactions::config::TransactionPool<H, B>
508	for TransactionPoolAdapter<C, Pool>
509where
510	C: HeaderBackend<B>
511		+ BlockBackend<B>
512		+ HeaderMetadata<B, Error = sp_blockchain::Error>
513		+ ProofProvider<B>
514		+ Send
515		+ Sync
516		+ 'static,
517	Pool: 'static + TransactionPool<Block = B, Hash = H, Error = E> + ?Sized,
518	B: BlockT,
519	H: std::hash::Hash + Eq + sp_runtime::traits::Member + sp_runtime::traits::MaybeSerialize,
520	E: 'static + IntoPoolError + From<sc_transaction_pool_api::error::Error>,
521{
522	fn transactions(&self) -> Vec<(H, Arc<B::Extrinsic>)> {
523		transactions_to_propagate(&*self.pool)
524	}
525
526	fn hash_of(&self, transaction: &B::Extrinsic) -> H {
527		self.pool.hash_of(transaction)
528	}
529
530	fn import(&self, transaction: B::Extrinsic) -> TransactionImportFuture {
531		let encoded = transaction.encode();
532		let uxt = match Decode::decode(&mut &encoded[..]) {
533			Ok(uxt) => uxt,
534			Err(e) => {
535				debug!(target: sc_transaction_pool::LOG_TARGET, "Transaction invalid: {:?}", e);
536				return Box::pin(futures::future::ready(TransactionImport::Bad));
537			},
538		};
539
540		let start = std::time::Instant::now();
541		let pool = self.pool.clone();
542		let client = self.client.clone();
543		Box::pin(async move {
544			match pool
545				.submit_one(
546					client.info().best_hash,
547					sc_transaction_pool_api::TransactionSource::External,
548					uxt,
549				)
550				.await
551			{
552				Ok(_) => {
553					let elapsed = start.elapsed();
554					trace!(target: sc_transaction_pool::LOG_TARGET, "import transaction: {elapsed:?}");
555					TransactionImport::NewGood
556				},
557				Err(e) => match e.into_pool_error() {
558					Ok(sc_transaction_pool_api::error::Error::AlreadyImported(_)) => {
559						TransactionImport::KnownGood
560					},
561					Ok(_) => TransactionImport::Bad,
562					Err(_) => {
563						// it is not bad at least, just some internal node logic error, so peer is
564						// innocent.
565						TransactionImport::KnownGood
566					},
567				},
568			}
569		})
570	}
571
572	fn on_broadcasted(&self, propagations: HashMap<H, Vec<String>>) {
573		self.pool.on_broadcasted(propagations)
574	}
575
576	fn transaction(&self, hash: &H) -> Option<Arc<B::Extrinsic>> {
577		self.pool.ready_transaction(hash).and_then(
578			// Only propagable transactions should be resolved for network service.
579			|tx| tx.is_propagable().then(|| tx.data().clone()),
580		)
581	}
582}
583
584#[cfg(test)]
585mod tests {
586	use super::*;
587	use futures::executor::block_on;
588	use sc_network_types::multihash::Code;
589	use sc_transaction_pool::BasicPool;
590	use sp_consensus::SelectChain;
591	use substrate_test_runtime_client::{
592		prelude::*,
593		runtime::{ExtrinsicBuilder, Transfer, TransferData},
594	};
595
596	/// `/ip4/1.2.3.4/tcp/30333`, as the libp2p backend reports its listen addresses.
597	fn tcp_address() -> Multiaddr {
598		Multiaddr::empty()
599			.with(Protocol::Ip4([1, 2, 3, 4].into()))
600			.with(Protocol::Tcp(30333))
601	}
602
603	#[test]
604	fn peer_id_appended_when_missing() {
605		let peer_id = PeerId::random();
606
607		assert_eq!(
608			with_local_peer_id(tcp_address(), peer_id),
609			tcp_address().with(Protocol::P2p(peer_id.into())),
610		);
611	}
612
613	#[test]
614	fn peer_id_not_appended_twice() {
615		// The litep2p backend hands out addresses that already carry the local peer id.
616		let peer_id = PeerId::random();
617		let address = tcp_address().with(Protocol::P2p(peer_id.into()));
618
619		assert_eq!(with_local_peer_id(address.clone(), peer_id), address);
620	}
621
622	#[test]
623	fn webrtc_certhash_preserved() {
624		// This is the address shape a WebRTC-enabled node hands to smoldot.
625		let peer_id = PeerId::random();
626		let address = Multiaddr::empty()
627			.with(Protocol::Ip4([1, 2, 3, 4].into()))
628			.with(Protocol::Udp(30334))
629			.with(Protocol::WebRTCDirect)
630			.with(Protocol::Certhash(Code::Sha2_256.digest(b"certificate")))
631			.with(Protocol::P2p(peer_id.into()));
632
633		assert_eq!(with_local_peer_id(address.clone(), peer_id), address);
634	}
635
636	#[test]
637	fn should_not_propagate_transactions_that_are_marked_as_such() {
638		// given
639		let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
640		let client = Arc::new(client);
641		let spawner = sp_core::testing::TaskExecutor::new();
642		let pool = Arc::from(BasicPool::new_full(
643			Default::default(),
644			true.into(),
645			None,
646			spawner,
647			client.clone(),
648		));
649		let source = sp_runtime::transaction_validity::TransactionSource::External;
650		let best = block_on(longest_chain.best_chain()).unwrap();
651		let transaction = Transfer {
652			amount: 5,
653			nonce: 0,
654			from: Sr25519Keyring::Alice.into(),
655			to: Sr25519Keyring::Bob.into(),
656		}
657		.into_unchecked_extrinsic();
658		block_on(pool.submit_one(best.hash(), source, transaction.clone())).unwrap();
659		block_on(pool.submit_one(
660			best.hash(),
661			source,
662			ExtrinsicBuilder::new_call_do_not_propagate().nonce(1).build(),
663		))
664		.unwrap();
665		assert_eq!(pool.status().ready, 2);
666
667		// when
668		let transactions = transactions_to_propagate(&*pool);
669
670		// then
671		assert_eq!(transactions.len(), 1);
672		assert!(TransferData::try_from(&*transactions[0].1).is_ok());
673	}
674}