Skip to content

Commit

Permalink
feat(rooch-cmd): add cmds for packing DA batch (#3310)
Browse files Browse the repository at this point in the history
## Summary

1. feat(rooch-da): add pack command to DA module
    
    Introduce the PackCommand to enable segmenting human-readable LedgerTransaction lists into chunk. Updated related DA module code and fixed some error-handling inconsistencies.

2. feat(rooch): add cmd for signing transaction order
    
    Introduces the `SignOrderCommand` to handle signing transaction orders with sequencer keys. Includes integration into the CLI, command routing, and dependencies on `rooch-sequencer`.

3. other fix/improvement
  • Loading branch information
popcnt1 authored Feb 13, 2025
1 parent 0ffd288 commit f370f15
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 14 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions crates/rooch-sequencer/src/actor/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,7 @@ impl SequencerActor {
let tx_order = self.last_sequencer_info.last_order + 1;

let tx_hash = tx_data.tx_hash();
let mut witness_data = tx_hash.as_ref().to_vec();
witness_data.extend(tx_order.to_le_bytes().iter());
let witness_hash = h256::sha3_256_of(&witness_data);
let tx_order_signature = Signature::sign(&witness_hash.0, &self.sequencer_key)
.as_ref()
.to_vec();
let tx_order_signature = sign_tx_order(tx_order, tx_hash, &self.sequencer_key);

// Calc transaction accumulator
let _tx_accumulator_root = self.tx_accumulator.append(vec![tx_hash].as_slice())?;
Expand Down Expand Up @@ -170,6 +165,15 @@ impl SequencerActor {
}
}

pub fn sign_tx_order(tx_order: u64, tx_hash: H256, sequencer_key: &RoochKeyPair) -> Vec<u8> {
let mut witness_data = tx_hash.as_ref().to_vec();
witness_data.extend(tx_order.to_le_bytes().iter());
let witness_hash = h256::sha3_256_of(&witness_data);
Signature::sign(&witness_hash.0, sequencer_key)
.as_ref()
.to_vec()
}

#[async_trait]
impl Actor for SequencerActor {
async fn started(&mut self, ctx: &mut ActorContext) {
Expand Down
1 change: 1 addition & 0 deletions crates/rooch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ rooch-common = { workspace = true }
rooch-store = { workspace = true }
rooch-faucet = { workspace = true }
rooch-oracle = { workspace = true }
rooch-sequencer = { workspace = true }

framework-release = { workspace = true }

Expand Down
4 changes: 4 additions & 0 deletions crates/rooch/src/commands/da/commands/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ pub struct ExecCommand {
)]
pub force_align: bool,

#[clap(long = "max-block-number", help = "Max block number to exec")]
pub max_block_number: Option<u128>,

#[clap(flatten)]
pub(crate) context_options: WalletContextOptions,
}
Expand Down Expand Up @@ -251,6 +254,7 @@ impl ExecCommand {
self.segment_dir.clone(),
moveos_store.transaction_store,
rooch_db.rooch_store.clone(),
self.max_block_number,
)
.await?;

Expand Down
18 changes: 10 additions & 8 deletions crates/rooch/src/commands/da/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use rooch_types::rooch_network::RoochChainID;
use rooch_types::sequencer::SequencerInfo;
use rooch_types::transaction::{LedgerTransaction, TransactionSequenceInfo};
use serde::{Deserialize, Serialize};
use std::cmp::max;
use std::cmp::min;
use std::collections::HashMap;
use std::fs;
use std::fs::File;
Expand All @@ -39,6 +39,7 @@ use tracing::{error, info, warn};
pub mod exec;
pub mod index;
pub mod namespace;
pub mod pack;
pub mod unpack;

pub(crate) struct SequencedTxStore {
Expand Down Expand Up @@ -510,12 +511,13 @@ impl TxMetaStore {
segment_dir: PathBuf,
transaction_store: TransactionDBStore,
rooch_store: RoochStore,
max_block_number: Option<u128>,
) -> anyhow::Result<Self> {
let tx_position_indexer = TxPositionIndexer::new_with_updates(
tx_position_indexer_path,
None,
Some(segment_dir),
None,
max_block_number,
)
.await?;
let exp_roots_map = Self::load_exp_roots(exp_roots_path)?;
Expand All @@ -539,7 +541,7 @@ impl TxMetaStore {

let mut reader = BufReader::new(File::open(exp_roots_path)?);
for line in reader.by_ref().lines() {
let line = line.unwrap();
let line = line?;
let parts: Vec<&str> = line.split(':').collect();
let tx_order = parts[0].parse::<u64>()?;
let state_root_raw = parts[1];
Expand Down Expand Up @@ -696,8 +698,8 @@ impl TxPositionIndexer {

pub(crate) fn dump_to_file(&self, file_path: PathBuf) -> anyhow::Result<()> {
let db = self.db;
let file = std::fs::File::create(file_path)?;
let mut writer = BufWriter::with_capacity(8 * 1024 * 1024, file.try_clone().unwrap());
let file = File::create(file_path)?;
let mut writer = BufWriter::with_capacity(8 * 1024 * 1024, file.try_clone()?);
let rtxn = self.db_env.read_txn()?;
let mut iter = db.iter(&rtxn)?;
while let Some((k, v)) = iter.next().transpose()? {
Expand All @@ -716,8 +718,8 @@ impl TxPositionIndexer {
let mut last_block_number = 0;

let db_env = Self::create_env(db_path.clone())?;
let file = std::fs::File::open(file_path)?;
let reader = std::io::BufReader::new(file);
let file = File::open(file_path)?;
let reader = BufReader::new(file);

let mut wtxn = db_env.write_txn()?; // Begin write_transaction early for create/put

Expand Down Expand Up @@ -912,7 +914,7 @@ impl TxPositionIndexer {
let segment_dir = segment_dir.ok_or_else(|| anyhow!("segment_dir is required"))?;
let ledger_tx_loader = LedgerTxGetter::new(segment_dir)?;
let stop_at = if let Some(max_block_number) = max_block_number {
max(max_block_number, ledger_tx_loader.get_max_chunk_id())
min(max_block_number, ledger_tx_loader.get_max_chunk_id())
} else {
ledger_tx_loader.get_max_chunk_id()
};
Expand Down
66 changes: 66 additions & 0 deletions crates/rooch/src/commands/da/commands/pack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use crate::cli_types::WalletContextOptions;
use crate::utils::get_sequencer_keypair;
use clap::Parser;
use rooch_types::da::batch::DABatch;
use rooch_types::da::chunk::{Chunk, ChunkV0};
use rooch_types::error::RoochResult;
use rooch_types::transaction::LedgerTransaction;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;

const DEFAULT_MAX_SEGMENT_SIZE: usize = 4 * 1024 * 1024;

/// Unpack human-readable LedgerTransaction List to segments.
#[derive(Debug, Parser)]
pub struct PackCommand {
#[clap(long = "segment-dir")]
pub segment_dir: PathBuf,
#[clap(long = "batch-path")]
pub batch_path: PathBuf,
#[clap(long = "chunk-id")]
pub chunk_id: u128,
#[clap(long)]
pub sequencer_account: Option<String>,
#[clap(flatten)]
pub context_options: WalletContextOptions,
}

impl PackCommand {
pub fn execute(self) -> RoochResult<()> {
let sequencer_keypair =
get_sequencer_keypair(self.context_options, self.sequencer_account)?;

let mut reader = BufReader::new(File::open(self.batch_path)?);
let mut tx_list = Vec::new();
for line in reader.by_ref().lines() {
let line = line?;
let tx: LedgerTransaction = serde_json::from_str(&line)?;
tx_list.push(tx);
}
let tx_order_start = tx_list.first().unwrap().sequence_info.tx_order;
let tx_order_end = tx_list.last().unwrap().sequence_info.tx_order;

let batch = DABatch::new(
self.chunk_id,
tx_order_start,
tx_order_end,
&tx_list,
sequencer_keypair,
);
batch.verify(true)?;

let segments = ChunkV0::from(batch).to_segments(DEFAULT_MAX_SEGMENT_SIZE);
for segment in segments.iter() {
let segment_path = self.segment_dir.join(segment.get_id().to_string());
let mut writer = File::create(segment_path)?;
writer.write_all(&segment.to_bytes())?;
writer.flush()?;
}

Ok(())
}
}
3 changes: 3 additions & 0 deletions crates/rooch/src/commands/da/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::cli_types::CommandAction;
use crate::commands::da::commands::exec::ExecCommand;
use crate::commands::da::commands::index::IndexCommand;
use crate::commands::da::commands::namespace::NamespaceCommand;
use crate::commands::da::commands::pack::PackCommand;
use crate::commands::da::commands::unpack::UnpackCommand;
use async_trait::async_trait;
use clap::Parser;
Expand All @@ -23,6 +24,7 @@ pub struct DA {
impl CommandAction<String> for DA {
async fn execute(self) -> RoochResult<String> {
match self.cmd {
DACommand::Pack(pack) => pack.execute().map(|_| "".to_owned()),
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()),
Expand All @@ -37,6 +39,7 @@ impl CommandAction<String> for DA {
#[derive(clap::Subcommand)]
#[clap(name = "da")]
pub enum DACommand {
Pack(PackCommand),
Unpack(UnpackCommand),
Namespace(NamespaceCommand),
Exec(Box<ExecCommand>),
Expand Down
1 change: 1 addition & 0 deletions crates/rooch/src/commands/transaction/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod get_transactions_by_hash;
pub mod get_transactions_by_order;
pub mod query;
pub mod sign;
pub mod sign_order;
pub mod submit;

pub(crate) enum FileOutputData {
Expand Down
32 changes: 32 additions & 0 deletions crates/rooch/src/commands/transaction/commands/sign_order.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use crate::cli_types::WalletContextOptions;
use crate::utils::get_sequencer_keypair;
use moveos_types::h256::H256;
use rooch_sequencer::actor::sequencer::sign_tx_order;
use rooch_types::error::RoochResult;

/// Get transactions by hashes
#[derive(Debug, clap::Parser)]
pub struct SignOrderCommand {
/// Transaction's hash
#[clap(long)]
pub tx_hash: H256,
#[clap(long)]
pub tx_order: u64,
#[clap(long)]
pub sequencer_account: Option<String>,
#[clap(flatten)]
pub(crate) context_options: WalletContextOptions,
}

impl SignOrderCommand {
pub fn execute(self) -> RoochResult<String> {
let sequencer_keypair =
get_sequencer_keypair(self.context_options, self.sequencer_account)?;
let tx_order_sign = sign_tx_order(self.tx_order, self.tx_hash, &sequencer_keypair);
let tx_order_sign_str = serde_json::to_string(&tx_order_sign)?;
Ok(tx_order_sign_str)
}
}
3 changes: 3 additions & 0 deletions crates/rooch/src/commands/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::cli_types::CommandAction;
use crate::commands::transaction::commands::sign_order::SignOrderCommand;
use crate::commands::transaction::commands::{
build::BuildCommand, get_transactions_by_hash::GetTransactionsByHashCommand,
get_transactions_by_order::GetTransactionsByOrderCommand, query::QueryCommand,
Expand Down Expand Up @@ -30,6 +31,7 @@ impl CommandAction<String> for Transaction {
TransactionCommand::Build(cmd) => cmd.execute_serialized().await,
TransactionCommand::Sign(cmd) => cmd.execute_serialized().await,
TransactionCommand::Submit(cmd) => cmd.execute_serialized().await,
TransactionCommand::SignOrder(cmd) => cmd.execute(),
}
}
}
Expand All @@ -42,4 +44,5 @@ pub enum TransactionCommand {
Query(QueryCommand),
Sign(SignCommand),
Submit(SubmitCommand),
SignOrder(SignOrderCommand),
}
27 changes: 27 additions & 0 deletions crates/rooch/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use crate::cli_types::WalletContextOptions;
use itertools::Itertools;
use rooch_key::keystore::account_keystore::AccountKeystore;
use rooch_types::address::RoochAddress;
use rooch_types::crypto::RoochKeyPair;
use rooch_types::error::{RoochError, RoochResult};
use std::io::{self, stdout, Write};
use std::{collections::BTreeMap, str::FromStr};

Expand Down Expand Up @@ -81,3 +86,25 @@ pub fn prompt_yes_no(question: &str) -> bool {
}
}
}

pub fn get_sequencer_keypair(
context_options: WalletContextOptions,
sequencer_account: Option<String>,
) -> RoochResult<RoochKeyPair> {
let context = context_options.build_require_password()?;
let sequencer_account = if sequencer_account.is_none() {
let active_address_opt = context.client_config.active_address;
if active_address_opt.is_none() {
return Err(RoochError::ActiveAddressDoesNotExistError);
}
active_address_opt.unwrap()
} else {
RoochAddress::from_str(sequencer_account.clone().unwrap().as_str()).map_err(|e| {
RoochError::CommandArgumentError(format!("Invalid sequencer account address: {}", e))
})?
};
context
.keystore
.get_key_pair(&sequencer_account, context.get_password())
.map_err(|e| RoochError::SequencerKeyPairDoesNotExistError(e.to_string()))
}

0 comments on commit f370f15

Please sign in to comment.