referrerpolicy=no-referrer-when-downgrade

pallet_revive_eth_rpc/
block_info_provider.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	ClientError, LOG_TARGET,
20	client::{SubscriptionType, SubstrateBlock, SubstrateBlockNumber},
21	subxt_client::SrcChainConfig,
22};
23use jsonrpsee::core::async_trait;
24use sp_core::H256;
25use std::sync::Arc;
26use subxt::{
27	OnlineClient, config::RpcConfigFor, error::OnlineClientAtBlockError,
28	rpcs::methods::LegacyRpcMethods,
29};
30use tokio::sync::RwLock;
31
32/// BlockInfoProvider cache and retrieves information about blocks.
33#[async_trait]
34pub trait BlockInfoProvider: Send + Sync {
35	/// Updates the latest block or the latest finalized block, depending on `subscription_type`.
36	///
37	/// Returns the new latest block, or `None` when the latest block did not change.
38	async fn update_latest(
39		&self,
40		block: Arc<SubstrateBlock>,
41		subscription_type: SubscriptionType,
42	) -> Option<Arc<SubstrateBlock>>;
43
44	/// Return the latest finalized block.
45	async fn latest_finalized_block(&self) -> Arc<SubstrateBlock>;
46
47	/// Return the latest block.
48	async fn latest_block(&self) -> Arc<SubstrateBlock>;
49
50	/// Return the latest block number
51	async fn latest_block_number(&self) -> SubstrateBlockNumber {
52		self.latest_block().await.block_number()
53	}
54
55	/// Get block by block_number.
56	async fn block_by_number(
57		&self,
58		block_number: SubstrateBlockNumber,
59	) -> Result<Option<Arc<SubstrateBlock>>, ClientError>;
60
61	/// Get block by block hash.
62	async fn block_by_hash(&self, hash: &H256) -> Result<Option<Arc<SubstrateBlock>>, ClientError>;
63}
64
65/// Provides information about blocks.
66#[derive(Clone)]
67pub struct SubxtBlockInfoProvider {
68	/// The latest block.
69	latest_block: Arc<RwLock<Arc<SubstrateBlock>>>,
70
71	/// The latest finalized block.
72	latest_finalized_block: Arc<RwLock<Arc<SubstrateBlock>>>,
73
74	/// The rpc client, used to fetch blocks not in the cache.
75	rpc: LegacyRpcMethods<RpcConfigFor<SrcChainConfig>>,
76
77	/// The api client, used to fetch blocks not in the cache.
78	api: OnlineClient<SrcChainConfig>,
79}
80
81impl SubxtBlockInfoProvider {
82	pub async fn new(
83		api: OnlineClient<SrcChainConfig>,
84		rpc: LegacyRpcMethods<RpcConfigFor<SrcChainConfig>>,
85	) -> Result<Self, ClientError> {
86		let latest_finalized_block = Arc::new(api.at_current_block().await?);
87		let best_hash = rpc.chain_get_block_hash(None).await?.ok_or(ClientError::BlockNotFound)?;
88		let latest_block = Arc::new(api.at_block(best_hash).await?);
89		Ok(Self {
90			api,
91			rpc,
92			latest_block: Arc::new(RwLock::new(latest_block)),
93			latest_finalized_block: Arc::new(RwLock::new(latest_finalized_block)),
94		})
95	}
96
97	/// Returns whether the block at `number` with `hash` is still on the canonical chain.
98	///
99	/// When eth-rpc reconnects to a node that is behind, the block counts as not canonical, so the
100	/// latest block moves back to one that node can serve.
101	async fn is_canonical(&self, number: SubstrateBlockNumber, hash: H256) -> bool {
102		match self.rpc.chain_get_block_hash(Some(number.into())).await {
103			Ok(canonical) => canonical == Some(hash),
104			Err(err) => {
105				log::debug!(target: LOG_TARGET,
106					"Failed to check if block #{number} ({hash:?}) is canonical, keeping it as the latest block: {err:?}");
107				true
108			},
109		}
110	}
111
112	/// Updates the latest finalized block, and the latest block when it is no longer ahead of it.
113	///
114	/// Returns the latest block when the finalized block moved it, or `None` otherwise.
115	async fn update_finalized(&self, block: Arc<SubstrateBlock>) -> Option<Arc<SubstrateBlock>> {
116		// The finalized block only ever increases.
117		let mut finalized = self.latest_finalized_block.write().await;
118		if block.block_number() >= finalized.block_number() {
119			*finalized = block;
120		}
121		let finalized_block = finalized.clone();
122		drop(finalized);
123
124		// A finalized block is on the best chain, so the best block is never behind it.
125		let mut best = self.latest_block.write().await;
126		if finalized_block.block_number() >= best.block_number() &&
127			finalized_block.block_hash() != best.block_hash()
128		{
129			log::debug!(target: LOG_TARGET,
130				"Advancing the latest block #{} ({:?}) to the finalized block #{} ({:?}): it is no longer ahead",
131				best.block_number(),
132				best.block_hash(),
133				finalized_block.block_number(),
134				finalized_block.block_hash());
135			*best = finalized_block.clone();
136			return Some(finalized_block);
137		}
138
139		None
140	}
141
142	/// Updates the latest block, ignoring a replay of a block at or below the cached one.
143	///
144	/// Returns the new latest block, or `None` when the latest block did not change.
145	async fn update_best(&self, block: Arc<SubstrateBlock>) -> Option<Arc<SubstrateBlock>> {
146		let is_same_or_above = |other: &SubstrateBlock| {
147			block.block_number() > other.block_number() || block.block_hash() == other.block_hash()
148		};
149
150		let mut best = self.latest_block.write().await;
151		if is_same_or_above(&best) {
152			let changed = block.block_hash() != best.block_hash();
153			*best = block.clone();
154			return changed.then_some(block);
155		}
156
157		let (best_number, best_hash) = (best.block_number(), best.block_hash());
158		drop(best);
159
160		// The chain's best block never falls behind the finalized block.
161		let finalized = self.latest_finalized_block.read().await.clone();
162		if !is_same_or_above(&finalized) {
163			log::debug!(target: LOG_TARGET,
164				"Ignoring best block #{} ({:?}): it is neither the finalized block #{} ({:?}) nor above it",
165				block.block_number(),
166				block.block_hash(),
167				finalized.block_number(),
168				finalized.block_hash());
169			return None;
170		}
171
172		// A lower block is a replay, unless the stored best block is no longer canonical.
173		if self.is_canonical(best_number, best_hash).await {
174			return None;
175		}
176
177		let mut best = self.latest_block.write().await;
178		if best.block_hash() != best_hash {
179			debug_assert!(false, "the latest block must have a single writer");
180			log::warn!(target: LOG_TARGET,
181				"Ignoring best block #{} ({:?}): the latest block was concurrently replaced with #{} ({:?})",
182				block.block_number(),
183				block.block_hash(),
184				best.block_number(),
185				best.block_hash());
186			return None;
187		}
188
189		log::trace!(target: LOG_TARGET,
190			"Moving the latest block back from #{best_number} ({best_hash:?}) to #{} ({:?}): it is no longer canonical",
191			block.block_number(),
192			block.block_hash());
193		*best = block.clone();
194		Some(block)
195	}
196}
197
198#[async_trait]
199impl BlockInfoProvider for SubxtBlockInfoProvider {
200	async fn update_latest(
201		&self,
202		block: Arc<SubstrateBlock>,
203		subscription_type: SubscriptionType,
204	) -> Option<Arc<SubstrateBlock>> {
205		match subscription_type {
206			SubscriptionType::FinalizedBlocks => self.update_finalized(block).await,
207			SubscriptionType::BestBlocks => self.update_best(block).await,
208		}
209	}
210
211	async fn latest_block(&self) -> Arc<SubstrateBlock> {
212		self.latest_block.read().await.clone()
213	}
214
215	async fn latest_finalized_block(&self) -> Arc<SubstrateBlock> {
216		self.latest_finalized_block.read().await.clone()
217	}
218
219	async fn block_by_number(
220		&self,
221		block_number: SubstrateBlockNumber,
222	) -> Result<Option<Arc<SubstrateBlock>>, ClientError> {
223		let latest = self.latest_block().await;
224		if block_number == latest.block_number() {
225			return Ok(Some(latest));
226		}
227
228		let latest_finalized = self.latest_finalized_block().await;
229		if block_number == latest_finalized.block_number() {
230			return Ok(Some(latest_finalized));
231		}
232
233		let Some(hash) = self.rpc.chain_get_block_hash(Some(block_number.into())).await? else {
234			return Ok(None);
235		};
236
237		match self.api.at_block(hash).await {
238			Ok(block) => Ok(Some(Arc::new(block))),
239			Err(
240				OnlineClientAtBlockError::BlockHeaderNotFound { .. } |
241				OnlineClientAtBlockError::BlockNotFound { .. },
242			) => Ok(None),
243			Err(err) => Err(err.into()),
244		}
245	}
246
247	async fn block_by_hash(&self, hash: &H256) -> Result<Option<Arc<SubstrateBlock>>, ClientError> {
248		let latest = self.latest_block().await;
249		if hash == &latest.block_hash() {
250			return Ok(Some(latest));
251		}
252
253		let latest_finalized = self.latest_finalized_block().await;
254		if hash == &latest_finalized.block_hash() {
255			return Ok(Some(latest_finalized));
256		}
257
258		match self.api.at_block(*hash).await {
259			Ok(block) => Ok(Some(Arc::new(block))),
260			Err(
261				OnlineClientAtBlockError::BlockHeaderNotFound { .. } |
262				OnlineClientAtBlockError::BlockNotFound { .. },
263			) => {
264				log::trace!(target: LOG_TARGET, "block_by_hash: block {hash:?} not found");
265				Ok(None)
266			},
267			Err(err) => {
268				log::trace!(target: LOG_TARGET, "block_by_hash: failed to fetch block {hash:?}: {err:?}");
269				Err(err.into())
270			},
271		}
272	}
273}
274
275#[cfg(test)]
276pub mod test {
277	use super::*;
278	use crate::BlockInfo;
279	use codec::Decode;
280	use std::sync::{
281		Mutex,
282		atomic::{AtomicBool, AtomicUsize, Ordering},
283	};
284	use subxt::{
285		backend::LegacyBackend,
286		config::{
287			polkadot::PolkadotConfigBuilder,
288			substrate::{SpecVersionForRange, SubstrateHeader},
289		},
290		metadata::Metadata,
291		rpcs::{
292			Error as RpcError, RpcClient, UserError,
293			client::{MockRpcClient, mock_rpc_client::Json},
294		},
295	};
296
297	/// A Noop BlockInfoProvider used to test [`crate::ReceiptProvider`].
298	pub struct MockBlockInfoProvider;
299
300	pub struct MockBlockInfo {
301		pub number: SubstrateBlockNumber,
302		pub hash: H256,
303	}
304
305	impl BlockInfo for MockBlockInfo {
306		fn hash(&self) -> H256 {
307			self.hash
308		}
309		fn number(&self) -> SubstrateBlockNumber {
310			self.number
311		}
312	}
313
314	#[async_trait]
315	impl BlockInfoProvider for MockBlockInfoProvider {
316		async fn update_latest(
317			&self,
318			_block: Arc<SubstrateBlock>,
319			_subscription_type: SubscriptionType,
320		) -> Option<Arc<SubstrateBlock>> {
321			None
322		}
323
324		async fn latest_finalized_block(&self) -> Arc<SubstrateBlock> {
325			unimplemented!()
326		}
327
328		async fn latest_block(&self) -> Arc<SubstrateBlock> {
329			unimplemented!()
330		}
331
332		async fn latest_block_number(&self) -> SubstrateBlockNumber {
333			2u64
334		}
335
336		async fn block_by_number(
337			&self,
338			_block_number: SubstrateBlockNumber,
339		) -> Result<Option<Arc<SubstrateBlock>>, ClientError> {
340			Ok(None)
341		}
342
343		async fn block_by_hash(
344			&self,
345			_hash: &H256,
346		) -> Result<Option<Arc<SubstrateBlock>>, ClientError> {
347			Ok(None)
348		}
349	}
350
351	/// A config carrying the generated runtime metadata for every block.
352	pub(crate) fn chain_config() -> SrcChainConfig {
353		let metadata_bytes: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/revive_chain.scale"));
354		let metadata = Metadata::decode(&mut &metadata_bytes[..]).unwrap();
355		PolkadotConfigBuilder::new()
356			.set_metadata_for_spec_versions(std::iter::once((0u32, metadata.into())))
357			.set_spec_version_for_block_ranges(std::iter::once(SpecVersionForRange {
358				block_range: 0..u64::MAX,
359				spec_version: 0,
360				transaction_version: 0,
361			}))
362			.build()
363	}
364
365	/// A block at the given block number, on one of two branches.
366	#[derive(Clone, Copy)]
367	enum MockBlockId {
368		MainBranch(u64),
369		SideBranch(u64),
370	}
371
372	impl MockBlockId {
373		/// Offsets the block number in the hash byte; keeps the genesis hash non-zero and
374		/// the two branches in disjoint ranges.
375		const MAIN_BRANCH_OFFSET: u8 = 0x01;
376		const SIDE_BRANCH_OFFSET: u8 = 0xa0;
377		/// The highest block number both offsets encode without leaving their range.
378		const MAX_BLOCK_NUMBER: u64 = (u8::MAX - Self::SIDE_BRANCH_OFFSET) as u64;
379
380		/// The hash of this block: its block number as the repeated byte, shifted by the
381		/// variant's offset.
382		fn hash(self) -> H256 {
383			let (offset, number) = match self {
384				MockBlockId::MainBranch(number) => (Self::MAIN_BRANCH_OFFSET, number),
385				MockBlockId::SideBranch(number) => (Self::SIDE_BRANCH_OFFSET, number),
386			};
387			assert!(
388				number <= Self::MAX_BLOCK_NUMBER,
389				"a mock block number must not exceed {}",
390				Self::MAX_BLOCK_NUMBER
391			);
392			H256::repeat_byte(offset + number as u8)
393		}
394
395		fn number(self) -> u64 {
396			match self {
397				MockBlockId::MainBranch(number) | MockBlockId::SideBranch(number) => number,
398			}
399		}
400
401		/// Recover the block embedded in a hash, so a header can be derived for any
402		/// `MockBlockId` hash without a table of known blocks.
403		fn from_hash(hash: H256) -> Option<MockBlockId> {
404			let bytes = hash.as_fixed_bytes();
405			let byte = bytes[0];
406			if bytes.iter().any(|other| other != &byte) {
407				return None;
408			}
409			if let Some(number) = byte.checked_sub(Self::SIDE_BRANCH_OFFSET) {
410				Some(MockBlockId::SideBranch(number.into()))
411			} else if let Some(number) = byte.checked_sub(Self::MAIN_BRANCH_OFFSET) {
412				Some(MockBlockId::MainBranch(number.into()))
413			} else {
414				None
415			}
416		}
417	}
418
419	const _: () = assert!(
420		MockBlockId::MAIN_BRANCH_OFFSET as u64 + MockBlockId::MAX_BLOCK_NUMBER <
421			MockBlockId::SIDE_BRANCH_OFFSET as u64,
422		"a main-branch hash must stay below the side-branch range"
423	);
424
425	/// Decode the JSON-RPC params into the handler's parameter tuple.
426	fn decode_params<ParamsTuple: serde::de::DeserializeOwned>(
427		params: Option<Box<serde_json::value::RawValue>>,
428	) -> ParamsTuple {
429		let raw = params.expect("legacy RPC methods always send params");
430		serde_json::from_str(raw.get()).expect("params decode into the parameter tuple")
431	}
432
433	/// Build the incoming block that tests pass to `update_latest`.
434	async fn block_at(
435		api: &OnlineClient<SrcChainConfig>,
436		block: MockBlockId,
437	) -> Arc<SubstrateBlock> {
438		Arc::new(api.at_block(block.hash()).await.unwrap())
439	}
440
441	/// The heads of the mocked chain.
442	#[derive(Clone)]
443	struct MockChainHeads {
444		/// The chain's best block.
445		best_block: Arc<Mutex<MockBlockId>>,
446		/// The chain's finalized block.
447		finalized_block: MockBlockId,
448		/// The number of `chain_getBlockHash` calls received.
449		block_hash_lookup_count: Arc<AtomicUsize>,
450		/// Fail block hash lookups while set.
451		fail_block_hash_lookups: Arc<AtomicBool>,
452		/// Answer best block requests with `null` while set.
453		report_no_best_block: Arc<AtomicBool>,
454	}
455
456	impl Default for MockChainHeads {
457		fn default() -> Self {
458			const INITIAL_BEST_BLOCK_NUMBER: u64 = 7;
459			const INITIAL_FINALIZED_BLOCK_NUMBER: u64 = 5;
460			Self {
461				best_block: Arc::new(Mutex::new(MockBlockId::MainBranch(
462					INITIAL_BEST_BLOCK_NUMBER,
463				))),
464				finalized_block: MockBlockId::MainBranch(INITIAL_FINALIZED_BLOCK_NUMBER),
465				block_hash_lookup_count: Arc::default(),
466				fail_block_hash_lookups: Arc::default(),
467				report_no_best_block: Arc::default(),
468			}
469		}
470	}
471
472	impl MockChainHeads {
473		/// The chain imports `block` as its new best and notifies `provider`.
474		async fn import_best(
475			&self,
476			provider: &SubxtBlockInfoProvider,
477			api: &OnlineClient<SrcChainConfig>,
478			block: MockBlockId,
479		) -> Option<Arc<SubstrateBlock>> {
480			*self.best_block.lock().unwrap() = block;
481			provider
482				.update_latest(block_at(api, block).await, SubscriptionType::BestBlocks)
483				.await
484		}
485
486		/// Build the clients that talk to this mocked chain.
487		async fn clients(
488			&self,
489		) -> (OnlineClient<SrcChainConfig>, LegacyRpcMethods<RpcConfigFor<SrcChainConfig>>) {
490			let config = chain_config();
491
492			let mock = MockRpcClient::builder()
493				.method_handler("chain_getBlockHash", {
494					let chain_heads = self.clone();
495					move |params: Option<Box<serde_json::value::RawValue>>| {
496						let (number,): (Option<u64>,) = decode_params(params);
497						chain_heads.block_hash_lookup_count.fetch_add(1, Ordering::SeqCst);
498						let response = if chain_heads.fail_block_hash_lookups.load(Ordering::SeqCst)
499						{
500							Err(RpcError::User(UserError {
501								code: -32000,
502								message: "scripted failure".into(),
503								data: None,
504							}))
505						} else {
506							match number {
507								None => {
508									if chain_heads.report_no_best_block.load(Ordering::SeqCst) {
509										Ok(None)
510									} else {
511										Ok(Some(chain_heads.best_block.lock().unwrap().hash()))
512									}
513								},
514								Some(number) => {
515									let best_block = *chain_heads.best_block.lock().unwrap();
516									Ok(if number > best_block.number() {
517										None
518									} else if number == best_block.number() {
519										Some(best_block.hash())
520									} else {
521										Some(MockBlockId::MainBranch(number).hash())
522									})
523								},
524							}
525						};
526						async move { response.map(Json) }
527					}
528				})
529				.method_handler("chain_getFinalizedHead", {
530					let finalized_hash = self.finalized_block.hash();
531					move |_params: Option<Box<serde_json::value::RawValue>>| async move {
532						Json(finalized_hash)
533					}
534				})
535				.method_handler(
536					"chain_getHeader",
537					|params: Option<Box<serde_json::value::RawValue>>| {
538						let (hash,): (H256,) = decode_params(params);
539						let header = MockBlockId::from_hash(hash).map(|block| SubstrateHeader {
540							parent_hash: H256::zero(),
541							number: block.number(),
542							state_root: H256::zero(),
543							extrinsics_root: H256::zero(),
544							digest: Default::default(),
545						});
546						async move { Json(header) }
547					},
548				)
549				.build();
550
551			let rpc_client = RpcClient::new(mock);
552			let backend = LegacyBackend::<SrcChainConfig>::builder().build(rpc_client.clone());
553			let api =
554				OnlineClient::<SrcChainConfig>::from_backend_with_config(config, Arc::new(backend))
555					.await
556					.unwrap();
557			let rpc = LegacyRpcMethods::<RpcConfigFor<SrcChainConfig>>::new(rpc_client);
558			(api, rpc)
559		}
560
561		/// Build a `SubxtBlockInfoProvider` backed by this mocked chain, and the client
562		/// used to construct the blocks the tests pass to `update_latest`.
563		async fn provider(&self) -> (SubxtBlockInfoProvider, OnlineClient<SrcChainConfig>) {
564			let (api, rpc) = self.clients().await;
565			let provider = SubxtBlockInfoProvider::new(api.clone(), rpc).await.unwrap();
566			// Setup queries end here: tests count only their own block hash lookups.
567			self.block_hash_lookup_count.store(0, Ordering::SeqCst);
568			(provider, api)
569		}
570	}
571
572	#[tokio::test]
573	async fn construction_seeds_the_latest_and_finalized_blocks() {
574		let chain_heads = MockChainHeads::default();
575		let (provider, _api) = chain_heads.provider().await;
576
577		assert_eq!(
578			provider.latest_block().await.block_hash(),
579			chain_heads.best_block.lock().unwrap().hash(),
580			"the latest block starts at the chain's best block"
581		);
582		assert_eq!(
583			provider.latest_finalized_block().await.block_hash(),
584			chain_heads.finalized_block.hash(),
585			"the latest finalized block starts at the chain's finalized block"
586		);
587	}
588
589	#[tokio::test]
590	async fn construction_fails_without_a_best_block() {
591		let chain_heads = MockChainHeads::default();
592		let (api, rpc) = chain_heads.clients().await;
593
594		chain_heads.report_no_best_block.store(true, Ordering::SeqCst);
595		assert!(
596			matches!(SubxtBlockInfoProvider::new(api, rpc).await, Err(ClientError::BlockNotFound)),
597			"construction fails when the chain reports no best block"
598		);
599	}
600
601	#[tokio::test]
602	async fn best_block_updates_follow_the_chain_head() {
603		let chain_heads = MockChainHeads::default();
604		let (provider, api) = chain_heads.provider().await;
605		let best = chain_heads.best_block.lock().unwrap().number();
606
607		let head = chain_heads
608			.import_best(&provider, &api, MockBlockId::MainBranch(best + 1))
609			.await;
610		assert_eq!(
611			provider.latest_block().await.block_hash(),
612			MockBlockId::MainBranch(best + 1).hash(),
613			"a higher block becomes the latest block"
614		);
615		assert_eq!(
616			head.map(|head| head.block_hash()),
617			Some(MockBlockId::MainBranch(best + 1).hash()),
618			"a higher block is returned as the new latest block"
619		);
620		assert_eq!(
621			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
622			0,
623			"no block hash lookup for a higher block"
624		);
625
626		let head = chain_heads
627			.import_best(&provider, &api, MockBlockId::SideBranch(best + 1))
628			.await;
629		assert_eq!(
630			provider.latest_block().await.block_hash(),
631			MockBlockId::SideBranch(best + 1).hash(),
632			"a same-number block from a side branch replaces the latest block"
633		);
634		assert_eq!(
635			head.map(|head| head.block_hash()),
636			Some(MockBlockId::SideBranch(best + 1).hash()),
637			"a same-number block from a side branch is returned as the new latest block"
638		);
639		assert_eq!(
640			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
641			1,
642			"one block hash lookup to accept a same-number block from another branch"
643		);
644
645		chain_heads
646			.import_best(&provider, &api, MockBlockId::SideBranch(best + 2))
647			.await;
648		assert_eq!(
649			provider.latest_block().await.block_hash(),
650			MockBlockId::SideBranch(best + 2).hash(),
651			"a higher side-branch block becomes the latest block"
652		);
653		assert_eq!(
654			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
655			1,
656			"no block hash lookup for a higher side-branch block"
657		);
658
659		let latest = provider.latest_block().await;
660		let head = provider
661			.update_latest(
662				block_at(&api, MockBlockId::SideBranch(best + 2)).await,
663				SubscriptionType::BestBlocks,
664			)
665			.await;
666		assert!(
667			!Arc::ptr_eq(&latest, &provider.latest_block().await),
668			"a repeat of the latest block replaces the cached one"
669		);
670		assert!(head.is_none(), "a repeat of the latest block does not change it");
671		assert_eq!(
672			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
673			1,
674			"no block hash lookup for a repeat of the latest block"
675		);
676	}
677
678	#[tokio::test]
679	async fn reorgs_are_followed_and_replays_are_ignored() {
680		let chain_heads = MockChainHeads::default();
681		let (provider, api) = chain_heads.provider().await;
682		let best = chain_heads.best_block.lock().unwrap().number();
683		let finalized = chain_heads.finalized_block.number();
684
685		chain_heads
686			.import_best(&provider, &api, MockBlockId::SideBranch(best - 1))
687			.await;
688		assert_eq!(
689			provider.latest_block().await.block_hash(),
690			MockBlockId::SideBranch(best - 1).hash(),
691			"a lower block is accepted after a reorg to a shorter branch"
692		);
693		assert_eq!(
694			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
695			1,
696			"one block hash lookup for the accepted lower block"
697		);
698
699		// The chain's best block moves back to the main branch without a notification.
700		*chain_heads.best_block.lock().unwrap() = MockBlockId::MainBranch(best);
701
702		provider
703			.update_latest(
704				block_at(&api, MockBlockId::MainBranch(finalized)).await,
705				SubscriptionType::BestBlocks,
706			)
707			.await;
708		assert_eq!(
709			provider.latest_block().await.block_hash(),
710			MockBlockId::MainBranch(finalized).hash(),
711			"a lower block is accepted once the latest block is no longer canonical"
712		);
713		assert_eq!(
714			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
715			2,
716			"one block hash lookup for each accepted lower block"
717		);
718
719		// The chain's next notification carries its best block.
720		chain_heads.import_best(&provider, &api, MockBlockId::MainBranch(best)).await;
721		assert_eq!(
722			provider.latest_block().await.block_hash(),
723			MockBlockId::MainBranch(best).hash(),
724			"the chain's best block becomes the latest block"
725		);
726		assert_eq!(
727			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
728			2,
729			"no block hash lookup for the chain's best block"
730		);
731
732		let head = provider
733			.update_latest(
734				block_at(&api, MockBlockId::MainBranch(best - 1)).await,
735				SubscriptionType::BestBlocks,
736			)
737			.await;
738		assert_eq!(
739			provider.latest_block().await.block_hash(),
740			MockBlockId::MainBranch(best).hash(),
741			"a lower block is ignored while the latest block is still canonical"
742		);
743		assert!(
744			head.is_none(),
745			"a lower block does not change a latest block that is still canonical"
746		);
747		assert_eq!(
748			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
749			3,
750			"one more block hash lookup to ignore an old block"
751		);
752
753		provider
754			.update_latest(
755				block_at(&api, MockBlockId::SideBranch(finalized - 1)).await,
756				SubscriptionType::BestBlocks,
757			)
758			.await;
759		assert_eq!(
760			provider.latest_block().await.block_hash(),
761			MockBlockId::MainBranch(best).hash(),
762			"a block below the finalized block is ignored"
763		);
764		assert_eq!(
765			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
766			3,
767			"no block hash lookup for a block below the finalized block"
768		);
769
770		let head = provider
771			.update_latest(
772				block_at(&api, MockBlockId::SideBranch(finalized)).await,
773				SubscriptionType::BestBlocks,
774			)
775			.await;
776		assert_eq!(
777			provider.latest_block().await.block_hash(),
778			MockBlockId::MainBranch(best).hash(),
779			"a block from another branch at the finalized block's number is ignored"
780		);
781		assert!(
782			head.is_none(),
783			"a block that contradicts the finalized block does not change the latest block"
784		);
785		assert_eq!(
786			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
787			3,
788			"no block hash lookup for a block conflicting with the finalized block"
789		);
790
791		provider
792			.update_latest(
793				block_at(&api, MockBlockId::SideBranch(best)).await,
794				SubscriptionType::BestBlocks,
795			)
796			.await;
797		assert_eq!(
798			provider.latest_block().await.block_hash(),
799			MockBlockId::MainBranch(best).hash(),
800			"a same-number block from another branch is ignored while the latest block is still \
801			 canonical"
802		);
803		assert_eq!(
804			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
805			4,
806			"one more block hash lookup to ignore a same-number block"
807		);
808	}
809
810	#[tokio::test]
811	async fn failed_block_hash_lookups_keep_the_stored_best_block() {
812		let chain_heads = MockChainHeads::default();
813		let (provider, api) = chain_heads.provider().await;
814		let best = chain_heads.best_block.lock().unwrap().number();
815		let finalized = chain_heads.finalized_block.number();
816
817		chain_heads.fail_block_hash_lookups.store(true, Ordering::SeqCst);
818		provider
819			.update_latest(
820				block_at(&api, MockBlockId::MainBranch(finalized)).await,
821				SubscriptionType::BestBlocks,
822			)
823			.await;
824		assert_eq!(
825			provider.latest_block().await.block_hash(),
826			MockBlockId::MainBranch(best).hash(),
827			"an old block is ignored while the block hash lookup fails"
828		);
829		assert_eq!(
830			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
831			1,
832			"one block hash lookup for the ignored old block"
833		);
834
835		chain_heads.fail_block_hash_lookups.store(false, Ordering::SeqCst);
836		chain_heads
837			.import_best(&provider, &api, MockBlockId::SideBranch(best - 1))
838			.await;
839		assert_eq!(
840			provider.latest_block().await.block_hash(),
841			MockBlockId::SideBranch(best - 1).hash(),
842			"a lower block is accepted once the block hash lookup succeeds"
843		);
844		assert_eq!(
845			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
846			2,
847			"one more block hash lookup for the accepted lower block"
848		);
849	}
850
851	#[tokio::test]
852	async fn finalized_blocks_never_move_backwards() {
853		let chain_heads = MockChainHeads::default();
854		let (provider, api) = chain_heads.provider().await;
855		let best = chain_heads.best_block.lock().unwrap().number();
856		let finalized = chain_heads.finalized_block.number();
857
858		provider
859			.update_latest(
860				block_at(&api, MockBlockId::MainBranch(finalized - 1)).await,
861				SubscriptionType::FinalizedBlocks,
862			)
863			.await;
864		assert_eq!(
865			provider.latest_finalized_block().await.block_hash(),
866			MockBlockId::MainBranch(finalized).hash(),
867			"a lower finalized block is ignored"
868		);
869
870		let head = provider
871			.update_latest(
872				block_at(&api, MockBlockId::MainBranch(finalized + 1)).await,
873				SubscriptionType::FinalizedBlocks,
874			)
875			.await;
876		assert_eq!(
877			provider.latest_finalized_block().await.block_hash(),
878			MockBlockId::MainBranch(finalized + 1).hash(),
879			"a higher finalized block becomes the latest finalized block"
880		);
881		assert_eq!(
882			provider.latest_block().await.block_hash(),
883			MockBlockId::MainBranch(best).hash(),
884			"the latest block stays put while it is ahead of the finalized block"
885		);
886		assert!(head.is_none(), "a finalized block below the latest block does not change it");
887
888		provider
889			.update_latest(
890				block_at(&api, MockBlockId::MainBranch(best + 1)).await,
891				SubscriptionType::FinalizedBlocks,
892			)
893			.await;
894		assert_eq!(
895			provider.latest_finalized_block().await.block_hash(),
896			MockBlockId::MainBranch(best + 1).hash(),
897			"a higher finalized block becomes the latest finalized block"
898		);
899		assert_eq!(
900			provider.latest_block().await.block_hash(),
901			MockBlockId::MainBranch(best + 1).hash(),
902			"the latest block follows a finalized block ahead of it"
903		);
904
905		chain_heads
906			.import_best(&provider, &api, MockBlockId::SideBranch(best + 2))
907			.await;
908		provider
909			.update_latest(
910				block_at(&api, MockBlockId::MainBranch(best + 2)).await,
911				SubscriptionType::FinalizedBlocks,
912			)
913			.await;
914		assert_eq!(
915			provider.latest_block().await.block_hash(),
916			MockBlockId::MainBranch(best + 2).hash(),
917			"a finalized block replaces a same-numbered latest block from another branch"
918		);
919
920		let latest = provider.latest_block().await;
921		provider
922			.update_latest(
923				block_at(&api, MockBlockId::MainBranch(best + 2)).await,
924				SubscriptionType::FinalizedBlocks,
925			)
926			.await;
927		assert!(
928			Arc::ptr_eq(&latest, &provider.latest_block().await),
929			"the latest block is unchanged when is already the finalized block"
930		);
931	}
932
933	#[tokio::test]
934	async fn stale_finalized_blocks_still_pull_up_the_latest_block() {
935		let inverted_best_block_number = MockChainHeads::default().finalized_block.number() - 2;
936		let chain_heads = MockChainHeads {
937			best_block: Arc::new(Mutex::new(MockBlockId::MainBranch(inverted_best_block_number))),
938			..MockChainHeads::default()
939		};
940		let (provider, api) = chain_heads.provider().await;
941		let finalized = chain_heads.finalized_block.number();
942
943		assert!(
944			provider.latest_finalized_block().await.block_number() >
945				provider.latest_block().await.block_number(),
946			"the latest block starts below the latest finalized block"
947		);
948
949		let head = provider
950			.update_latest(
951				block_at(&api, MockBlockId::MainBranch(finalized - 1)).await,
952				SubscriptionType::FinalizedBlocks,
953			)
954			.await;
955		assert_eq!(
956			provider.latest_finalized_block().await.block_hash(),
957			MockBlockId::MainBranch(finalized).hash(),
958			"a lower finalized block is ignored"
959		);
960		assert_eq!(
961			provider.latest_block().await.block_hash(),
962			MockBlockId::MainBranch(finalized).hash(),
963			"the latest block is pulled up to the latest finalized block"
964		);
965		assert_eq!(
966			head.map(|head| head.block_hash()),
967			Some(MockBlockId::MainBranch(finalized).hash()),
968			"the latest finalized block, not the lower one, is returned as the new latest block"
969		);
970	}
971
972	#[tokio::test]
973	async fn block_by_number_uses_the_cache_before_querying_the_chain() {
974		let chain_heads = MockChainHeads::default();
975		let (provider, _api) = chain_heads.provider().await;
976		let best = chain_heads.best_block.lock().unwrap().number();
977		let finalized = chain_heads.finalized_block.number();
978
979		let block = provider.block_by_number(best).await.unwrap().unwrap();
980		assert_eq!(
981			block.block_hash(),
982			MockBlockId::MainBranch(best).hash(),
983			"the latest block is returned for its number"
984		);
985		let block = provider.block_by_number(finalized).await.unwrap().unwrap();
986		assert_eq!(
987			block.block_hash(),
988			MockBlockId::MainBranch(finalized).hash(),
989			"the latest finalized block is returned for its number"
990		);
991		assert_eq!(
992			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
993			0,
994			"no block hash lookup for the cached blocks"
995		);
996
997		let block = provider.block_by_number(best - 1).await.unwrap().unwrap();
998		assert_eq!(
999			block.block_hash(),
1000			MockBlockId::MainBranch(best - 1).hash(),
1001			"an uncached block is fetched from the chain"
1002		);
1003		assert_eq!(
1004			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
1005			1,
1006			"one block hash lookup for an uncached block number"
1007		);
1008
1009		assert!(
1010			provider.block_by_number(best + 1).await.unwrap().is_none(),
1011			"no block exists above the chain's best block"
1012		);
1013		assert_eq!(
1014			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
1015			2,
1016			"one more block hash lookup for a number above the chain's best block"
1017		);
1018
1019		chain_heads.fail_block_hash_lookups.store(true, Ordering::SeqCst);
1020		assert!(
1021			provider.block_by_number(best - 1).await.is_err(),
1022			"a block hash lookup failure is returned as an error"
1023		);
1024	}
1025
1026	#[tokio::test]
1027	async fn block_by_hash_uses_the_cache_before_querying_the_chain() {
1028		let chain_heads = MockChainHeads::default();
1029		let (provider, _api) = chain_heads.provider().await;
1030		let best = chain_heads.best_block.lock().unwrap().number();
1031		let finalized = chain_heads.finalized_block.number();
1032
1033		let block = provider
1034			.block_by_hash(&MockBlockId::MainBranch(best).hash())
1035			.await
1036			.unwrap()
1037			.unwrap();
1038		assert_eq!(block.block_number(), best, "the latest block is returned for its hash");
1039
1040		let block = provider
1041			.block_by_hash(&MockBlockId::MainBranch(finalized).hash())
1042			.await
1043			.unwrap()
1044			.unwrap();
1045		assert_eq!(
1046			block.block_number(),
1047			finalized,
1048			"the latest finalized block is returned for its hash"
1049		);
1050
1051		let block = provider
1052			.block_by_hash(&MockBlockId::SideBranch(best).hash())
1053			.await
1054			.unwrap()
1055			.unwrap();
1056		assert_eq!(
1057			block.block_hash(),
1058			MockBlockId::SideBranch(best).hash(),
1059			"an uncached block is fetched from the chain"
1060		);
1061		assert_eq!(block.block_number(), best, "the fetched block's number comes from its header");
1062
1063		assert!(
1064			provider.block_by_hash(&H256::zero()).await.unwrap().is_none(),
1065			"no block exists for an unknown hash"
1066		);
1067
1068		assert_eq!(
1069			chain_heads.block_hash_lookup_count.load(Ordering::SeqCst),
1070			0,
1071			"fetching by hash never asks the chain for a block hash"
1072		);
1073	}
1074}