referrerpolicy=no-referrer-when-downgrade

pallet_revive_eth_rpc/
receipt_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.
17use crate::{
18	client::{SubstrateBlock, SubstrateBlockNumber},
19	Address, AddressOrAddresses, BlockInfoProvider, BlockNumberOrTag, BlockTag, Bytes, ClientError,
20	FilterTopic, ReceiptExtractor, SubxtBlockInfoProvider, LOG_TARGET,
21};
22use pallet_revive::evm::{Filter, Log, ReceiptInfo, TransactionSigned};
23use sp_core::{H256, U256};
24use sqlx::{query, QueryBuilder, Row, Sqlite, SqlitePool};
25use std::{
26	collections::{BTreeMap, HashMap},
27	sync::Arc,
28};
29use tokio::sync::Mutex;
30
31/// ReceiptProvider stores transaction receipts and logs in a SQLite database.
32#[derive(Clone)]
33pub struct ReceiptProvider<B: BlockInfoProvider = SubxtBlockInfoProvider> {
34	/// The database pool.
35	pool: SqlitePool,
36	/// The block provider used to fetch blocks, and reconstruct receipts.
37	block_provider: B,
38	/// A means to extract receipts from extrinsics.
39	receipt_extractor: ReceiptExtractor,
40	/// When `Some`, old blocks will be pruned.
41	keep_latest_n_blocks: Option<usize>,
42	/// A Map of the latest block numbers to block hashes.
43	block_number_to_hash: Arc<Mutex<BTreeMap<SubstrateBlockNumber, H256>>>,
44}
45
46/// Provides information about a block,
47/// This is an abstratction on top of [`SubstrateBlock`] that can't be mocked in tests.
48/// Can be removed once <https://github.com/paritytech/subxt/issues/1883> is fixed.
49pub trait BlockInfo {
50	/// Returns the block hash.
51	fn hash(&self) -> H256;
52	/// Returns the block number.
53	fn number(&self) -> SubstrateBlockNumber;
54}
55
56impl BlockInfo for SubstrateBlock {
57	fn hash(&self) -> H256 {
58		SubstrateBlock::hash(self)
59	}
60	fn number(&self) -> SubstrateBlockNumber {
61		SubstrateBlock::number(self)
62	}
63}
64
65impl<B: BlockInfoProvider> ReceiptProvider<B> {
66	/// Create a new `ReceiptProvider` with the given database URL and block provider.
67	pub async fn new(
68		pool: SqlitePool,
69		block_provider: B,
70		receipt_extractor: ReceiptExtractor,
71		keep_latest_n_blocks: Option<usize>,
72	) -> Result<Self, sqlx::Error> {
73		sqlx::migrate!().run(&pool).await?;
74		Ok(Self {
75			pool,
76			block_provider,
77			receipt_extractor,
78			keep_latest_n_blocks,
79			block_number_to_hash: Default::default(),
80		})
81	}
82
83	async fn fetch_row(&self, transaction_hash: &H256) -> Option<(H256, usize)> {
84		let transaction_hash = transaction_hash.as_ref();
85		let result = query!(
86			r#"
87			SELECT block_hash, transaction_index
88			FROM transaction_hashes
89			WHERE transaction_hash = $1
90			"#,
91			transaction_hash
92		)
93		.fetch_optional(&self.pool)
94		.await
95		.ok()??;
96
97		let block_hash = H256::from_slice(&result.block_hash[..]);
98		let transaction_index = result.transaction_index.try_into().ok()?;
99		Some((block_hash, transaction_index))
100	}
101
102	/// Deletes older records from the database.
103	pub async fn remove(&self, block_hashes: &[H256]) -> Result<(), ClientError> {
104		if block_hashes.is_empty() {
105			return Ok(());
106		}
107		log::debug!(target: LOG_TARGET, "Removing block hashes: {block_hashes:?}");
108
109		let placeholders = vec!["?"; block_hashes.len()].join(", ");
110		let sql = format!("DELETE FROM transaction_hashes WHERE block_hash in ({placeholders})");
111		let mut delete_tx_query = sqlx::query(&sql);
112
113		let sql = format!("DELETE FROM logs WHERE block_hash in ({placeholders})");
114		let mut delete_logs_query = sqlx::query(&sql);
115
116		for block_hash in block_hashes {
117			delete_tx_query = delete_tx_query.bind(block_hash.as_ref());
118			delete_logs_query = delete_logs_query.bind(block_hash.as_ref());
119		}
120
121		let delete_transaction_hashes = delete_tx_query.execute(&self.pool);
122		let delete_logs = delete_logs_query.execute(&self.pool);
123		tokio::try_join!(delete_transaction_hashes, delete_logs)?;
124		Ok(())
125	}
126
127	/// Check if the block is before the earliest block.
128	pub fn is_before_earliest_block(&self, at: &BlockNumberOrTag) -> bool {
129		match at {
130			BlockNumberOrTag::U256(block_number) =>
131				self.receipt_extractor.is_before_earliest_block(block_number.as_u32()),
132			BlockNumberOrTag::BlockTag(_) => false,
133		}
134	}
135
136	/// Fetch receipts from the given block.
137	pub async fn receipts_from_block(
138		&self,
139		block: &SubstrateBlock,
140	) -> Result<Vec<(TransactionSigned, ReceiptInfo)>, ClientError> {
141		self.receipt_extractor.extract_from_block(block).await
142	}
143
144	/// Extract and insert receipts from the given block.
145	pub async fn insert_block_receipts(
146		&self,
147		block: &SubstrateBlock,
148	) -> Result<Vec<(TransactionSigned, ReceiptInfo)>, ClientError> {
149		let receipts = self.receipts_from_block(block).await?;
150		self.insert(block, &receipts).await?;
151		Ok(receipts)
152	}
153
154	/// Insert receipts into the provider.
155	///
156	/// Note: Can be merged into `insert_block_receipts` once <https://github.com/paritytech/subxt/issues/1883> is fixed and subxt let
157	/// us create Mock `SubstrateBlock`
158	async fn insert(
159		&self,
160		block: &impl BlockInfo,
161		receipts: &[(TransactionSigned, ReceiptInfo)],
162	) -> Result<(), ClientError> {
163		if receipts.is_empty() {
164			return Ok(());
165		}
166
167		let block_hash = block.hash();
168		let block_hash_ref = block_hash.as_ref();
169		let block_number = block.number() as i64;
170
171		let result = sqlx::query!(
172			r#"SELECT EXISTS(SELECT 1 FROM transaction_hashes WHERE block_hash = $1) AS "exists!: bool""#,
173			block_hash_ref
174		)
175		.fetch_one(&self.pool)
176		.await?;
177
178		if result.exists {
179			return Ok(());
180		}
181
182		// Keep track of the latest block hashes, so we can prune older blocks.
183		if let Some(keep_latest_n_blocks) = self.keep_latest_n_blocks {
184			let latest = block.number();
185			let mut block_number_to_hash = self.block_number_to_hash.lock().await;
186
187			let oldest_block = latest.saturating_sub(keep_latest_n_blocks as _);
188			let mut to_remove = block_number_to_hash
189				.iter()
190				.take_while(|(n, _)| **n <= oldest_block)
191				.map(|(_, hash)| *hash)
192				.collect::<Vec<_>>();
193
194			block_number_to_hash.retain(|&n, _| n > oldest_block);
195			match block_number_to_hash.insert(block.number(), block_hash) {
196				Some(old_hash) if old_hash != block_hash => {
197					to_remove.push(old_hash);
198				},
199				_ => {},
200			}
201
202			log::trace!(target: LOG_TARGET, "Pruning old blocks: {to_remove:?}");
203			self.remove(&to_remove).await?;
204		}
205
206		for (_, receipt) in receipts {
207			let transaction_hash: &[u8] = receipt.transaction_hash.as_ref();
208			let transaction_index = receipt.transaction_index.as_u32() as i32;
209
210			query!(
211				r#"
212				INSERT OR REPLACE INTO transaction_hashes (transaction_hash, block_hash, transaction_index)
213				VALUES ($1, $2, $3)
214				"#,
215				transaction_hash,
216				block_hash_ref,
217				transaction_index
218			)
219			.execute(&self.pool)
220			.await?;
221
222			for log in &receipt.logs {
223				let log_index = log.log_index.as_u32() as i32;
224				let address: &[u8] = log.address.as_ref();
225
226				let topic_0 = log.topics.first().as_ref().map(|v| &v[..]);
227				let topic_1 = log.topics.get(1).as_ref().map(|v| &v[..]);
228				let topic_2 = log.topics.get(2).as_ref().map(|v| &v[..]);
229				let topic_3 = log.topics.get(3).as_ref().map(|v| &v[..]);
230				let data = log.data.as_ref().map(|v| &v.0[..]);
231
232				query!(
233					r#"
234					INSERT OR REPLACE INTO logs(
235						block_hash,
236						transaction_index,
237						log_index,
238						address,
239						block_number,
240						transaction_hash,
241						topic_0, topic_1, topic_2, topic_3,
242						data)
243					VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
244					"#,
245					block_hash_ref,
246					transaction_index,
247					log_index,
248					address,
249					block_number,
250					transaction_hash,
251					topic_0,
252					topic_1,
253					topic_2,
254					topic_3,
255					data
256				)
257				.execute(&self.pool)
258				.await?;
259			}
260		}
261		Ok(())
262	}
263
264	/// Get logs that match the given filter.
265	pub async fn logs(&self, filter: Option<Filter>) -> anyhow::Result<Vec<Log>> {
266		let mut qb = QueryBuilder::<Sqlite>::new("SELECT logs.* FROM logs WHERE 1=1");
267		let filter = filter.unwrap_or_default();
268
269		let latest_block = U256::from(self.block_provider.latest_block_number().await);
270
271		let as_block_number = |block_param| match block_param {
272			None => Ok(None),
273			Some(BlockNumberOrTag::U256(v)) => Ok(Some(v)),
274			Some(BlockNumberOrTag::BlockTag(BlockTag::Latest)) => Ok(Some(latest_block)),
275			Some(BlockNumberOrTag::BlockTag(tag)) => anyhow::bail!("Unsupported tag: {tag:?}"),
276		};
277
278		let from_block = as_block_number(filter.from_block)?;
279		let to_block = as_block_number(filter.to_block)?;
280
281		match (from_block, to_block, filter.block_hash) {
282			(Some(_), _, Some(_)) | (_, Some(_), Some(_)) => {
283				anyhow::bail!("block number and block hash cannot be used together");
284			},
285
286			(Some(block), _, _) | (_, Some(block), _) if block > latest_block => {
287				anyhow::bail!("block number exceeds latest block");
288			},
289			(Some(from_block), Some(to_block), None) if from_block > to_block => {
290				anyhow::bail!("invalid block range params");
291			},
292			(Some(from_block), Some(to_block), None) if from_block == to_block => {
293				qb.push(" AND block_number = ").push_bind(from_block.as_u64() as i64);
294			},
295			(Some(from_block), Some(to_block), None) => {
296				qb.push(" AND block_number BETWEEN ")
297					.push_bind(from_block.as_u64() as i64)
298					.push(" AND ")
299					.push_bind(to_block.as_u64() as i64);
300			},
301			(Some(from_block), None, None) => {
302				qb.push(" AND block_number >= ").push_bind(from_block.as_u64() as i64);
303			},
304			(None, Some(to_block), None) => {
305				qb.push(" AND block_number <= ").push_bind(to_block.as_u64() as i64);
306			},
307			(None, None, Some(hash)) => {
308				qb.push(" AND block_hash = ").push_bind(hash.0.to_vec());
309			},
310			(None, None, None) => {
311				qb.push(" AND block_number = ").push_bind(latest_block.as_u64() as i64);
312			},
313		}
314
315		if let Some(addresses) = filter.address {
316			match addresses {
317				AddressOrAddresses::Address(addr) => {
318					qb.push(" AND address = ").push_bind(addr.0.to_vec());
319				},
320				AddressOrAddresses::Addresses(addrs) => {
321					qb.push(" AND address IN (");
322					let mut separated = qb.separated(", ");
323					for addr in addrs {
324						separated.push_bind(addr.0.to_vec());
325					}
326					separated.push_unseparated(")");
327				},
328			}
329		}
330
331		if let Some(topics) = filter.topics {
332			if topics.len() > 4 {
333				return Err(anyhow::anyhow!("exceed max topics"));
334			}
335
336			for (i, topic) in topics.into_iter().enumerate() {
337				match topic {
338					FilterTopic::Single(hash) => {
339						qb.push(format_args!(" AND topic_{i} = ")).push_bind(hash.0.to_vec());
340					},
341					FilterTopic::Multiple(hashes) => {
342						qb.push(format_args!(" AND topic_{i} IN ("));
343						let mut separated = qb.separated(", ");
344						for hash in hashes {
345							separated.push_bind(hash.0.to_vec());
346						}
347						separated.push_unseparated(")");
348					},
349				}
350			}
351		}
352
353		qb.push(" LIMIT 10000");
354
355		let logs = qb
356			.build()
357			.try_map(|row| {
358				let block_hash: Vec<u8> = row.try_get("block_hash")?;
359				let transaction_index: i64 = row.try_get("transaction_index")?;
360				let log_index: i64 = row.try_get("log_index")?;
361				let address: Vec<u8> = row.try_get("address")?;
362				let block_number: i64 = row.try_get("block_number")?;
363				let transaction_hash: Vec<u8> = row.try_get("transaction_hash")?;
364				let topic_0: Option<Vec<u8>> = row.try_get("topic_0")?;
365				let topic_1: Option<Vec<u8>> = row.try_get("topic_1")?;
366				let topic_2: Option<Vec<u8>> = row.try_get("topic_2")?;
367				let topic_3: Option<Vec<u8>> = row.try_get("topic_3")?;
368				let data: Option<Vec<u8>> = row.try_get("data")?;
369
370				let topics = [topic_0, topic_1, topic_2, topic_3]
371					.iter()
372					.filter_map(|t| t.as_ref().map(|t| H256::from_slice(t)))
373					.collect::<Vec<_>>();
374
375				Ok(Log {
376					address: Address::from_slice(&address),
377					block_hash: H256::from_slice(&block_hash),
378					block_number: U256::from(block_number as u64),
379					data: data.map(Bytes::from),
380					log_index: U256::from(log_index as u64),
381					topics,
382					transaction_hash: H256::from_slice(&transaction_hash),
383					transaction_index: U256::from(transaction_index as u64),
384					removed: false,
385				})
386			})
387			.fetch_all(&self.pool)
388			.await?;
389
390		Ok(logs)
391	}
392
393	/// Get the number of receipts per block.
394	pub async fn receipts_count_per_block(&self, block_hash: &H256) -> Option<usize> {
395		let block_hash = block_hash.as_ref();
396		let row = query!(
397			r#"
398            SELECT COUNT(*) as count
399            FROM transaction_hashes
400            WHERE block_hash = $1
401            "#,
402			block_hash
403		)
404		.fetch_one(&self.pool)
405		.await
406		.ok()?;
407
408		let count = row.count as usize;
409		Some(count)
410	}
411
412	/// Return all transaction hashes for the given block hash.
413	pub async fn block_transaction_hashes(
414		&self,
415		block_hash: &H256,
416	) -> Option<HashMap<usize, H256>> {
417		let block_hash = block_hash.as_ref();
418		let rows = query!(
419			r#"
420		      SELECT transaction_index, transaction_hash
421		      FROM transaction_hashes
422		      WHERE block_hash = $1
423		      "#,
424			block_hash
425		)
426		.map(|row| {
427			let transaction_index = row.transaction_index as usize;
428			let transaction_hash = H256::from_slice(&row.transaction_hash);
429			(transaction_index, transaction_hash)
430		})
431		.fetch_all(&self.pool)
432		.await
433		.ok()?;
434
435		Some(rows.into_iter().collect())
436	}
437
438	/// Get the receipt for the given block hash and transaction index.
439	pub async fn receipt_by_block_hash_and_index(
440		&self,
441		block_hash: &H256,
442		transaction_index: usize,
443	) -> Option<ReceiptInfo> {
444		let block = self.block_provider.block_by_hash(block_hash).await.ok()??;
445		let (_, receipt) = self
446			.receipt_extractor
447			.extract_from_transaction(&block, transaction_index)
448			.await
449			.ok()?;
450		Some(receipt)
451	}
452
453	/// Get the receipt for the given transaction hash.
454	pub async fn receipt_by_hash(&self, transaction_hash: &H256) -> Option<ReceiptInfo> {
455		let (block_hash, transaction_index) = self.fetch_row(transaction_hash).await?;
456
457		let block = self.block_provider.block_by_hash(&block_hash).await.ok()??;
458		let (_, receipt) = self
459			.receipt_extractor
460			.extract_from_transaction(&block, transaction_index)
461			.await
462			.ok()?;
463		Some(receipt)
464	}
465
466	/// Get the signed transaction for the given transaction hash.
467	pub async fn signed_tx_by_hash(&self, transaction_hash: &H256) -> Option<TransactionSigned> {
468		let transaction_hash = transaction_hash.as_ref();
469		let result = query!(
470			r#"
471			SELECT block_hash, transaction_index
472			FROM transaction_hashes
473			WHERE transaction_hash = $1
474			"#,
475			transaction_hash
476		)
477		.fetch_optional(&self.pool)
478		.await
479		.ok()??;
480
481		let block_hash = H256::from_slice(&result.block_hash[..]);
482		let transaction_index = result.transaction_index.try_into().ok()?;
483
484		let block = self.block_provider.block_by_hash(&block_hash).await.ok()??;
485		let (signed_tx, _) = self
486			.receipt_extractor
487			.extract_from_transaction(&block, transaction_index)
488			.await
489			.ok()?;
490		Some(signed_tx)
491	}
492}
493
494#[cfg(test)]
495mod tests {
496	use super::*;
497	use crate::test::{MockBlockInfo, MockBlockInfoProvider};
498	use pallet_revive::evm::{ReceiptInfo, TransactionSigned};
499	use pretty_assertions::assert_eq;
500	use sp_core::{H160, H256};
501	use sqlx::SqlitePool;
502
503	async fn count(pool: &SqlitePool, table: &str, block_hash: Option<H256>) -> usize {
504		let count: i64 = match block_hash {
505			None =>
506				sqlx::query_scalar(&format!("SELECT COUNT(*) FROM {table}"))
507					.fetch_one(pool)
508					.await,
509			Some(hash) =>
510				sqlx::query_scalar(&format!("SELECT COUNT(*) FROM {table} WHERE block_hash = ?"))
511					.bind(hash.as_ref())
512					.fetch_one(pool)
513					.await,
514		}
515		.unwrap();
516
517		count as _
518	}
519
520	async fn setup_sqlite_provider(pool: SqlitePool) -> ReceiptProvider<MockBlockInfoProvider> {
521		ReceiptProvider {
522			pool,
523			block_provider: MockBlockInfoProvider {},
524			receipt_extractor: ReceiptExtractor::new_mock(),
525			keep_latest_n_blocks: Some(10),
526			block_number_to_hash: Default::default(),
527		}
528	}
529
530	#[sqlx::test]
531	async fn test_insert_remove(pool: SqlitePool) -> anyhow::Result<()> {
532		let provider = setup_sqlite_provider(pool).await;
533		let block = MockBlockInfo { hash: H256::default(), number: 0 };
534		let receipts = vec![(
535			TransactionSigned::default(),
536			ReceiptInfo {
537				logs: vec![Log { block_hash: block.hash, ..Default::default() }],
538				..Default::default()
539			},
540		)];
541
542		provider.insert(&block, &receipts).await?;
543		let row = provider.fetch_row(&receipts[0].1.transaction_hash).await;
544		assert_eq!(row, Some((block.hash, 0)));
545
546		provider.remove(&[block.hash()]).await?;
547		assert_eq!(count(&provider.pool, "transaction_hashes", Some(block.hash())).await, 0);
548		assert_eq!(count(&provider.pool, "logs", Some(block.hash())).await, 0);
549		Ok(())
550	}
551
552	#[sqlx::test]
553	async fn test_prune(pool: SqlitePool) -> anyhow::Result<()> {
554		let provider = setup_sqlite_provider(pool).await;
555		let n = provider.keep_latest_n_blocks.unwrap();
556
557		for i in 0..2 * n {
558			let block = MockBlockInfo { hash: H256::from([i as u8; 32]), number: i as _ };
559			let transaction_hash = H256::from([i as u8; 32]);
560			let receipts = vec![(
561				TransactionSigned::default(),
562				ReceiptInfo {
563					transaction_hash,
564					logs: vec![Log {
565						block_hash: block.hash,
566						transaction_hash,
567						..Default::default()
568					}],
569					..Default::default()
570				},
571			)];
572			provider.insert(&block, &receipts).await?;
573		}
574		assert_eq!(count(&provider.pool, "transaction_hashes", None).await, n);
575		assert_eq!(count(&provider.pool, "logs", None).await, n);
576		assert_eq!(provider.block_number_to_hash.lock().await.len(), n);
577
578		return Ok(());
579	}
580
581	#[sqlx::test]
582	async fn test_fork(pool: SqlitePool) -> anyhow::Result<()> {
583		let provider = setup_sqlite_provider(pool).await;
584
585		for i in [1u8, 2u8] {
586			let block = MockBlockInfo { hash: H256::from([i; 32]), number: 1 };
587			let transaction_hash = H256::from([i; 32]);
588			let receipts = vec![(
589				TransactionSigned::default(),
590				ReceiptInfo {
591					transaction_hash,
592					logs: vec![Log {
593						block_hash: block.hash,
594						transaction_hash,
595						..Default::default()
596					}],
597					..Default::default()
598				},
599			)];
600			provider.insert(&block, &receipts).await?;
601		}
602		assert_eq!(count(&provider.pool, "transaction_hashes", None).await, 1);
603		assert_eq!(count(&provider.pool, "logs", None).await, 1);
604		assert_eq!(
605			provider.block_number_to_hash.lock().await.clone(),
606			[(1, H256::from([2u8; 32]))].into(),
607			"New receipt for block #1 should replace the old one"
608		);
609
610		return Ok(());
611	}
612
613	#[sqlx::test]
614	async fn test_receipts_count_per_block(pool: SqlitePool) -> anyhow::Result<()> {
615		let provider = setup_sqlite_provider(pool).await;
616		let block = MockBlockInfo { hash: H256::default(), number: 0 };
617		let receipts = vec![
618			(
619				TransactionSigned::default(),
620				ReceiptInfo { transaction_hash: H256::from([0u8; 32]), ..Default::default() },
621			),
622			(
623				TransactionSigned::default(),
624				ReceiptInfo { transaction_hash: H256::from([1u8; 32]), ..Default::default() },
625			),
626		];
627
628		provider.insert(&block, &receipts).await?;
629		let count = provider.receipts_count_per_block(&block.hash).await;
630		assert_eq!(count, Some(2));
631		Ok(())
632	}
633
634	#[sqlx::test]
635	async fn test_query_logs(pool: SqlitePool) -> anyhow::Result<()> {
636		let provider = setup_sqlite_provider(pool).await;
637		let block1 = MockBlockInfo { hash: H256::from([1u8; 32]), number: 1 };
638		let block2 = MockBlockInfo { hash: H256::from([2u8; 32]), number: 2 };
639		let log1 = Log {
640			block_hash: block1.hash,
641			block_number: block1.number.into(),
642			address: H160::from([1u8; 20]),
643			topics: vec![H256::from([1u8; 32]), H256::from([2u8; 32])],
644			data: Some(vec![0u8; 32].into()),
645			transaction_hash: H256::default(),
646			transaction_index: U256::from(1),
647			log_index: U256::from(1),
648			..Default::default()
649		};
650		let log2 = Log {
651			block_hash: block2.hash,
652			block_number: block2.number.into(),
653			address: H160::from([2u8; 20]),
654			topics: vec![H256::from([2u8; 32]), H256::from([3u8; 32])],
655			transaction_hash: H256::from([1u8; 32]),
656			transaction_index: U256::from(2),
657			log_index: U256::from(1),
658			..Default::default()
659		};
660
661		provider
662			.insert(
663				&block1,
664				&vec![(
665					TransactionSigned::default(),
666					ReceiptInfo {
667						logs: vec![log1.clone()],
668						transaction_hash: log1.transaction_hash,
669						transaction_index: log1.transaction_index,
670						..Default::default()
671					},
672				)],
673			)
674			.await?;
675		provider
676			.insert(
677				&block2,
678				&vec![(
679					TransactionSigned::default(),
680					ReceiptInfo {
681						logs: vec![log2.clone()],
682						transaction_hash: log2.transaction_hash,
683						transaction_index: log2.transaction_index,
684						..Default::default()
685					},
686				)],
687			)
688			.await?;
689
690		// Empty filter
691		let logs = provider.logs(None).await?;
692		assert_eq!(logs, vec![log2.clone()]);
693
694		// from_block filter
695		let logs = provider
696			.logs(Some(Filter { from_block: Some(log2.block_number.into()), ..Default::default() }))
697			.await?;
698		assert_eq!(logs, vec![log2.clone()]);
699
700		// from_block filter (using latest block)
701		let logs = provider
702			.logs(Some(Filter { from_block: Some(BlockTag::Latest.into()), ..Default::default() }))
703			.await?;
704		assert_eq!(logs, vec![log2.clone()]);
705
706		// to_block filter
707		let logs = provider
708			.logs(Some(Filter { to_block: Some(log1.block_number.into()), ..Default::default() }))
709			.await?;
710		assert_eq!(logs, vec![log1.clone()]);
711
712		// block_hash filter
713		let logs = provider
714			.logs(Some(Filter { block_hash: Some(log1.block_hash), ..Default::default() }))
715			.await?;
716		assert_eq!(logs, vec![log1.clone()]);
717
718		// single address
719		let logs = provider
720			.logs(Some(Filter {
721				from_block: Some(U256::from(0).into()),
722				address: Some(log1.address.into()),
723				..Default::default()
724			}))
725			.await?;
726		assert_eq!(logs, vec![log1.clone()]);
727
728		// multiple addresses
729		let logs = provider
730			.logs(Some(Filter {
731				from_block: Some(U256::from(0).into()),
732				address: Some(vec![log1.address, log2.address].into()),
733				..Default::default()
734			}))
735			.await?;
736		assert_eq!(logs, vec![log1.clone(), log2.clone()]);
737
738		// single topic
739		let logs = provider
740			.logs(Some(Filter {
741				from_block: Some(U256::from(0).into()),
742				topics: Some(vec![FilterTopic::Single(log1.topics[0])]),
743				..Default::default()
744			}))
745			.await?;
746		assert_eq!(logs, vec![log1.clone()]);
747
748		// multiple topic
749		let logs = provider
750			.logs(Some(Filter {
751				from_block: Some(U256::from(0).into()),
752				topics: Some(vec![
753					FilterTopic::Single(log1.topics[0]),
754					FilterTopic::Single(log1.topics[1]),
755				]),
756				..Default::default()
757			}))
758			.await?;
759		assert_eq!(logs, vec![log1.clone()]);
760
761		// multiple topic for topic_0
762		let logs = provider
763			.logs(Some(Filter {
764				from_block: Some(U256::from(0).into()),
765				topics: Some(vec![FilterTopic::Multiple(vec![log1.topics[0], log2.topics[0]])]),
766				..Default::default()
767			}))
768			.await?;
769		assert_eq!(logs, vec![log1.clone(), log2.clone()]);
770
771		// Altogether
772		let logs = provider
773			.logs(Some(Filter {
774				from_block: Some(log1.block_number.into()),
775				to_block: Some(log2.block_number.into()),
776				block_hash: None,
777				address: Some(vec![log1.address, log2.address].into()),
778				topics: Some(vec![FilterTopic::Multiple(vec![log1.topics[0], log2.topics[0]])]),
779			}))
780			.await?;
781		assert_eq!(logs, vec![log1.clone(), log2.clone()]);
782		Ok(())
783	}
784}