Skip to content

Commit 41ca6c3

Browse files
yiweichigreged93frisitano
authored
test: add test cases for handling l1 events and l1 reorg (#428)
* tests: add test fixture for easier abstraction * fix: smol rework * fix: fmt * tests: extend helpers and migrate * tests: answer comments + finish migration * tests: migrate remaining tests * tests: cleaning + fix * tests: disable eth wire bridge * tests: use expect_tx * test: add anvil to test fixture * feat: add test l1 reorg * test: add test_l1_sync_batch_commit * test: add test_l1_sync_batch_finalized * test: add l1 reorg tests * fix: chainspec dev l1 config * fix: ci * improve anvil config * improve comments * fix: l1 sync test && address some comments * fix: l1 reorg test * address comment * feat: use AnvilApi * fix ci * fix: read test tx in one * add test-utils * fix: address comment * test: add test_l1_sync_batch_revert_before_l1_synced * add database to test fixture * fix: test_l1_sync_batch_finalized * update comment * fix: patch svm-rs and svm-rs-builder * clean up anvil_state.json (this is not a fix) * feat: remove svm patch * put finalized_block fix behind #[cfg(feature = test-utils)] * fmt * fix: use database handle from chain-orchastrator handle * feat: add reboot tests (#467) * feat: add reboot to testFixture * feat: add reboot * feat: add reboot * feat: add more test * fix: db * fix: ci * merge mian * fix: tests * fix: ci * fix: ci * increase DEFAULT_EVENT_WAIT_TIMEOUT for ci * make DEFAULT_EVENT_WAIT_TIMEOUT configurable * ci: log failure event waiting * test * feat: shutdown refactor (#472) * shutdown refactor * clean up * use unused ports of anvil and run tests concurrently * fix: comment * fix: merge main conflict * fix: foundry and pprof conflict * fix: conflict * fix: clippy * address comment * fix: reorg to set db l2 head block number * address comment --------- Co-authored-by: Gregory Edison <gregory.edison1993@gmail.com> Co-authored-by: greg <82421016+greged93@users.noreply.github.com> Co-authored-by: frisitano <35734660+frisitano@users.noreply.github.com>
1 parent 76f9c0c commit 41ca6c3

File tree

35 files changed

+4654
-529
lines changed

35 files changed

+4654
-529
lines changed

Cargo.lock

Lines changed: 2572 additions & 292 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,12 @@ alloy-eips = { version = "1.0.37", default-features = false }
130130
alloy-genesis = { version = "1.0.37", default-features = false }
131131
alloy-json-rpc = { version = "1.0.37", default-features = false }
132132
alloy-network = { version = "1.0.37", default-features = false }
133+
alloy-node-bindings = { version = "1.0.37", default-features = false }
133134
alloy-primitives = { version = "1.4.1", default-features = false }
134135
alloy-provider = { version = "1.0.37", default-features = false }
135136
alloy-rpc-client = { version = "1.0.37", default-features = false }
136137
alloy-rpc-types-engine = { version = "1.0.37", default-features = false }
138+
alloy-rpc-types-anvil = { version = "1.0.37", default-features = false }
137139
alloy-rpc-types-eth = { version = "1.0.37", default-features = false }
138140
alloy-sol-types = { version = "1.4.1", default-features = false }
139141
alloy-signer = { version = "1.0.37", default-features = false }
@@ -151,9 +153,11 @@ scroll-alloy-rpc-types-engine = { git = "https://github.com/scroll-tech/reth.git
151153

152154
# reth
153155
reth-chainspec = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
156+
reth-db = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
154157
reth-e2e-test-utils = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7" }
155158
reth-eth-wire = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
156159
reth-eth-wire-types = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
160+
reth-fs-util = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
157161
reth-network = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
158162
reth-network-api = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }
159163
reth-network-p2p = { git = "https://github.com/scroll-tech/reth.git", tag = "scroll-v91.7", default-features = false }

crates/chain-orchestrator/src/consolidation.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,13 @@ impl BatchReconciliationResult {
100100
self,
101101
reorg_results: Vec<L2BlockInfoWithL1Messages>,
102102
) -> Result<BatchConsolidationOutcome, ChainOrchestratorError> {
103-
let mut consolidate_chain =
104-
BatchConsolidationOutcome::new(self.batch_info, self.target_status);
103+
// Create the batch consolidation outcome with the L2 head block number updated if there
104+
// were any reorgs.
105+
let mut consolidate_chain = BatchConsolidationOutcome::new(
106+
self.batch_info,
107+
self.target_status,
108+
!reorg_results.is_empty(),
109+
);
105110

106111
// First append all non-reorg results to the consolidated chain.
107112
self.actions.into_iter().filter(|action| !action.is_reorg()).for_each(|action| {

crates/chain-orchestrator/src/event.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum ChainOrchestratorEvent {
4646
l1_block_number: u64,
4747
},
4848
/// A batch has been finalized returning a list of finalized batches.
49-
BatchFinalized {
49+
BatchFinalizeIndexed {
5050
/// The L1 block info at which the batch finalization event was received.
5151
l1_block_info: BlockInfo,
5252
/// The list of batches that have been triggered for the derivation pipeline.
@@ -121,4 +121,6 @@ pub enum ChainOrchestratorEvent {
121121
},
122122
/// The head of the fork choice state has been updated in the engine driver.
123123
FcsHeadUpdated(BlockInfo),
124+
/// The chain orchestrator is shutting down.
125+
Shutdown,
124126
}

crates/chain-orchestrator/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ impl<
189189
biased;
190190

191191
_guard = &mut shutdown => {
192+
self.notify(ChainOrchestratorEvent::Shutdown);
192193
break;
193194
}
194195
Some(command) = self.handle_rx.recv() => {
@@ -847,7 +848,7 @@ impl<
847848
self.derivation_pipeline.push_batch(*batch, BatchStatus::Finalized).await;
848849
}
849850

850-
Ok(Some(ChainOrchestratorEvent::BatchFinalized { l1_block_info, triggered_batches }))
851+
Ok(Some(ChainOrchestratorEvent::BatchFinalizeIndexed { l1_block_info, triggered_batches }))
851852
}
852853

853854
/// Handles a batch revert event by updating the database.

crates/database/db/src/operations.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,9 @@ impl<T: WriteConnectionProvider + ?Sized + Sync> DatabaseWriteOperations for T {
831831
&self,
832832
outcome: BatchConsolidationOutcome,
833833
) -> Result<(), DatabaseError> {
834+
if let Some(block_info) = outcome.updated_l2_head() {
835+
self.set_l2_head_block_number(block_info.number).await?;
836+
}
834837
self.insert_blocks(
835838
outcome.blocks.iter().map(|b| b.block_info).collect(),
836839
outcome.batch_info,

crates/node/Cargo.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,13 @@ aws-config = "1.8.0"
7979
aws-sdk-kms = "1.76.0"
8080

8181
# test-utils
82+
anvil = { git = "https://github.com/foundry-rs/foundry.git", rev = "2c84e1c970d11ef5023a77d8002a1cb70b143888", default-features = false, optional = true }
8283
alloy-rpc-types-eth = { workspace = true, optional = true }
84+
alloy-rpc-types-anvil = { workspace = true, optional = true }
85+
reth-db = { workspace = true, optional = true, features = ["test-utils"] }
8386
reth-e2e-test-utils = { workspace = true, optional = true }
8487
reth-engine-local = { workspace = true, optional = true }
88+
reth-fs-util = { workspace = true, optional = true }
8589
reth-provider = { workspace = true, optional = true }
8690
reth-rpc-layer = { workspace = true, optional = true }
8791
reth-rpc-server-types = { workspace = true, optional = true }
@@ -107,7 +111,7 @@ tracing.workspace = true
107111
console-subscriber = "0.5.0"
108112

109113
# pprof dependencies
110-
pprof = { version = "0.15.0", features = ["flamegraph", "protobuf-codec"] }
114+
pprof = { version = "0.15.0", features = ["flamegraph", "prost-codec"] }
111115
hyper = { version = "1.5", features = ["full"] }
112116
hyper-util = { version = "0.1", features = ["tokio"] }
113117
http-body-util = "0.1"
@@ -118,6 +122,7 @@ alloy-consensus.workspace = true
118122
alloy-genesis.workspace = true
119123
alloy-eips.workspace = true
120124
futures.workspace = true
125+
reth-db = { workspace = true, features = ["test-utils"] }
121126
reth-e2e-test-utils.workspace = true
122127
reth-node-core.workspace = true
123128
reth-provider.workspace = true
@@ -138,7 +143,9 @@ alloy-rpc-types-eth = { workspace = true }
138143
[features]
139144
js-tracer = ["reth-scroll-node/js-tracer", "reth-scroll-rpc/js-tracer"]
140145
test-utils = [
146+
"reth-db",
141147
"reth-engine-local",
148+
"reth-fs-util",
142149
"reth-trie-db/test-utils",
143150
"reth-chainspec/test-utils",
144151
"reth-evm/test-utils",
@@ -161,6 +168,11 @@ test-utils = [
161168
"reth-network-p2p/test-utils",
162169
"rollup-node-chain-orchestrator/test-utils",
163170
"scroll-network/test-utils",
171+
"anvil",
164172
"reth-storage-api",
165173
"alloy-rpc-types-eth",
174+
"alloy-rpc-types-anvil",
175+
"alloy-provider/anvil-api",
176+
"alloy-provider/anvil-node",
177+
"reth-db?/test-utils",
166178
]

crates/node/src/args.rs

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use rollup_node_providers::{
3636
use rollup_node_sequencer::{
3737
L1MessageInclusionMode, PayloadBuildingConfig, Sequencer, SequencerConfig,
3838
};
39-
use rollup_node_watcher::{L1Watcher, L1WatcherHandle};
39+
use rollup_node_watcher::{L1Watcher, L1WatcherCommand};
4040
use scroll_alloy_hardforks::ScrollHardforks;
4141
use scroll_alloy_network::Scroll;
4242
use scroll_alloy_provider::{ScrollAuthApiEngineClient, ScrollEngineApi};
@@ -52,12 +52,23 @@ use scroll_wire::ScrollWireEvent;
5252
use std::{fs, path::PathBuf, sync::Arc};
5353
use tokio::sync::mpsc::UnboundedReceiver;
5454

55-
/// A struct that represents the arguments for the rollup node.
56-
#[derive(Debug, Clone, clap::Args)]
57-
pub struct ScrollRollupNodeConfig {
55+
/// Test-related configuration arguments.
56+
#[derive(Debug, Clone, Default, clap::Args)]
57+
pub struct TestArgs {
5858
/// Whether the rollup node should be run in test mode.
5959
#[arg(long)]
6060
pub test: bool,
61+
/// Test mode: skip L1 watcher Synced notifications.
62+
#[arg(long, default_value = "false")]
63+
pub skip_l1_synced: bool,
64+
}
65+
66+
/// A struct that represents the arguments for the rollup node.
67+
#[derive(Debug, Clone, clap::Args)]
68+
pub struct ScrollRollupNodeConfig {
69+
/// Test-related arguments
70+
#[command(flatten)]
71+
pub test_args: TestArgs,
6172
/// Consensus args
6273
#[command(flatten)]
6374
pub consensus_args: ConsensusArgs,
@@ -152,6 +163,7 @@ impl ScrollRollupNodeConfig {
152163
) -> eyre::Result<()> {
153164
// Instantiate the database
154165
let db_path = node_config.datadir().db();
166+
155167
let database_path = if let Some(database_path) = &self.database_args.rn_db_path {
156168
database_path.to_string_lossy().to_string()
157169
} else {
@@ -274,7 +286,7 @@ impl ScrollRollupNodeConfig {
274286
// Run the database migrations
275287
if let Some(named) = chain_spec.chain().named() {
276288
named
277-
.migrate(db.inner().get_connection(), self.test)
289+
.migrate(db.inner().get_connection(), self.test_args.test)
278290
.await
279291
.expect("failed to perform migration");
280292
} else {
@@ -388,51 +400,44 @@ impl ScrollRollupNodeConfig {
388400
};
389401
let consensus = self.consensus_args.consensus(authorized_signer)?;
390402

391-
// Define some types to support definitions of return type of following function in no_std.
392-
#[cfg(feature = "test-utils")]
393-
type L1WatcherMockOpt = Option<rollup_node_watcher::test_utils::L1WatcherMock>;
394-
395-
#[cfg(not(feature = "test-utils"))]
396-
type L1WatcherMockOpt = Option<std::convert::Infallible>;
397-
398-
let (_l1_watcher_mock, l1_watcher_handle): (L1WatcherMockOpt, Option<L1WatcherHandle>) =
399-
if let Some(provider) = l1_provider.filter(|_| !self.test) {
400-
tracing::info!(target: "scroll::node::args", ?l1_block_startup_info, "Starting L1 watcher");
401-
(
402-
None,
403-
Some(
404-
L1Watcher::spawn(
405-
provider,
406-
l1_block_startup_info,
407-
node_config,
408-
self.l1_provider_args.logs_query_block_range,
409-
self.l1_provider_args.liveness_threshold,
410-
self.l1_provider_args.liveness_check_interval,
411-
)
412-
.await,
413-
),
414-
)
415-
} else {
416-
// Create a channel for L1 notifications that we can use to inject L1 messages for
417-
// testing
403+
let is_anvil_provider = self.blob_provider_args.anvil_url.is_some();
404+
405+
let (_l1_notification_tx, _l1_command_rx, l1_watcher_handle): (_, _, _) = if let Some(
406+
provider,
407+
) =
408+
l1_provider.filter(|_| !self.test_args.test || is_anvil_provider)
409+
{
410+
tracing::info!(target: "scroll::node::args", ?l1_block_startup_info, "Starting L1 watcher");
411+
412+
let (notification_tx, handle) = L1Watcher::spawn(
413+
provider,
414+
l1_block_startup_info,
415+
node_config,
416+
self.l1_provider_args.logs_query_block_range,
417+
self.l1_provider_args.liveness_threshold,
418+
self.l1_provider_args.liveness_check_interval,
418419
#[cfg(feature = "test-utils")]
419-
{
420-
let (notification_tx, notification_rx) = tokio::sync::mpsc::channel(1000);
421-
let (command_tx, command_rx) = tokio::sync::mpsc::unbounded_channel();
422-
let handle =
423-
rollup_node_watcher::L1WatcherHandle::new(command_tx, notification_rx);
424-
let watcher_mock = rollup_node_watcher::test_utils::L1WatcherMock {
425-
command_rx: Arc::new(tokio::sync::Mutex::new(command_rx)),
426-
notification_tx,
427-
};
428-
(Some(watcher_mock), Some(handle))
429-
}
420+
self.test_args.skip_l1_synced,
421+
)
422+
.await;
423+
(Some(notification_tx), None::<UnboundedReceiver<L1WatcherCommand>>, Some(handle))
424+
} else {
425+
// Create a channel for L1 notifications that we can use to inject L1 messages for
426+
// testing
427+
#[cfg(feature = "test-utils")]
428+
{
429+
let (notification_tx, notification_rx) = tokio::sync::mpsc::channel(1000);
430+
let (command_tx, command_rx) = tokio::sync::mpsc::unbounded_channel();
431+
let handle = rollup_node_watcher::L1WatcherHandle::new(command_tx, notification_rx);
430432

431-
#[cfg(not(feature = "test-utils"))]
432-
{
433-
(None, None)
434-
}
435-
};
433+
(Some(notification_tx), Some(command_rx), Some(handle))
434+
}
435+
436+
#[cfg(not(feature = "test-utils"))]
437+
{
438+
(None, None, None)
439+
}
440+
};
436441

437442
// Construct the l1 provider.
438443
let l1_messages_provider = db.clone();
@@ -474,7 +479,7 @@ impl ScrollRollupNodeConfig {
474479
let signer = if let Some(configured_signer) = self.signer_args.signer(chain_id).await? {
475480
// Use the signer configured by SignerArgs
476481
Some(rollup_node_signer::Signer::spawn(configured_signer))
477-
} else if self.test {
482+
} else if self.test_args.test {
478483
// Use a random private key signer for testing
479484
Some(rollup_node_signer::Signer::spawn(PrivateKeySigner::random()))
480485
} else {
@@ -522,7 +527,14 @@ impl ScrollRollupNodeConfig {
522527
.await?;
523528

524529
#[cfg(feature = "test-utils")]
525-
let handle = handle.with_l1_watcher_mock(_l1_watcher_mock);
530+
let handle = {
531+
let command_rx = _l1_command_rx.map(|rx| Arc::new(tokio::sync::Mutex::new(rx)));
532+
let l1_watcher_mock = rollup_node_watcher::test_utils::L1WatcherMock {
533+
command_rx,
534+
notification_tx: _l1_notification_tx.expect("L1 notification sender should be set"),
535+
};
536+
handle.with_l1_watcher_mock(Some(l1_watcher_mock))
537+
};
526538

527539
Ok((chain_orchestrator, handle))
528540
}
@@ -1036,7 +1048,7 @@ mod tests {
10361048
#[test]
10371049
fn test_validate_sequencer_enabled_without_any_signer_fails() {
10381050
let config = ScrollRollupNodeConfig {
1039-
test: false,
1051+
test_args: TestArgs::default(),
10401052
sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() },
10411053
signer_args: SignerArgs { key_file: None, aws_kms_key_id: None, private_key: None },
10421054
database_args: RollupNodeDatabaseArgs::default(),
@@ -1067,7 +1079,7 @@ mod tests {
10671079
#[test]
10681080
fn test_validate_remote_source_enabled_without_url_fails() {
10691081
let config = ScrollRollupNodeConfig {
1070-
test: false,
1082+
test_args: TestArgs::default(),
10711083
sequencer_args: SequencerArgs::default(),
10721084
signer_args: SignerArgs::default(),
10731085
database_args: RollupNodeDatabaseArgs::default(),
@@ -1099,7 +1111,7 @@ mod tests {
10991111
#[test]
11001112
fn test_validate_sequencer_enabled_with_both_signers_fails() {
11011113
let config = ScrollRollupNodeConfig {
1102-
test: false,
1114+
test_args: TestArgs::default(),
11031115
sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() },
11041116
signer_args: SignerArgs {
11051117
key_file: Some(PathBuf::from("/path/to/key")),
@@ -1132,7 +1144,7 @@ mod tests {
11321144
#[test]
11331145
fn test_validate_sequencer_enabled_with_key_file_succeeds() {
11341146
let config = ScrollRollupNodeConfig {
1135-
test: false,
1147+
test_args: TestArgs::default(),
11361148
sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() },
11371149
signer_args: SignerArgs {
11381150
key_file: Some(PathBuf::from("/path/to/key")),
@@ -1160,7 +1172,7 @@ mod tests {
11601172
#[test]
11611173
fn test_validate_sequencer_enabled_with_aws_kms_succeeds() {
11621174
let config = ScrollRollupNodeConfig {
1163-
test: false,
1175+
test_args: TestArgs::default(),
11641176
sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() },
11651177
signer_args: SignerArgs {
11661178
key_file: None,
@@ -1188,7 +1200,7 @@ mod tests {
11881200
#[test]
11891201
fn test_validate_sequencer_disabled_without_any_signer_succeeds() {
11901202
let config = ScrollRollupNodeConfig {
1191-
test: false,
1203+
test_args: TestArgs::default(),
11921204
sequencer_args: SequencerArgs { sequencer_enabled: false, ..Default::default() },
11931205
signer_args: SignerArgs { key_file: None, aws_kms_key_id: None, private_key: None },
11941206
database_args: RollupNodeDatabaseArgs::default(),

crates/node/src/pprof.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,7 @@ impl PprofConfig {
191191
match report.pprof() {
192192
Ok(profile) => {
193193
// The profile object needs to be converted to bytes
194-
let body = match profile.write_to_bytes() {
195-
Ok(bytes) => bytes,
196-
Err(e) => {
197-
error!("Failed to encode profile: {}", e);
198-
let error_msg = format!("Failed to encode profile: {}", e);
199-
return Ok(Response::builder()
200-
.status(StatusCode::INTERNAL_SERVER_ERROR)
201-
.body(Full::new(Bytes::from(error_msg)))
202-
.unwrap());
203-
}
204-
};
194+
let body: Vec<u8> = profile.encode_to_vec();
205195

206196
info!("Successfully collected CPU profile ({} bytes)", body.len());
207197

crates/node/src/test_utils/block_builder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ impl<'a> BlockBuilder<'a> {
9292

9393
/// Build the block and validate against expectations.
9494
pub async fn build_and_await_block(self) -> eyre::Result<ScrollBlock> {
95-
let sequencer_node = &self.fixture.nodes[0];
95+
let sequencer_node =
96+
self.fixture.nodes[0].as_ref().expect("sequencer node has been shutdown");
9697

9798
// Get the sequencer from the rollup manager handle
9899
let handle = &sequencer_node.rollup_manager_handle;

0 commit comments

Comments
 (0)