diff --git a/crates/rooch-common/src/utils/mod.rs b/crates/rooch-common/src/utils/mod.rs index 049694ed8d..1e7d5516c3 100644 --- a/crates/rooch-common/src/utils/mod.rs +++ b/crates/rooch-common/src/utils/mod.rs @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 pub mod humanize; +pub mod vec; diff --git a/crates/rooch-common/src/utils/vec.rs b/crates/rooch-common/src/utils/vec.rs new file mode 100644 index 0000000000..5f399a1b5a --- /dev/null +++ b/crates/rooch-common/src/utils/vec.rs @@ -0,0 +1,192 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +// find the last true element in the array: +// the array is sorted by the predicate, and the predicate is true for the first n elements and false for the rest. +pub fn find_last_true(arr: &[T], predicate: impl Fn(&T) -> bool) -> Option<&T> { + if arr.is_empty() { + return None; + } + if !predicate(&arr[0]) { + return None; + } + if predicate(&arr[arr.len() - 1]) { + return Some(&arr[arr.len() - 1]); + } + + // binary search + let mut left = 0; + let mut right = arr.len() - 1; + + while left + 1 < right { + let mid = left + (right - left) / 2; + if predicate(&arr[mid]) { + left = mid; // mid is true, the final answer is mid or on the right + } else { + right = mid; // mid is false, the final answer is on the left + } + } + + // left is the last true position + Some(&arr[left]) +} + +#[cfg(test)] +mod tests { + use super::*; + + mod find_last_true { + use super::*; + + #[derive(Debug, PartialEq)] + struct TestItem { + id: usize, + value: bool, + } + + impl TestItem { + fn new(id: usize, value: bool) -> Self { + Self { id, value } + } + } + + #[test] + fn test_empty_array() { + let items: Vec = vec![]; + let result = find_last_true(&items, |item| item.value); + assert!(result.is_none()); + } + + #[test] + fn test_single_element_true() { + let items = vec![TestItem::new(0, true)]; + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(0)); + } + + #[test] + fn test_single_element_false() { + let items = vec![TestItem::new(0, false)]; + let result = find_last_true(&items, |item| item.value); + assert_eq!(result, None); + } + + #[test] + fn test_all_true() { + let items = vec![ + TestItem::new(1, true), + TestItem::new(2, true), + TestItem::new(3, true), + ]; + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(3)); + } + + #[test] + fn test_all_false() { + let items = vec![ + TestItem::new(1, false), + TestItem::new(2, false), + TestItem::new(3, false), + ]; + let result = find_last_true(&items, |item| item.value); + assert_eq!(result, None); + } + + #[test] + fn test_odd_length_middle_transition() { + let items = vec![ + TestItem::new(1, true), + TestItem::new(2, true), + TestItem::new(3, true), + TestItem::new(4, false), + TestItem::new(5, false), + ]; + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(3)); + } + + #[test] + fn test_even_length_middle_transition() { + let items = vec![ + TestItem::new(1, true), + TestItem::new(2, true), + TestItem::new(3, false), + TestItem::new(4, false), + ]; + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(2)); + } + + #[test] + fn test_only_first_true() { + let items = vec![ + TestItem::new(1, true), + TestItem::new(2, false), + TestItem::new(3, false), + TestItem::new(4, false), + ]; + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(1)); + } + + #[test] + fn test_only_last_true() { + let items = vec![ + TestItem::new(1, false), + TestItem::new(2, false), + TestItem::new(3, false), + TestItem::new(4, true), + ]; + let result = find_last_true(&items, |item| item.value); + assert_eq!(result, None); // no sorted array, so no result + } + + #[test] + fn test_large_array() { + let mut items = Vec::new(); + for i in 1..1000 { + items.push(TestItem::new(i, i <= 500)); + } + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(500)); + + let mut items = Vec::new(); + for i in 1..1001 { + items.push(TestItem::new(i, i <= 500)); + } + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(500)); + + let mut items = Vec::new(); + for i in 1..1001 { + items.push(TestItem::new(i, i <= 501)); + } + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(501)); + } + + #[test] + fn test_various_transition_points() { + // Test cases with different transition points + let test_cases = [ + (vec![true], 0), + (vec![true, false], 0), + (vec![true, true, false], 1), + (vec![true, true, true, false], 2), + (vec![true, true, true, true, false], 3), + ]; + + for (i, (values, expected)) in test_cases.iter().enumerate() { + let items: Vec = values + .iter() + .enumerate() + .map(|(id, &v)| TestItem::new(id, v)) + .collect(); + + let result = find_last_true(&items, |item| item.value).map(|item| item.id); + assert_eq!(result, Some(*expected), "Failed at test case {}", i); + } + } + } +} diff --git a/crates/rooch/src/commands/da/commands/exec.rs b/crates/rooch/src/commands/da/commands/exec.rs index d0020e4c99..a535d0fe6b 100644 --- a/crates/rooch/src/commands/da/commands/exec.rs +++ b/crates/rooch/src/commands/da/commands/exec.rs @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::commands::da::commands::{build_rooch_db, LedgerTxGetter, TxOrderHashBlockGetter}; +use crate::commands::da::commands::{build_rooch_db, LedgerTxGetter, TxDAIndexer}; use anyhow::Context; use bitcoin::hashes::Hash; use bitcoin_client::actor::client::BitcoinClientConfig; @@ -108,13 +108,13 @@ impl ExecCommand { let (order_state_pair, tx_order_end) = self.load_order_state_pair(); let ledger_tx_loader = LedgerTxGetter::new(self.segment_dir.clone())?; - let tx_order_hash_block_list = TxOrderHashBlockGetter::load_from_file( + let tx_da_indexer = TxDAIndexer::load_from_file( self.order_hash_path.clone(), moveos_store.transaction_store, )?; Ok(ExecInner { ledger_tx_getter: ledger_tx_loader, - tx_order_hash_block_getter: tx_order_hash_block_list, + tx_da_indexer, order_state_pair, tx_order_end, bitcoin_client_proxy, @@ -149,7 +149,7 @@ impl ExecCommand { struct ExecInner { ledger_tx_getter: LedgerTxGetter, - tx_order_hash_block_getter: TxOrderHashBlockGetter, + tx_da_indexer: TxDAIndexer, order_state_pair: HashMap, tx_order_end: u64, @@ -264,7 +264,7 @@ impl ExecInner { } async fn produce_tx(&self, tx: Sender) -> anyhow::Result<()> { - let last_executed_opt = self.tx_order_hash_block_getter.find_last_executed()?; + let last_executed_opt = self.tx_da_indexer.find_last_executed()?; let mut next_tx_order = last_executed_opt .clone() .map(|v| v.tx_order + 1) @@ -283,9 +283,8 @@ impl ExecInner { if let (Some(rollback), Some(last_executed)) = (self.rollback, last_executed_opt.clone()) { let last_executed_tx_order = last_executed.tx_order; if rollback < last_executed_tx_order { - let new_last_and_rollback = self - .tx_order_hash_block_getter - .slice(rollback, last_executed_tx_order)?; + let new_last_and_rollback = + self.tx_da_indexer.slice(rollback, last_executed_tx_order)?; // split into two parts, the first get execution info for new startup, all others rollback let (new_last, rollback_part) = new_last_and_rollback.split_first().unwrap(); tracing::info!( @@ -304,9 +303,8 @@ impl ExecInner { ) })?; } - let rollback_execution_info = self - .tx_order_hash_block_getter - .get_execution_info(new_last.tx_hash)?; + let rollback_execution_info = + self.tx_da_indexer.get_execution_info(new_last.tx_hash)?; self.update_startup_info_after_rollback(rollback_execution_info.unwrap())?; next_block_number = new_last.block_number; next_tx_order = rollback + 1; diff --git a/crates/rooch/src/commands/da/commands/dump_tx_order_hash.rs b/crates/rooch/src/commands/da/commands/index_tx.rs similarity index 86% rename from crates/rooch/src/commands/da/commands/dump_tx_order_hash.rs rename to crates/rooch/src/commands/da/commands/index_tx.rs index cf640a96fa..9140b7b8a9 100644 --- a/crates/rooch/src/commands/da/commands/dump_tx_order_hash.rs +++ b/crates/rooch/src/commands/da/commands/index_tx.rs @@ -1,23 +1,23 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::commands::da::commands::{LedgerTxGetter, TxOrderHashBlock}; +use crate::commands::da::commands::{LedgerTxGetter, TxDAIndex}; use rooch_types::error::{RoochError, RoochResult}; use std::fs::File; use std::io::BufWriter; use std::io::Write; use std::path::PathBuf; -/// Dump tx_order:tx_hash:block_number to a file from segments +/// Index tx_order:tx_hash:block_number to a file from segments #[derive(Debug, clap::Parser)] -pub struct DumpTxOrderHashCommand { +pub struct IndexTxCommand { #[clap(long = "segment-dir")] pub segment_dir: PathBuf, #[clap(long = "output")] pub output: PathBuf, } -impl DumpTxOrderHashCommand { +impl IndexTxCommand { pub fn execute(self) -> RoochResult<()> { let ledger_tx_loader = LedgerTxGetter::new(self.segment_dir)?; let mut block_number = ledger_tx_loader.get_min_chunk_id(); @@ -46,7 +46,7 @@ impl DumpTxOrderHashCommand { writeln!( writer, "{}", - TxOrderHashBlock::new(tx_order, tx_hash, block_number) + TxDAIndex::new(tx_order, tx_hash, block_number) )?; expected_tx_order += 1; } diff --git a/crates/rooch/src/commands/da/commands/mod.rs b/crates/rooch/src/commands/da/commands/mod.rs index 19becd2cd4..8cba6c5fef 100644 --- a/crates/rooch/src/commands/da/commands/mod.rs +++ b/crates/rooch/src/commands/da/commands/mod.rs @@ -6,6 +6,7 @@ use moveos_store::transaction_store::{TransactionDBStore, TransactionStore}; use moveos_types::h256::H256; use moveos_types::moveos_std::object::ObjectMeta; use moveos_types::transaction::TransactionExecutionInfo; +use rooch_common::utils::vec::find_last_true; use rooch_config::RoochOpt; use rooch_db::RoochDB; use rooch_types::da::chunk::chunk_from_segments; @@ -18,8 +19,8 @@ use std::fs::File; use std::io::{BufRead, BufReader, Read}; use std::path::PathBuf; -pub mod dump_tx_order_hash; pub mod exec; +pub mod index_tx; pub mod namespace; pub mod unpack; @@ -143,15 +144,15 @@ impl LedgerTxGetter { } #[derive(Debug, Clone)] -pub struct TxOrderHashBlock { +pub struct TxDAIndex { pub tx_order: u64, pub tx_hash: H256, pub block_number: u128, } -impl TxOrderHashBlock { +impl TxDAIndex { pub fn new(tx_order: u64, tx_hash: H256, block_number: u128) -> Self { - TxOrderHashBlock { + TxDAIndex { tx_order, tx_hash, block_number, @@ -159,7 +160,7 @@ impl TxOrderHashBlock { } } -impl std::fmt::Display for TxOrderHashBlock { +impl std::fmt::Display for TxDAIndex { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -169,7 +170,7 @@ impl std::fmt::Display for TxOrderHashBlock { } } -impl std::str::FromStr for TxOrderHashBlock { +impl std::str::FromStr for TxDAIndex { type Err = anyhow::Error; fn from_str(s: &str) -> Result { @@ -180,7 +181,7 @@ impl std::str::FromStr for TxOrderHashBlock { let tx_order = parts[0].parse::()?; let tx_hash = H256::from_str(parts[1])?; let block_number = parts[2].parse::()?; - Ok(TxOrderHashBlock { + Ok(TxDAIndex { tx_order, tx_hash, block_number, @@ -191,12 +192,12 @@ impl std::str::FromStr for TxOrderHashBlock { /// TxOrderHashBlockGetter is used to get TxOrderHashBlock from a file /// all tx_order_hash_blocks(start from tx_order 1) are stored in a file, /// each line is a TxOrderHashBlock -pub struct TxOrderHashBlockGetter { - tx_order_hash_blocks: Vec, +pub struct TxDAIndexer { + tx_order_hash_blocks: Vec, transaction_store: TransactionDBStore, } -impl TxOrderHashBlockGetter { +impl TxDAIndexer { pub fn load_from_file( file_path: PathBuf, transaction_store: TransactionDBStore, @@ -205,20 +206,30 @@ impl TxOrderHashBlockGetter { let mut reader = BufReader::new(File::open(file_path)?); for line in reader.by_ref().lines() { let line = line?; - let item = line.parse::()?; + let item = line.parse::()?; tx_order_hashes.push(item); } - Ok(TxOrderHashBlockGetter { + tx_order_hashes.sort_by(|a, b| a.tx_order.cmp(&b.tx_order)); // avoiding wrong order + Ok(TxDAIndexer { tx_order_hash_blocks: tx_order_hashes, transaction_store, }) } - pub fn slice( - &self, - start_tx_order: u64, - end_tx_order: u64, - ) -> anyhow::Result> { + pub fn get_tx_hash(&self, tx_order: u64) -> Option { + let r = self + .tx_order_hash_blocks + .binary_search_by(|x| x.tx_order.cmp(&tx_order)); + let idx = match r { + Ok(i) => i, + Err(_) => { + return None; + } + }; + Some(self.tx_order_hash_blocks[idx].tx_hash) + } + + pub fn slice(&self, start_tx_order: u64, end_tx_order: u64) -> anyhow::Result> { let r = self .tx_order_hash_blocks .binary_search_by(|x| x.tx_order.cmp(&start_tx_order)); @@ -232,14 +243,14 @@ impl TxOrderHashBlockGetter { Ok(self.tx_order_hash_blocks[start_idx..end_idx + 1].to_vec()) } - pub fn find_last_executed(&self) -> anyhow::Result> { + pub fn find_last_executed(&self) -> anyhow::Result> { let r = find_last_true(&self.tx_order_hash_blocks, |item| { self.has_executed(item.tx_hash) }); Ok(r.cloned()) } - pub fn has_executed(&self, tx_hash: H256) -> bool { + fn has_executed(&self, tx_hash: H256) -> bool { let execution_info = self .transaction_store .get_tx_execution_info(tx_hash) @@ -254,194 +265,18 @@ impl TxOrderHashBlockGetter { let execution_info = self.transaction_store.get_tx_execution_info(tx_hash)?; Ok(execution_info) } -} - -// find the last true element in the array: -// the array is sorted by the predicate, and the predicate is true for the first n elements and false for the rest. -fn find_last_true(arr: &[T], predicate: impl Fn(&T) -> bool) -> Option<&T> { - if arr.is_empty() { - return None; - } - if !predicate(&arr[0]) { - return None; - } - if predicate(&arr[arr.len() - 1]) { - return Some(&arr[arr.len() - 1]); - } - // binary search - let mut left = 0; - let mut right = arr.len() - 1; + pub fn get_execution_info_by_order( + &self, + tx_order: u64, + ) -> anyhow::Result> { + let tx_hash_option = self.get_tx_hash(tx_order); - while left + 1 < right { - let mid = left + (right - left) / 2; - if predicate(&arr[mid]) { - left = mid; // mid is true, the final answer is mid or on the right + if let Some(tx_hash) = tx_hash_option { + let execution_info = self.transaction_store.get_tx_execution_info(tx_hash)?; + Ok(execution_info) } else { - right = mid; // mid is false, the final answer is on the left - } - } - - // left is the last true position - Some(&arr[left]) -} - -#[cfg(test)] -mod tests { - use super::*; - - mod find_last_true { - use super::*; - - #[derive(Debug, PartialEq)] - struct TestItem { - id: usize, - value: bool, - } - - impl TestItem { - fn new(id: usize, value: bool) -> Self { - Self { id, value } - } - } - - #[test] - fn test_empty_array() { - let items: Vec = vec![]; - let result = find_last_true(&items, |item| item.value); - assert!(result.is_none()); - } - - #[test] - fn test_single_element_true() { - let items = vec![TestItem::new(0, true)]; - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(0)); - } - - #[test] - fn test_single_element_false() { - let items = vec![TestItem::new(0, false)]; - let result = find_last_true(&items, |item| item.value); - assert_eq!(result, None); - } - - #[test] - fn test_all_true() { - let items = vec![ - TestItem::new(1, true), - TestItem::new(2, true), - TestItem::new(3, true), - ]; - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(3)); - } - - #[test] - fn test_all_false() { - let items = vec![ - TestItem::new(1, false), - TestItem::new(2, false), - TestItem::new(3, false), - ]; - let result = find_last_true(&items, |item| item.value); - assert_eq!(result, None); - } - - #[test] - fn test_odd_length_middle_transition() { - let items = vec![ - TestItem::new(1, true), - TestItem::new(2, true), - TestItem::new(3, true), - TestItem::new(4, false), - TestItem::new(5, false), - ]; - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(3)); - } - - #[test] - fn test_even_length_middle_transition() { - let items = vec![ - TestItem::new(1, true), - TestItem::new(2, true), - TestItem::new(3, false), - TestItem::new(4, false), - ]; - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(2)); - } - - #[test] - fn test_only_first_true() { - let items = vec![ - TestItem::new(1, true), - TestItem::new(2, false), - TestItem::new(3, false), - TestItem::new(4, false), - ]; - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(1)); - } - - #[test] - fn test_only_last_true() { - let items = vec![ - TestItem::new(1, false), - TestItem::new(2, false), - TestItem::new(3, false), - TestItem::new(4, true), - ]; - let result = find_last_true(&items, |item| item.value); - assert_eq!(result, None); // no sorted array, so no result - } - - #[test] - fn test_large_array() { - let mut items = Vec::new(); - for i in 1..1000 { - items.push(TestItem::new(i, i <= 500)); - } - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(500)); - - let mut items = Vec::new(); - for i in 1..1001 { - items.push(TestItem::new(i, i <= 500)); - } - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(500)); - - let mut items = Vec::new(); - for i in 1..1001 { - items.push(TestItem::new(i, i <= 501)); - } - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(501)); - } - - #[test] - fn test_various_transition_points() { - // Test cases with different transition points - let test_cases = [ - (vec![true], 0), - (vec![true, false], 0), - (vec![true, true, false], 1), - (vec![true, true, true, false], 2), - (vec![true, true, true, true, false], 3), - ]; - - for (i, (values, expected)) in test_cases.iter().enumerate() { - let items: Vec = values - .iter() - .enumerate() - .map(|(id, &v)| TestItem::new(id, v)) - .collect(); - - let result = find_last_true(&items, |item| item.value).map(|item| item.id); - assert_eq!(result, Some(*expected), "Failed at test case {}", i); - } + Ok(None) } } } diff --git a/crates/rooch/src/commands/da/mod.rs b/crates/rooch/src/commands/da/mod.rs index a97a5a342c..8d1a3677c9 100644 --- a/crates/rooch/src/commands/da/mod.rs +++ b/crates/rooch/src/commands/da/mod.rs @@ -4,8 +4,8 @@ pub mod commands; use crate::cli_types::CommandAction; -use crate::commands::da::commands::dump_tx_order_hash::DumpTxOrderHashCommand; use crate::commands::da::commands::exec::ExecCommand; +use crate::commands::da::commands::index_tx::IndexTxCommand; use crate::commands::da::commands::namespace::NamespaceCommand; use crate::commands::da::commands::unpack::UnpackCommand; use async_trait::async_trait; @@ -26,9 +26,7 @@ impl CommandAction for DA { DACommand::Unpack(unpack) => unpack.execute().map(|_| "".to_owned()), DACommand::Namespace(namespace) => namespace.execute().map(|_| "".to_owned()), DACommand::Exec(exec) => exec.execute().await.map(|_| "".to_owned()), - DACommand::DumpTxOrderHash(dump_tx_order_hash) => { - dump_tx_order_hash.execute().map(|_| "".to_owned()) - } + DACommand::IndexTx(index_tx) => index_tx.execute().map(|_| "".to_owned()), } } } @@ -39,5 +37,5 @@ pub enum DACommand { Unpack(UnpackCommand), Namespace(NamespaceCommand), Exec(ExecCommand), - DumpTxOrderHash(DumpTxOrderHashCommand), + IndexTx(IndexTxCommand), } diff --git a/crates/rooch/src/commands/db/commands/dump_tx_root.rs b/crates/rooch/src/commands/db/commands/dump_tx_root.rs new file mode 100644 index 0000000000..6472e67e30 --- /dev/null +++ b/crates/rooch/src/commands/db/commands/dump_tx_root.rs @@ -0,0 +1,69 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::commands::da::commands::TxDAIndexer; +use crate::commands::db::commands::init; +use clap::Parser; +use rooch_config::R_OPT_NET_HELP; +use rooch_types::error::RoochResult; +use rooch_types::rooch_network::RoochChainID; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; + +/// Get changeset by order +#[derive(Debug, Parser)] +pub struct DumpTxRootCommand { + #[clap(long, help = "start tx order")] + pub start: u64, + #[clap(long, help = "total tx count, [start, start+limit)")] + pub limit: u64, + #[clap( + long = "order-hash-path", + help = "Path to tx_order:tx_hash:block_number file" + )] + pub order_hash_path: PathBuf, + #[clap(long, help = "tx_order:state_root output file path")] + pub output: PathBuf, + + #[clap(long = "data-dir", short = 'd')] + /// Path to data dir, this dir is base dir, the final data_dir is base_dir/chain_network_name + pub base_data_dir: Option, + + /// If local chainid, start the service with a temporary data store. + /// All data will be deleted when the service is stopped. + #[clap(long, short = 'n', help = R_OPT_NET_HELP)] + pub chain_id: Option, +} + +impl DumpTxRootCommand { + pub async fn execute(self) -> RoochResult<()> { + let (_root, rooch_db, _start_time) = init(self.base_data_dir, self.chain_id); + let moveos_store = rooch_db.moveos_store.clone(); + let tx_da_indexer = TxDAIndexer::load_from_file( + self.order_hash_path.clone(), + moveos_store.transaction_store, + )?; + + let file = File::create(self.output.clone())?; + let mut writer = BufWriter::with_capacity(8 * 1024 * 1024, file.try_clone().unwrap()); + + for tx_order in self.start..self.start + self.limit { + let execution_info = tx_da_indexer.get_execution_info_by_order(tx_order)?; + if execution_info.is_none() { + tracing::warn!("tx_order {} execution_info not found", tx_order); + continue; + } + writeln!( + writer, + "{}:{:?}", + tx_order, + execution_info.unwrap().state_root + )?; + } + writer.flush()?; + file.sync_data()?; + + Ok(()) + } +} diff --git a/crates/rooch/src/commands/db/commands/mod.rs b/crates/rooch/src/commands/db/commands/mod.rs index 268d99109b..8f533abfee 100644 --- a/crates/rooch/src/commands/db/commands/mod.rs +++ b/crates/rooch/src/commands/db/commands/mod.rs @@ -12,6 +12,7 @@ use std::path::PathBuf; use std::time::SystemTime; pub mod drop; +pub mod dump_tx_root; pub mod get_changeset_by_order; pub mod repair; pub mod revert; diff --git a/crates/rooch/src/commands/db/mod.rs b/crates/rooch/src/commands/db/mod.rs index 2cb0a9221e..93a3fb62e1 100644 --- a/crates/rooch/src/commands/db/mod.rs +++ b/crates/rooch/src/commands/db/mod.rs @@ -3,6 +3,7 @@ use crate::cli_types::CommandAction; use crate::commands::db::commands::drop::DropCommand; +use crate::commands::db::commands::dump_tx_root::DumpTxRootCommand; use crate::commands::db::commands::get_changeset_by_order::GetChangesetByOrderCommand; use crate::commands::db::commands::repair::RepairCommand; use crate::commands::db::commands::revert::RevertCommand; @@ -41,6 +42,9 @@ impl CommandAction for DB { serde_json::to_string_pretty(&resp).expect("Failed to serialize response") }) } + DBCommand::DumpTxRoot(dump_tx_root) => dump_tx_root.execute().await.map(|resp| { + serde_json::to_string_pretty(&resp).expect("Failed to serialize response") + }), } } } @@ -53,4 +57,5 @@ pub enum DBCommand { Drop(DropCommand), Repair(RepairCommand), GetChangesetByOrder(GetChangesetByOrderCommand), + DumpTxRoot(DumpTxRootCommand), }