1use std::{fmt::Debug, path::PathBuf, str::FromStr};
6
7use anyhow::anyhow;
8use iota_storage::IndexStoreTables;
9use iota_types::{
10 Identifier, TypeTag,
11 base_types::{IotaAddress, ObjectID, TxSequenceNumber},
12 digests::TransactionDigest,
13};
14use move_core_types::language_storage::ModuleId;
15use serde::{Serialize, de::DeserializeOwned};
16use typed_store::{
17 rocks::{DBMap, MetricConf},
18 traits::Map,
19};
20
21use crate::get_db_entries;
22
23#[derive(Clone, Debug)]
24pub enum SearchRange<T: Serialize + Clone + Debug> {
25 ExclusiveLastKey(T),
26 Count(u64),
27}
28
29impl<T: Serialize + Clone + Debug + FromStr> FromStr for SearchRange<T>
30where
31 <T as std::str::FromStr>::Err: std::fmt::Debug,
32{
33 type Err = anyhow::Error;
34
35 fn from_str(s: &str) -> Result<Self, Self::Err> {
36 let last_key = T::from_str(s).map_err(|e| anyhow!("Failed to parse last_key: {:?}", e))?;
37 Ok(SearchRange::ExclusiveLastKey(last_key))
38 }
39}
40
41pub fn search_index(
44 db_path: PathBuf,
45 table_name: String,
46 start: String,
47 termination: SearchRange<String>,
48) -> Result<Vec<(String, String)>, anyhow::Error> {
49 let start = start.as_str();
50 println!("Opening db at {:?} ...", db_path);
51 let db_read_only_handle =
52 IndexStoreTables::get_read_only_handle(db_path, None, None, MetricConf::default());
53 match table_name.as_str() {
54 "transactions_from_addr" => {
55 get_db_entries!(
56 db_read_only_handle.transactions_from_addr,
57 from_addr_seq,
58 start,
59 termination
60 )
61 }
62 "transactions_to_addr" => {
63 get_db_entries!(
64 db_read_only_handle.transactions_to_addr,
65 from_addr_seq,
66 start,
67 termination
68 )
69 }
70 "transactions_by_input_object_id" => {
71 get_db_entries!(
72 db_read_only_handle.transactions_by_input_object_id,
73 from_id_seq,
74 start,
75 termination
76 )
77 }
78 "transactions_by_mutated_object_id" => {
79 get_db_entries!(
80 db_read_only_handle.transactions_by_mutated_object_id,
81 from_id_seq,
82 start,
83 termination
84 )
85 }
86 "transactions_by_move_function" => {
87 get_db_entries!(
88 db_read_only_handle.transactions_by_move_function,
89 from_id_module_function_txseq,
90 start,
91 termination
92 )
93 }
94 "transaction_order" => {
95 get_db_entries!(
96 db_read_only_handle.transaction_order,
97 u64::from_str,
98 start,
99 termination
100 )
101 }
102 "transactions_seq" => {
103 get_db_entries!(
104 db_read_only_handle.transactions_seq,
105 TransactionDigest::from_str,
106 start,
107 termination
108 )
109 }
110 "owner_index" => {
111 get_db_entries!(
112 db_read_only_handle.owner_index,
113 from_addr_oid,
114 start,
115 termination
116 )
117 }
118 "coin_index" => {
119 get_db_entries!(
120 db_read_only_handle.coin_index,
121 from_addr_str_oid,
122 start,
123 termination
124 )
125 }
126 "dynamic_field_index" => {
127 get_db_entries!(
128 db_read_only_handle.dynamic_field_index,
129 from_oid_oid,
130 start,
131 termination
132 )
133 }
134 "event_by_event_module" => {
135 get_db_entries!(
136 db_read_only_handle.event_by_event_module,
137 from_module_id_and_event_id,
138 start,
139 termination
140 )
141 }
142 "event_by_move_module" => {
143 get_db_entries!(
144 db_read_only_handle.event_by_move_module,
145 from_module_id_and_event_id,
146 start,
147 termination
148 )
149 }
150 "event_order" => {
151 get_db_entries!(
152 db_read_only_handle.event_order,
153 from_event_id,
154 start,
155 termination
156 )
157 }
158 "event_by_sender" => {
159 get_db_entries!(
160 db_read_only_handle.event_by_sender,
161 from_address_and_event_id,
162 start,
163 termination
164 )
165 }
166 _ => Err(anyhow!("Invalid or unsupported table: {}", table_name)),
167 }
168}
169
170#[macro_export]
171macro_rules! get_db_entries {
172 ($db_map:expr, $key_converter:expr, $start:expr, $term:expr) => {{
173 let key = $key_converter($start)?;
174 println!("Searching from key: {:?}", key);
175 let termination = match $term {
176 SearchRange::ExclusiveLastKey(last_key) => {
177 println!(
178 "Retrieving all keys up to (but not including) key: {:?}",
179 key
180 );
181 SearchRange::ExclusiveLastKey($key_converter(last_key.as_str())?)
182 }
183 SearchRange::Count(count) => {
184 println!("Retrieving up to {} keys", count);
185 SearchRange::Count(count)
186 }
187 };
188
189 $db_map.try_catch_up_with_primary().unwrap();
190 get_entries_to_str(&$db_map, key, termination)
191 }};
192}
193
194fn get_entries_to_str<K, V>(
195 db_map: &DBMap<K, V>,
196 start: K,
197 termination: SearchRange<K>,
198) -> Result<Vec<(String, String)>, anyhow::Error>
199where
200 K: Serialize + serde::de::DeserializeOwned + Clone + Debug,
201 V: serde::Serialize + DeserializeOwned + Clone + Debug,
202{
203 get_entries(db_map, start, termination).map(|entries| {
204 entries
205 .into_iter()
206 .map(|(k, v)| (format!("{:?}", k), format!("{:?}", v)))
207 .collect()
208 })
209}
210
211fn get_entries<K, V>(
212 db_map: &DBMap<K, V>,
213 start: K,
214 termination: SearchRange<K>,
215) -> Result<Vec<(K, V)>, anyhow::Error>
216where
217 K: Serialize + serde::de::DeserializeOwned + Clone + std::fmt::Debug,
218 V: serde::Serialize + DeserializeOwned + Clone,
219{
220 let mut entries = Vec::new();
221 match termination {
222 SearchRange::ExclusiveLastKey(exclusive_last_key) => {
223 let iter = db_map.safe_iter_with_bounds(Some(start), Some(exclusive_last_key));
224
225 for result in iter {
226 let (key, value) = result?;
227 entries.push((key.clone(), value.clone()));
228 }
229 }
230 SearchRange::Count(mut count) => {
231 let mut iter = db_map.safe_iter_with_bounds(Some(start), None);
232
233 while count > 0 {
234 if let Some(result) = iter.next() {
235 let (key, value) = result?;
236 entries.push((key.clone(), value.clone()));
237 } else {
238 break;
239 }
240 count -= 1;
241 }
242 }
243 }
244 Ok(entries)
245}
246
247fn from_addr_seq(s: &str) -> Result<(IotaAddress, TxSequenceNumber), anyhow::Error> {
248 let s = s.trim();
250 let tokens = s.split(',').collect::<Vec<&str>>();
251 if tokens.len() != 2 {
252 return Err(anyhow!("Invalid address, sequence number pair"));
253 }
254 let address = IotaAddress::from_str(tokens[0].trim())?;
255 let sequence_number = TxSequenceNumber::from_str(tokens[1].trim())?;
256
257 Ok((address, sequence_number))
258}
259
260fn from_id_seq(s: &str) -> Result<(ObjectID, TxSequenceNumber), anyhow::Error> {
261 let s = s.trim();
263 let tokens = s.split(',').collect::<Vec<&str>>();
264 if tokens.len() != 2 {
265 return Err(anyhow!("Invalid object id, sequence number pair"));
266 }
267 let oid = ObjectID::from_str(tokens[0].trim())?;
268 let sequence_number = TxSequenceNumber::from_str(tokens[1].trim())?;
269
270 Ok((oid, sequence_number))
271}
272
273fn from_id_module_function_txseq(
274 s: &str,
275) -> Result<(ObjectID, String, String, TxSequenceNumber), anyhow::Error> {
276 let s = s.trim();
278 let tokens = s.split(',').collect::<Vec<&str>>();
279 if tokens.len() != 4 {
280 return Err(anyhow!(
281 "Invalid object id, module name, function name, TX sequence number quad"
282 ));
283 }
284 let pid = ObjectID::from_str(tokens[0].trim())?;
285 let module: Identifier = Identifier::from_str(tokens[1].trim())?;
286 let func: Identifier = Identifier::from_str(tokens[2].trim())?;
287 let seq: TxSequenceNumber = TxSequenceNumber::from_str(tokens[3].trim())?;
288
289 Ok((pid, module.to_string(), func.to_string(), seq))
290}
291
292fn from_addr_oid(s: &str) -> Result<(IotaAddress, ObjectID), anyhow::Error> {
293 let s = s.trim();
295 let tokens = s.split(',').collect::<Vec<&str>>();
296 if tokens.len() != 2 {
297 return Err(anyhow!("Invalid address, object id pair"));
298 }
299 let addr = IotaAddress::from_str(tokens[0].trim())?;
300 let oid = ObjectID::from_str(tokens[1].trim())?;
301
302 Ok((addr, oid))
303}
304
305fn from_addr_str_oid(s: &str) -> Result<(IotaAddress, String, ObjectID), anyhow::Error> {
306 let s = s.trim();
308 let tokens = s.split(',').collect::<Vec<&str>>();
309 if tokens.len() != 3 {
310 return Err(anyhow!("Invalid addr, type tag object id triplet"));
311 }
312 let address = IotaAddress::from_str(tokens[0].trim())?;
313 let tag: TypeTag = TypeTag::from_str(tokens[1].trim())?;
314 let oid: ObjectID = ObjectID::from_str(tokens[2].trim())?;
315
316 Ok((address, tag.to_string(), oid))
317}
318
319fn from_oid_oid(s: &str) -> Result<(ObjectID, ObjectID), anyhow::Error> {
320 let s = s.trim();
322 let tokens = s.split(',').collect::<Vec<&str>>();
323 if tokens.len() != 2 {
324 return Err(anyhow!("Invalid object id, object id triplet"));
325 }
326 let oid1 = ObjectID::from_str(tokens[0].trim())?;
327 let oid2: ObjectID = ObjectID::from_str(tokens[1].trim())?;
328
329 Ok((oid1, oid2))
330}
331
332fn from_module_id_and_event_id(
333 s: &str,
334) -> Result<(ModuleId, (TxSequenceNumber, usize)), anyhow::Error> {
335 let tokens = s.split(' ').collect::<Vec<&str>>();
337 if tokens.len() != 3 {
338 return Err(anyhow!("Invalid input"));
339 }
340 let tx_seq = TxSequenceNumber::from_str(tokens[1])?;
341 let event_seq = usize::from_str(tokens[2])?;
342 let tokens = tokens[0].split("::").collect::<Vec<&str>>();
343 if tokens.len() != 2 {
344 return Err(anyhow!("Invalid module id"));
345 }
346 let package = ObjectID::from_str(tokens[0].trim())?;
347
348 Ok((
349 ModuleId::new(package.into(), Identifier::from_str(tokens[1].trim())?),
350 (tx_seq, event_seq),
351 ))
352}
353
354fn from_event_id(s: &str) -> Result<(TxSequenceNumber, usize), anyhow::Error> {
355 let tokens = s.split(' ').collect::<Vec<&str>>();
357 if tokens.len() != 2 {
358 return Err(anyhow!("Invalid input"));
359 }
360 let tx_seq = TxSequenceNumber::from_str(tokens[0])?;
361 let event_seq = usize::from_str(tokens[1])?;
362 Ok((tx_seq, event_seq))
363}
364
365fn from_address_and_event_id(
366 s: &str,
367) -> Result<(IotaAddress, (TxSequenceNumber, usize)), anyhow::Error> {
368 let tokens = s.split(' ').collect::<Vec<&str>>();
370 if tokens.len() != 3 {
371 return Err(anyhow!("Invalid input"));
372 }
373 let tx_seq = TxSequenceNumber::from_str(tokens[1])?;
374 let event_seq = usize::from_str(tokens[2])?;
375 let address = IotaAddress::from_str(tokens[0].trim())?;
376 Ok((address, (tx_seq, event_seq)))
377}