Skip to content

Commit

Permalink
[sequencer] Fix sequencer order bug and add tests (#1824)
Browse files Browse the repository at this point in the history
  • Loading branch information
jolestar authored Jun 5, 2024
1 parent 3b7f9dd commit 1819e4b
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 39 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

42 changes: 41 additions & 1 deletion crates/rooch-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use clap::Parser;
use moveos_config::{temp_dir, DataDirPath};
use once_cell::sync::Lazy;
use rooch_types::crypto::RoochKeyPair;
use rooch_types::rooch_network::{BuiltinChainID, RoochChainID};
use rooch_types::genesis_config::GenesisConfig;
use rooch_types::rooch_network::{BuiltinChainID, RoochChainID, RoochNetwork};
use serde::{Deserialize, Serialize};
use std::fs::create_dir_all;
use std::str::FromStr;
use std::sync::Arc;
use std::{fmt::Debug, path::Path, path::PathBuf};

Expand Down Expand Up @@ -72,6 +74,12 @@ pub struct RoochOpt {
#[clap(long, short = 'n', help = R_OPT_NET_HELP)]
pub chain_id: Option<RoochChainID>,

#[serde(skip_serializing_if = "Option::is_none")]
#[clap(long)]
/// The genesis config file path for custom chain network.
/// If the file path equals to builtin chain network name(local/dev/test/main), will use builtin genesis config.
pub genesis_config: Option<String>,

#[clap(flatten)]
pub store: StoreConfig,

Expand Down Expand Up @@ -150,6 +158,7 @@ impl RoochOpt {
let mut opt = RoochOpt {
base_data_dir: Some("TMP".into()),
chain_id: Some(BuiltinChainID::Local.into()),
genesis_config: None,
store: StoreConfig::default(),
port: None,
eth_rpc_url: None,
Expand Down Expand Up @@ -215,6 +224,37 @@ impl RoochOpt {
self.port.unwrap_or(50051)
}

pub fn chain_id(&self) -> RoochChainID {
self.chain_id.clone().unwrap_or_default()
}

pub fn genesis_config(&self) -> Option<GenesisConfig> {
self.genesis_config.clone().map(|path| {
let path = path.trim();
let genesis_config: GenesisConfig = match BuiltinChainID::from_str(path) {
Ok(builtin_id) => builtin_id.genesis_config().clone(),
Err(_) => {
let content =
std::fs::read_to_string(path).expect("read genesis config file should ok");
serde_yaml::from_str(&content).expect("parse genesis config should ok")
}
};
genesis_config
})
}

pub fn network(&self) -> RoochNetwork {
match self.chain_id() {
RoochChainID::Builtin(id) => RoochNetwork::builtin(id),
RoochChainID::Custom(id) => {
let genesis_config = self
.genesis_config()
.expect("Genesis config is required for custom network.");
RoochNetwork::new(id, genesis_config)
}
}
}

pub fn store_config(&self) -> &StoreConfig {
&self.store
}
Expand Down
6 changes: 1 addition & 5 deletions crates/rooch-rpc-server/src/server/rooch_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,7 @@ impl RoochAPIServer for RoochServer {
limit: Option<StrView<u64>>,
descending_order: Option<bool>,
) -> RpcResult<TransactionWithInfoPageView> {
let last_sequencer_order = self
.rpc_service
.get_sequencer_order()
.await?
.map_or(0, |v| v.last_order);
let last_sequencer_order = self.rpc_service.get_sequencer_order().await?;

let limit_of = min(
limit
Expand Down
3 changes: 1 addition & 2 deletions crates/rooch-rpc-server/src/service/rpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use rooch_types::indexer::state::{
FieldStateFilter, IndexerFieldState, IndexerObjectState, IndexerStateID, ObjectStateFilter,
};
use rooch_types::indexer::transaction::{IndexerTransaction, TransactionFilter};
use rooch_types::sequencer::SequencerOrder;
use rooch_types::transaction::{ExecuteTransactionResponse, LedgerTransaction, RoochTransaction};
use std::collections::HashMap;

Expand Down Expand Up @@ -190,7 +189,7 @@ impl RpcService {
Ok(resp)
}

pub async fn get_sequencer_order(&self) -> Result<Option<SequencerOrder>> {
pub async fn get_sequencer_order(&self) -> Result<u64> {
let resp = self.sequencer.get_sequencer_order().await?;
Ok(resp)
}
Expand Down
3 changes: 3 additions & 0 deletions crates/rooch-sequencer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ moveos-types = { workspace = true }

rooch-types = { workspace = true }
rooch-store = { workspace = true }
rooch-config = { workspace = true }
rooch-db = { workspace = true }
rooch-genesis = { workspace = true }
41 changes: 16 additions & 25 deletions crates/rooch-sequencer/src/actor/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ use moveos_types::h256::{self, H256};
use rooch_store::transaction_store::TransactionStore;
use rooch_store::RoochStore;
use rooch_types::crypto::{RoochKeyPair, Signature};
use rooch_types::sequencer::SequencerOrder;
use rooch_types::transaction::{LedgerTransaction, LedgerTxData};
use tracing::{debug, info};
use tracing::info;

pub struct SequencerActor {
last_order: u64,
Expand All @@ -26,12 +25,12 @@ pub struct SequencerActor {

impl SequencerActor {
pub fn new(sequencer_key: RoochKeyPair, rooch_store: RoochStore) -> Result<Self> {
let last_order_opt = rooch_store
// The genesis tx order is 0, so the sequencer order should not be None
let last_order = rooch_store
.get_meta_store()
.get_sequencer_order()?
.map(|order| order.last_order);
// Reserve tx_order = 0 for genesis tx
let last_order = last_order_opt.unwrap_or(0u64);
.map(|order| order.last_order)
.ok_or_else(|| anyhow::anyhow!("Load sequencer tx order failed"))?;
info!("Load latest sequencer order {:?}", last_order);
Ok(Self {
last_order,
Expand All @@ -40,23 +39,16 @@ impl SequencerActor {
})
}

pub fn last_order(&self) -> u64 {
self.last_order
}

pub fn sequence(&mut self, mut tx_data: LedgerTxData) -> Result<LedgerTransaction> {
let now = SystemTime::now();
let tx_timestamp = now.duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as u64;

let tx_order = if self.last_order == 0 {
let last_order_opt = self
.rooch_store
.get_meta_store()
.get_sequencer_order()?
.map(|order| order.last_order);
match last_order_opt {
Some(last_order) => last_order + 1,
None => 0,
}
} else {
self.last_order + 1
};
let tx_order = self.last_order + 1;

let hash = tx_data.tx_hash();
let mut witness_data = hash.as_ref().to_vec();
witness_data.extend(tx_order.to_le_bytes().iter());
Expand All @@ -72,8 +64,8 @@ impl SequencerActor {
);

self.rooch_store.save_transaction(tx.clone())?;
debug!("sequencer tx: {} order: {:?}", hash, tx_order);

info!("sequencer tx: {} order: {:?}", hash, tx_order);
self.last_order = tx_order;
Ok(tx)
}
}
Expand Down Expand Up @@ -129,10 +121,9 @@ impl Handler<GetTxHashsMessage> for SequencerActor {
impl Handler<GetSequencerOrderMessage> for SequencerActor {
async fn handle(
&mut self,
msg: GetSequencerOrderMessage,
_msg: GetSequencerOrderMessage,
_ctx: &mut ActorContext,
) -> Result<Option<SequencerOrder>> {
let GetSequencerOrderMessage {} = msg;
self.rooch_store.get_meta_store().get_sequencer_order()
) -> Result<u64> {
Ok(self.last_order)
}
}
3 changes: 1 addition & 2 deletions crates/rooch-sequencer/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
use anyhow::Result;
use coerce::actor::message::Message;
use moveos_types::h256::H256;
use rooch_types::sequencer::SequencerOrder;
use rooch_types::transaction::{LedgerTransaction, LedgerTxData};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -51,5 +50,5 @@ impl Message for GetTxHashsMessage {
pub struct GetSequencerOrderMessage {}

impl Message for GetSequencerOrderMessage {
type Result = Result<Option<SequencerOrder>>;
type Result = Result<u64>;
}
3 changes: 1 addition & 2 deletions crates/rooch-sequencer/src/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::{actor::sequencer::SequencerActor, messages::TransactionSequenceMessa
use anyhow::Result;
use coerce::actor::ActorRef;
use moveos_types::h256::H256;
use rooch_types::sequencer::SequencerOrder;
use rooch_types::transaction::{LedgerTransaction, LedgerTxData};

#[derive(Clone)]
Expand Down Expand Up @@ -45,7 +44,7 @@ impl SequencerProxy {
self.actor.send(GetTxHashsMessage { tx_orders }).await?
}

pub async fn get_sequencer_order(&self) -> Result<Option<SequencerOrder>> {
pub async fn get_sequencer_order(&self) -> Result<u64> {
self.actor.send(GetSequencerOrderMessage {}).await?
}
}
90 changes: 90 additions & 0 deletions crates/rooch-sequencer/tests/test_sequencer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) RoochNetwork
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use coerce::actor::{system::ActorSystem, IntoActor};
use rooch_config::RoochOpt;
use rooch_db::RoochDB;
use rooch_genesis::RoochGenesis;
use rooch_sequencer::{actor::sequencer::SequencerActor, proxy::SequencerProxy};
use rooch_types::{
crypto::RoochKeyPair,
transaction::{LedgerTxData, RoochTransaction},
};

fn init_rooch_db(opt: &RoochOpt) -> Result<RoochDB> {
let rooch_db = RoochDB::init(opt.store_config())?;
let network = opt.network();
let genesis = RoochGenesis::build(network)?;
genesis.init_genesis(&rooch_db)?;
Ok(rooch_db)
}

#[test]
fn test_sequencer() -> Result<()> {
let opt = RoochOpt::new_with_temp_store()?;
let mut last_tx_order = 0;
{
let rooch_db = init_rooch_db(&opt)?;
let sequencer_key = RoochKeyPair::generate_secp256k1();
let mut sequencer = SequencerActor::new(sequencer_key, rooch_db.rooch_store)?;
assert_eq!(sequencer.last_order(), last_tx_order);
for _ in 0..10 {
let tx_data = LedgerTxData::L2Tx(RoochTransaction::mock());
let ledger_tx = sequencer.sequence(tx_data)?;
assert_eq!(ledger_tx.sequence_info.tx_order, last_tx_order + 1);
last_tx_order = ledger_tx.sequence_info.tx_order;
}
assert_eq!(sequencer.last_order(), last_tx_order);
}
// load from db again
{
let rooch_db = RoochDB::init(opt.store_config())?;
let sequencer_key = RoochKeyPair::generate_secp256k1();
let mut sequencer = SequencerActor::new(sequencer_key, rooch_db.rooch_store)?;
assert_eq!(sequencer.last_order(), last_tx_order);
let tx_data = LedgerTxData::L2Tx(RoochTransaction::mock());
let ledger_tx = sequencer.sequence(tx_data)?;
assert_eq!(ledger_tx.sequence_info.tx_order, last_tx_order + 1);
}
Ok(())
}

// test concurrent
// Build a sequencer actor and sequence transactions concurrently
#[tokio::test]
async fn test_sequencer_concurrent() -> Result<()> {
let opt = RoochOpt::new_with_temp_store()?;
let rooch_db = init_rooch_db(&opt)?;
let sequencer_key = RoochKeyPair::generate_secp256k1();

let actor_system = ActorSystem::global_system();

let sequencer = SequencerActor::new(sequencer_key, rooch_db.rooch_store)?
.into_actor(Some("Sequencer"), &actor_system)
.await?;
let sequencer_proxy = SequencerProxy::new(sequencer.into());

// start n thread to sequence
let n = 10;
let mut handles = vec![];
for _ in 0..n {
let sequencer_proxy = sequencer_proxy.clone();
//Use tokio to spawn a new async task
let handle = tokio::task::spawn(async move {
for _ in 0..n {
let tx_data = LedgerTxData::L2Tx(RoochTransaction::mock());
let _ = sequencer_proxy.sequence_transaction(tx_data).await.unwrap();
}
});
handles.push(handle);
}
for handle in handles {
handle.await?;
}

let sequencer_order = sequencer_proxy.get_sequencer_order().await?;
assert_eq!(sequencer_order, n * n);

Ok(())
}
6 changes: 6 additions & 0 deletions crates/rooch-store/src/meta_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ impl MetaDBStore {
}

pub fn save_sequencer_order(&self, sequencer_order: SequencerOrder) -> Result<()> {
let pre_sequencer_order = self.get_sequencer_order()?;
if let Some(pre_sequencer_order) = pre_sequencer_order {
if sequencer_order.last_order != pre_sequencer_order.last_order + 1 {
return Err(anyhow::anyhow!("Sequencer order is not continuous"));
}
}
self.sequencer_order_store
.put_sync(SEQUENCER_ORDER_KEY.to_string(), sequencer_order)
}
Expand Down
3 changes: 2 additions & 1 deletion crates/rooch-types/src/rooch_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ impl FromStr for BuiltinChainID {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
let s = s.to_lowercase();
match s.as_str() {
"local" => Ok(BuiltinChainID::Local),
"dev" => Ok(BuiltinChainID::Dev),
"test" => Ok(BuiltinChainID::Test),
Expand Down
1 change: 0 additions & 1 deletion crates/rooch-types/src/transaction/rooch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ impl RoochTransaction {
}

//TODO use protest Arbitrary to generate mock data
#[cfg(test)]
pub fn mock() -> RoochTransaction {
use crate::{address::RoochSupportedAddress, crypto::RoochKeyPair};
use move_core_types::{
Expand Down

0 comments on commit 1819e4b

Please sign in to comment.