fc_cli/frontier_db_cmd/
meta_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::{
20	collections::HashMap,
21	str::{self, FromStr},
22	sync::Arc,
23};
24
25use ethereum_types::H256;
26use serde::Deserialize;
27use sp_blockchain::HeaderBackend;
28// Substrate
29use sp_runtime::traits::Block as BlockT;
30
31use super::{utils::FrontierDbMessage, FrontierDbCmd, Operation};
32
33#[derive(Debug, Deserialize)]
34#[serde(untagged)]
35pub enum MetaValue<H> {
36	Tips(Vec<H>),
37	Schema(HashMap<H256, fp_storage::EthereumStorageSchema>),
38}
39
40#[derive(Clone, Copy, Debug)]
41pub enum MetaKey {
42	Tips,
43	Schema,
44}
45
46impl FromStr for MetaKey {
47	type Err = sc_cli::Error;
48
49	// A convenience function to verify the user input is known.
50	fn from_str(input: &str) -> Result<MetaKey, Self::Err> {
51		let tips = str::from_utf8(fc_db::kv::static_keys::CURRENT_SYNCING_TIPS).unwrap();
52		let schema = str::from_utf8(fp_storage::PALLET_ETHEREUM_SCHEMA_CACHE).unwrap();
53		match input {
54			x if x == tips => Ok(MetaKey::Tips),
55			y if y == schema => Ok(MetaKey::Schema),
56			_ => Err(format!("`{input:?}` is not a meta column static key").into()),
57		}
58	}
59}
60
61pub struct MetaDb<'a, B, C> {
62	cmd: &'a FrontierDbCmd,
63	backend: Arc<fc_db::kv::Backend<B, C>>,
64}
65
66impl<'a, B: BlockT, C: HeaderBackend<B>> MetaDb<'a, B, C> {
67	pub fn new(cmd: &'a FrontierDbCmd, backend: Arc<fc_db::kv::Backend<B, C>>) -> Self {
68		Self { cmd, backend }
69	}
70
71	pub fn query(&self, key: &MetaKey, value: &Option<MetaValue<B::Hash>>) -> sc_cli::Result<()> {
72		match self.cmd.operation {
73			Operation::Create => match (key, value) {
74				// Insert data to the meta column, static tips key.
75				(MetaKey::Tips, Some(MetaValue::Tips(hashes))) => {
76					if self.backend.meta().current_syncing_tips()?.is_empty() {
77						self.backend
78							.meta()
79							.write_current_syncing_tips(hashes.clone())?;
80					} else {
81						return Err(self.key_not_empty_error(key));
82					}
83				}
84				// Insert data to the meta column, static schema cache key.
85				(MetaKey::Schema, Some(MetaValue::Schema(schema_map))) => {
86					if self.backend.meta().ethereum_schema()?.is_none() {
87						let data = schema_map
88							.iter()
89							.map(|(key, value)| (*value, *key))
90							.collect::<Vec<(fp_storage::EthereumStorageSchema, H256)>>();
91						self.backend.meta().write_ethereum_schema(data)?;
92					} else {
93						return Err(self.key_not_empty_error(key));
94					}
95				}
96				_ => return Err(self.key_value_error(key, value)),
97			},
98			Operation::Read => match key {
99				// Read meta column, static tips key.
100				MetaKey::Tips => {
101					let value = self.backend.meta().current_syncing_tips()?;
102					println!("{value:?}");
103				}
104				// Read meta column, static schema cache key.
105				MetaKey::Schema => {
106					let value = self.backend.meta().ethereum_schema()?;
107					println!("{value:?}");
108				}
109			},
110			Operation::Update => match (key, value) {
111				// Update the static tips key's value.
112				(MetaKey::Tips, Some(MetaValue::Tips(new_value))) => {
113					let value = self.backend.meta().current_syncing_tips()?;
114					self.confirmation_prompt(&self.cmd.operation, key, &value, new_value)?;
115					self.backend
116						.meta()
117						.write_current_syncing_tips(new_value.clone())?;
118				}
119				// Update the static schema cache key's value.
120				(MetaKey::Schema, Some(MetaValue::Schema(schema_map))) => {
121					let value = self.backend.meta().ethereum_schema()?;
122					let new_value = schema_map
123						.iter()
124						.map(|(key, value)| (*value, *key))
125						.collect::<Vec<(fp_storage::EthereumStorageSchema, H256)>>();
126					self.confirmation_prompt(
127						&self.cmd.operation,
128						key,
129						&value,
130						&Some(new_value.clone()),
131					)?;
132					self.backend.meta().write_ethereum_schema(new_value)?;
133				}
134				_ => return Err(self.key_value_error(key, value)),
135			},
136			Operation::Delete => match key {
137				// Deletes the static tips key's value.
138				MetaKey::Tips => {
139					let value = self.backend.meta().current_syncing_tips()?;
140					self.confirmation_prompt(&self.cmd.operation, key, &value, &vec![])?;
141					self.backend.meta().write_current_syncing_tips(vec![])?;
142				}
143				// Deletes the static schema cache key's value.
144				MetaKey::Schema => {
145					let value = self.backend.meta().ethereum_schema()?;
146					self.confirmation_prompt(&self.cmd.operation, key, &value, &Some(vec![]))?;
147					self.backend.meta().write_ethereum_schema(vec![])?;
148				}
149			},
150		}
151		Ok(())
152	}
153}
154
155impl<B: BlockT, C: HeaderBackend<B>> FrontierDbMessage for MetaDb<'_, B, C> {}