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