1
// Copyright 2025 Moonbeam foundation
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Lazy loading implementation for Frontier backend.
18
//!
19
//! This module provides a wrapper around the standard Frontier backend that enables
20
//! lazy loading functionality. It intercepts Ethereum-specific queries and routes them
21
//! through the RPC client to fetch data on-demand from a remote node.
22

            
23
use fc_api::{LogIndexerBackend, TransactionMetadata};
24
use serde::de::DeserializeOwned;
25
use sp_core::H256;
26
use sp_runtime::traits::{Block as BlockT, Header};
27
use std::sync::Arc;
28

            
29
/// A wrapper around the Frontier backend that supports lazy loading.
30
///
31
/// This backend intercepts Ethereum-specific queries (block hash lookups, transaction metadata)
32
/// and fetches the data from a remote RPC node when needed, while delegating other operations
33
/// to the underlying Frontier backend.
34
#[derive(Clone)]
35
pub struct LazyLoadingFrontierBackend<Block: BlockT> {
36
	pub(crate) rpc_client: Arc<super::rpc_client::RPC>,
37
	pub(crate) frontier_backend: Arc<dyn fc_api::Backend<Block> + Send + Sync>,
38
}
39

            
40
impl<Block: BlockT + DeserializeOwned> LazyLoadingFrontierBackend<Block>
41
where
42
	<Block::Header as Header>::Number: From<u32>,
43
{
44
	fn get_substrate_block_hash(
45
		&self,
46
		block_number: <Block::Header as Header>::Number,
47
	) -> Result<Option<Block::Hash>, String> {
48
		self.rpc_client
49
			.block_hash::<Block>(Some(block_number))
50
			.map_err(|e| format!("failed to get substrate block hash: {:?}", e))
51
	}
52
}
53

            
54
#[async_trait::async_trait]
55
impl<Block: BlockT + DeserializeOwned> fc_api::Backend<Block> for LazyLoadingFrontierBackend<Block>
56
where
57
	<Block::Header as Header>::Number: From<u32>,
58
{
59
	async fn block_hash(&self, eth_block_hash: &H256) -> Result<Option<Vec<Block::Hash>>, String> {
60
		let block = self
61
			.rpc_client
62
			.block_by_hash(eth_block_hash, false)
63
			.map_err(|e| format!("failed to get block by hash: {:?}", e))?;
64

            
65
		match block {
66
			Some(block) => {
67
				let block_number = block.header.number.as_u32().into();
68
				let substrate_block_hash = self.get_substrate_block_hash(block_number)?;
69
				Ok(substrate_block_hash.map(|h| vec![h]))
70
			}
71
			None => Ok(None),
72
		}
73
	}
74

            
75
	async fn transaction_metadata(
76
		&self,
77
		eth_transaction_hash: &H256,
78
	) -> Result<Vec<TransactionMetadata<Block>>, String> {
79
		let transaction = self
80
			.rpc_client
81
			.transaction_by_hash(eth_transaction_hash)
82
			.map_err(|e| format!("failed to get transaction by hash: {:?}", e))?;
83

            
84
		match transaction {
85
			Some(tx) => {
86
				let block_number = tx.block_number.unwrap_or_default().as_u32().into();
87
				let substrate_block_hash = self.get_substrate_block_hash(block_number)?;
88

            
89
				Ok(vec![TransactionMetadata::<Block> {
90
					ethereum_index: tx.transaction_index.unwrap_or_default().as_u32(),
91
					ethereum_block_hash: tx.block_hash.unwrap_or_default(),
92
					substrate_block_hash: substrate_block_hash.unwrap_or_default(),
93
				}])
94
			}
95
			None => Ok(vec![]),
96
		}
97
	}
98

            
99
	fn log_indexer(&self) -> &dyn LogIndexerBackend<Block> {
100
		self.frontier_backend.log_indexer()
101
	}
102

            
103
	async fn first_block_hash(&self) -> Result<Block::Hash, String> {
104
		self.frontier_backend.first_block_hash().await
105
	}
106

            
107
	async fn latest_block_hash(&self) -> Result<Block::Hash, String> {
108
		self.frontier_backend.latest_block_hash().await
109
	}
110
}