fc_cli/frontier_db_cmd/
mapping_db.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use std::sync::Arc;
20
21use ethereum_types::H256;
22use serde::Deserialize;
23// Substrate
24use sp_api::ProvideRuntimeApi;
25use sp_blockchain::HeaderBackend;
26use sp_runtime::traits::Block as BlockT;
27// Frontier
28use fp_rpc::EthereumRuntimeRPCApi;
29
30use super::{utils::FrontierDbMessage, Column, FrontierDbCmd, Operation};
31
32#[derive(Debug, Deserialize)]
33#[serde(untagged)]
34pub enum MappingValue<H> {
35	SubstrateBlockHash(H),
36}
37
38#[derive(Clone, Copy, Debug)]
39pub enum MappingKey {
40	EthBlockOrTransactionHash(H256),
41}
42
43pub struct MappingDb<'a, B, C> {
44	cmd: &'a FrontierDbCmd,
45	client: Arc<C>,
46	backend: Arc<fc_db::kv::Backend<B, C>>,
47}
48
49impl<'a, B, C> MappingDb<'a, B, C>
50where
51	B: BlockT,
52	C: HeaderBackend<B> + ProvideRuntimeApi<B>,
53	C::Api: EthereumRuntimeRPCApi<B>,
54{
55	pub fn new(
56		cmd: &'a FrontierDbCmd,
57		client: Arc<C>,
58		backend: Arc<fc_db::kv::Backend<B, C>>,
59	) -> Self {
60		Self {
61			cmd,
62			client,
63			backend,
64		}
65	}
66
67	pub fn query(
68		&self,
69		column: &Column,
70		key: &MappingKey,
71		value: &Option<MappingValue<B::Hash>>,
72	) -> sc_cli::Result<()> {
73		match self.cmd.operation {
74			Operation::Create => match (key, value) {
75				// Insert a mapping commitment using the state at the requested block.
76				(
77					MappingKey::EthBlockOrTransactionHash(ethereum_block_hash),
78					Some(MappingValue::SubstrateBlockHash(substrate_block_hash)),
79				) => {
80					if self
81						.backend
82						.mapping()
83						.block_hash(ethereum_block_hash)?
84						.is_none()
85					{
86						let existing_transaction_hashes: Vec<H256> = if let Some(statuses) = self
87							.client
88							.runtime_api()
89							.current_transaction_statuses(*substrate_block_hash)
90							.map_err(|e| format!("{e:?}"))?
91						{
92							statuses
93								.iter()
94								.map(|t| t.transaction_hash)
95								.collect::<Vec<H256>>()
96						} else {
97							vec![]
98						};
99
100						let commitment = fc_db::kv::MappingCommitment::<B> {
101							block_hash: *substrate_block_hash,
102							ethereum_block_hash: *ethereum_block_hash,
103							ethereum_transaction_hashes: existing_transaction_hashes,
104						};
105
106						self.backend.mapping().write_hashes(commitment)?;
107					} else {
108						return Err(self.key_not_empty_error(key));
109					}
110				}
111				_ => return Err(self.key_value_error(key, value)),
112			},
113			Operation::Read => match (column, key) {
114				// Given ethereum block hash, get substrate block hash.
115				(Column::Block, MappingKey::EthBlockOrTransactionHash(ethereum_block_hash)) => {
116					let value = self.backend.mapping().block_hash(ethereum_block_hash)?;
117					println!("{value:?}");
118				}
119				// Given ethereum transaction hash, get transaction metadata.
120				(
121					Column::Transaction,
122					MappingKey::EthBlockOrTransactionHash(ethereum_transaction_hash),
123				) => {
124					let value = self
125						.backend
126						.mapping()
127						.transaction_metadata(ethereum_transaction_hash)?;
128					println!("{value:?}");
129				}
130				_ => return Err(self.key_column_error(key, value)),
131			},
132			Operation::Update => match (key, value) {
133				// Update a mapping commitment using the state at the requested block.
134				(
135					MappingKey::EthBlockOrTransactionHash(ethereum_block_hash),
136					Some(MappingValue::SubstrateBlockHash(substrate_block_hash)),
137				) => {
138					if self
139						.backend
140						.mapping()
141						.block_hash(ethereum_block_hash)?
142						.is_some()
143					{
144						let existing_transaction_hashes: Vec<H256> = if let Some(statuses) = self
145							.client
146							.runtime_api()
147							.current_transaction_statuses(*substrate_block_hash)
148							.map_err(|e| format!("{e:?}"))?
149						{
150							statuses
151								.iter()
152								.map(|t| t.transaction_hash)
153								.collect::<Vec<H256>>()
154						} else {
155							vec![]
156						};
157
158						let commitment = fc_db::kv::MappingCommitment::<B> {
159							block_hash: *substrate_block_hash,
160							ethereum_block_hash: *ethereum_block_hash,
161							ethereum_transaction_hashes: existing_transaction_hashes,
162						};
163
164						self.backend.mapping().write_hashes(commitment)?;
165					}
166				}
167				_ => return Err(self.key_value_error(key, value)),
168			},
169			Operation::Delete => {
170				return Err("Delete operation is not supported for non-static keys"
171					.to_string()
172					.into())
173			}
174		}
175		Ok(())
176	}
177}
178
179impl<B: BlockT, C: HeaderBackend<B>> FrontierDbMessage for MappingDb<'_, B, C> {}