fc_cli/frontier_db_cmd/
mapping_db.rs1use std::sync::Arc;
20
21use ethereum_types::H256;
22use serde::Deserialize;
23use sp_api::ProvideRuntimeApi;
25use sp_blockchain::HeaderBackend;
26use sp_runtime::traits::Block as BlockT;
27use 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 (
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 (Column::Block, MappingKey::EthBlockOrTransactionHash(ethereum_block_hash)) => {
116 let value = self.backend.mapping().block_hash(ethereum_block_hash)?;
117 println!("{value:?}");
118 }
119 (
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 (
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> {}