Skip to content

Commit 33de524

Browse files
committed
add docker compose tests
1 parent 0959819 commit 33de524

6 files changed

Lines changed: 263 additions & 2 deletions

File tree

crates/node/src/args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ pub struct RemoteBlockSourceArgs {
923923
pub enabled: bool,
924924

925925
/// URL for the remote L2 source node RPC
926-
#[arg(long = "remote-source.url", value_name = "URL")]
926+
#[arg(long = "remote-source.url", id = "remote_source_url", value_name = "URL")]
927927
pub url: Option<reqwest::Url>,
928928

929929
/// Polling interval in milliseconds (when already synced)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
services:
2+
l1-node:
3+
image: ghcr.io/foundry-rs/foundry:v1.2.3
4+
entrypoint: ["bash", "/launch_l1.bash"]
5+
ports:
6+
- "8544:8545"
7+
volumes:
8+
- ./launch_l1.bash:/launch_l1.bash:ro
9+
- ./anvil.env:/anvil.env:ro
10+
- ./anvil_state.json:/anvil_state.json:ro
11+
healthcheck:
12+
test: ["CMD", "bash", "-c", "[ \"$(cast storage 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 0x67 --rpc-url http://localhost:8545)\" = \"0x000000000000000000000000b674ff99cca262c99d3eab5b32796a99188543da\" ]"]
13+
interval: 3s
14+
timeout: 10s
15+
retries: 30
16+
start_period: 0s
17+
18+
rollup-node-sequencer:
19+
build:
20+
context: ../
21+
dockerfile: Dockerfile.test
22+
entrypoint: ["bash", "/launch_rollup_node_sequencer.bash"]
23+
environment:
24+
- RUST_LOG=info
25+
ports:
26+
- "8545:8545" # JSON-RPC
27+
- "6060:6060" # Metrics
28+
volumes:
29+
- ./launch_rollup_node_sequencer.bash:/launch_rollup_node_sequencer.bash:ro
30+
- ./l2reth-genesis-e2e.json:/l2reth/l2reth-genesis-e2e.json:ro
31+
- l2reth-sequencer:/l2reth
32+
depends_on:
33+
l1-node:
34+
condition: service_healthy
35+
36+
rollup-node-remote-source:
37+
build:
38+
context: ../
39+
dockerfile: Dockerfile.test
40+
entrypoint: ["bash", "/launch_rollup_node_remote_source.bash"]
41+
environment:
42+
- RUST_LOG=info
43+
ports:
44+
- "8546:8545" # JSON-RPC
45+
- "6061:6060" # Metrics
46+
volumes:
47+
- ./launch_rollup_node_remote_source.bash:/launch_rollup_node_remote_source.bash:ro
48+
- ./l2reth-genesis-e2e.json:/l2reth/l2reth-genesis-e2e.json:ro
49+
- l2reth-remote-source:/l2reth
50+
depends_on:
51+
l1-node:
52+
condition: service_healthy
53+
54+
l2geth-skipsignercheck:
55+
image: scrolltech/l2geth:scroll-v5.10.2-skip-signer-check
56+
platform: linux/amd64
57+
entrypoint: ["bash", "/launch_l2geth_skipsignercheck.bash"]
58+
ports:
59+
- "8547:8545" # JSON-RPC
60+
- "6062:6060" # Metrics
61+
volumes:
62+
- ./l2geth-genesis-e2e.json:/l2geth-genesis-e2e.json:ro
63+
- ./launch_l2geth_skipsignercheck.bash:/launch_l2geth_skipsignercheck.bash:ro
64+
- l2geth-skipsignercheck:/l2geth
65+
depends_on:
66+
l1-node:
67+
condition: service_healthy
68+
69+
volumes:
70+
l2reth-sequencer:
71+
l2reth-remote-source:
72+
l2geth-skipsignercheck:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
geth init --datadir=/l2geth /l2geth-genesis-e2e.json
5+
6+
echo "Starting l2geth with skip signer check..."
7+
exec geth --datadir=/l2geth \
8+
--port 30303 --syncmode full --networkid 938471 --nodiscover \
9+
--http --http.addr 0.0.0.0 --http.port 8545 --http.vhosts "*" --http.corsdomain "*" --http.api "admin,eth,scroll,net,web3,debug" \
10+
--ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api "admin,eth,scroll,net,web3,debug" \
11+
--pprof --pprof.addr 0.0.0.0 --pprof.port 6060 --metrics --verbosity 5 --log.debug \
12+
--l1.endpoint "http://l1-node:8545" --l1.confirmations finalized --l1.sync.startblock 0 --l1.sync.interval 1s \
13+
--gcmode archive --cache.noprefetch --cache.snapshot=0 --snapshot=false \
14+
--gossip.enablebroadcasttoall \
15+
--nat extip:0.0.0.0 \
16+
--skipsignercheck
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# Prepare signer key
5+
echo -n "0xd510c4b7c61a604f800c4f06803b1ee14b9a63de345e53426ae50425f2dbb058" > /l2reth/sequencer-key
6+
7+
# Prepare node key for the remote source node to have a predictable enode URL
8+
echo -n "53b69b913cb8c64a3ae83bf0ffb1e9e9efa80d7e1924dd076a8d2b1482f1b21b" > /l2reth/nodekey
9+
# -> enode://849431bd98c23f8203cf475cfd8efb980d1e2af46337141cfb7dd960d4ae6f8c489846da293305705c8918fbf991e41805afc8c7231c96f3f938120b6826affc@rollup-node-remote-source:30303
10+
11+
export RUST_LOG=sqlx=off,scroll=trace,reth=info,rollup=trace,info
12+
13+
exec rollup-node node --chain /l2reth/l2reth-genesis-e2e.json --datadir=/l2reth --metrics=0.0.0.0:6060 \
14+
--disable-discovery \
15+
--network.valid_signer "0xb674ff99cca262c99d3eab5b32796a99188543da" \
16+
--http --http.addr=0.0.0.0 --http.port=8545 --http.corsdomain "*" --http.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev \
17+
--ws --ws.addr=0.0.0.0 --ws.port=8546 --ws.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev \
18+
--rpc.rollup-node-admin \
19+
--log.stdout.format log-fmt -vvv \
20+
--sequencer.enabled \
21+
--sequencer.allow-empty-blocks \
22+
--signer.key-file /l2reth/sequencer-key \
23+
--sequencer.block-time 500 \
24+
--sequencer.payload-building-duration 400 \
25+
--txpool.pending-max-count=1000 \
26+
--builder.gaslimit=30000000 \
27+
--rpc.max-connections=5000 \
28+
--p2p-secret-key /l2reth/nodekey \
29+
--engine.sync-at-startup false \
30+
--remote-source.enabled \
31+
--remote-source.url http://rollup-node-sequencer:8545 \
32+
--remote-source.poll-interval-ms 100 \
33+
--l1.url http://l1-node:8545 \
34+
--blob.mock

tests/src/docker_compose.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,17 @@ const RN_FOLLOWER_RPC_URL: &str = "http://localhost:8546";
4343
/// The l2geth node RPC URL for the Docker Compose environment.
4444
const L2GETH_SEQUENCER_RPC_URL: &str = "http://localhost:8547";
4545

46+
/// The l2geth follower node RPC URL for the Docker Compose environment.
4647
const L2GETH_FOLLOWER_RPC_URL: &str = "http://localhost:8548";
4748

48-
const RN_SEQUENCER_ENODE: &str= "enode://e7f7e271f62bd2b697add14e6987419758c97e83b0478bd948f5f2d271495728e7edef5bd78ad65258ac910f28e86928ead0c42ee51f2a0168d8ca23ba939766@{IP}:30303";
49+
/// The remote source node RPC URL for the Docker Compose environment.
50+
const RN_REMOTE_SOURCE_RPC_URL: &str = "http://localhost:8546";
51+
52+
/// The l2geth skip signer check node RPC URL for the Docker Compose environment.
53+
const L2GETH_SKIPSIGNERCHECK_RPC_URL: &str = "http://localhost:8547";
54+
55+
const RN_SEQUENCER_ENODE: &str = "enode://e7f7e271f62bd2b697add14e6987419758c97e83b0478bd948f5f2d271495728e7edef5bd78ad65258ac910f28e86928ead0c42ee51f2a0168d8ca23ba939766@{IP}:30303";
56+
const RN_REMOTE_SOURCE_ENODE: &str = "enode://849431bd98c23f8203cf475cfd8efb980d1e2af46337141cfb7dd960d4ae6f8c489846da293305705c8918fbf991e41805afc8c7231c96f3f938120b6826affc@{IP}:30303";
4957
const L2GETH_SEQUENCER_ENODE: &str = "enode://8fc4f6dfd0a2ebf56560d0b0ef5e60ad7bcb01e13f929eae53a4c77086d9c1e74eb8b8c8945035d25c6287afdd871f0d41b3fd7e189697decd0f13538d1ac620@{IP}:30303";
5058

5159
pub struct DockerComposeEnv {
@@ -83,6 +91,33 @@ impl DockerComposeEnv {
8391
Ok(env)
8492
}
8593

94+
/// Create a new DockerComposeEnv for remote block source tests and wait for all services to be
95+
/// ready
96+
pub async fn new_remote_source(test_name: &str) -> Result<Self> {
97+
let project_name = format!("test-{test_name}");
98+
let compose_file = "docker-compose.remote-source.yml";
99+
100+
tracing::info!("🚀 Starting test environment: {project_name}");
101+
102+
// Pre-cleanup existing containers to avoid conflicts
103+
Self::cleanup(compose_file, &project_name, false);
104+
105+
// Start the environment
106+
let env = Self::start_environment(compose_file, &project_name)?;
107+
108+
// Start streaming logs in the background
109+
let _ = Self::stream_container_logs(compose_file, &project_name).await;
110+
111+
// Wait for all services to be ready
112+
tracing::info!("⏳ Waiting for services to be ready...");
113+
Self::wait_for_l2_node_ready(RN_SEQUENCER_RPC_URL, 30).await?;
114+
Self::wait_for_l2_node_ready(RN_REMOTE_SOURCE_RPC_URL, 30).await?;
115+
Self::wait_for_l2_node_ready(L2GETH_SKIPSIGNERCHECK_RPC_URL, 30).await?;
116+
117+
tracing::info!("✅ All services are ready!");
118+
Ok(env)
119+
}
120+
86121
fn start_environment(compose_file: &str, project_name: &str) -> Result<Self> {
87122
tracing::info!("📦 Starting docker-compose services...");
88123

@@ -188,6 +223,21 @@ impl DockerComposeEnv {
188223
))
189224
}
190225

226+
/// Get a configured remote source provider
227+
pub async fn get_rn_remote_source_provider(&self) -> Result<NamedProvider> {
228+
let provider = ProviderBuilder::<_, _, Scroll>::default()
229+
.with_recommended_fillers()
230+
.connect(RN_REMOTE_SOURCE_RPC_URL)
231+
.await
232+
.map_err(|e| eyre::eyre!("Failed to connect to RN remote source: {}", e))?;
233+
Ok(NamedProvider::new(
234+
Box::new(provider),
235+
"RN Remote Source",
236+
"rollup-node-remote-source",
237+
RN_REMOTE_SOURCE_RPC_URL,
238+
))
239+
}
240+
191241
/// Get a configured l2geth sequencer provider
192242
pub async fn get_l2geth_sequencer_provider(&self) -> Result<NamedProvider> {
193243
let provider = ProviderBuilder::<_, _, Scroll>::default()
@@ -218,6 +268,21 @@ impl DockerComposeEnv {
218268
))
219269
}
220270

271+
/// Get a configured l2geth skip signer check provider
272+
pub async fn get_l2geth_skipsignercheck_provider(&self) -> Result<NamedProvider> {
273+
let provider = ProviderBuilder::<_, _, Scroll>::default()
274+
.with_recommended_fillers()
275+
.connect(L2GETH_SKIPSIGNERCHECK_RPC_URL)
276+
.await
277+
.map_err(|e| eyre::eyre!("Failed to connect to l2geth skip signer check: {}", e))?;
278+
Ok(NamedProvider::new(
279+
Box::new(provider),
280+
"L2Geth SkipSignerCheck",
281+
"l2geth-skipsignercheck",
282+
L2GETH_SKIPSIGNERCHECK_RPC_URL,
283+
))
284+
}
285+
221286
// ===== UTILITIES =====
222287

223288
/// Wait for L2 node to be ready
@@ -441,6 +506,13 @@ impl DockerComposeEnv {
441506
Ok(RN_SEQUENCER_ENODE.replace("{IP}", &ip))
442507
}
443508

509+
/// Get the rollup node remote source enode URL with resolved IP address
510+
pub fn rn_remote_source_enode(&self) -> Result<String> {
511+
let ip =
512+
self.get_container_ip(&self.get_full_container_name("rollup-node-remote-source"))?;
513+
Ok(RN_REMOTE_SOURCE_ENODE.replace("{IP}", &ip))
514+
}
515+
444516
/// Get the l2geth sequencer enode URL with resolved IP address
445517
pub fn l2geth_sequencer_enode(&self) -> Result<String> {
446518
let ip = self.get_container_ip(&self.get_full_container_name("l2geth-sequencer"))?;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use eyre::Result;
2+
use tests::*;
3+
4+
#[tokio::test]
5+
async fn docker_test_remote_block_source_basic() -> Result<()> {
6+
reth_tracing::init_test_tracing();
7+
8+
tracing::info!("=== STARTING docker_test_remote_block_source_basic ===");
9+
let env = DockerComposeEnv::new_remote_source("docker_test_remote_block_source_basic").await?;
10+
11+
let rn_sequencer = env.get_rn_sequencer_provider().await?;
12+
let rn_remote_source = env.get_rn_remote_source_provider().await?;
13+
let l2geth = env.get_l2geth_skipsignercheck_provider().await?;
14+
15+
utils::enable_automatic_sequencing(&rn_sequencer).await?;
16+
17+
let target_block = 10;
18+
utils::wait_for_block(&[&rn_sequencer], target_block).await?;
19+
let sequencer_block = rn_sequencer.get_block_number().await?;
20+
21+
utils::wait_for_block(&[&rn_remote_source], sequencer_block).await?;
22+
utils::assert_blocks_match(&[&rn_sequencer, &rn_remote_source], sequencer_block).await?;
23+
24+
utils::admin_add_peer(&l2geth, &env.rn_remote_source_enode()?).await?;
25+
utils::wait_for_block(&[&l2geth], sequencer_block).await?;
26+
utils::assert_blocks_match(&[&rn_remote_source, &l2geth], sequencer_block).await?;
27+
28+
let remote_block = rn_remote_source.get_block_number().await?;
29+
assert!(
30+
remote_block >= sequencer_block,
31+
"remote source should be at or ahead of sequencer: remote={}, sequencer={}",
32+
remote_block,
33+
sequencer_block
34+
);
35+
36+
Ok(())
37+
}
38+
39+
#[tokio::test]
40+
async fn docker_test_remote_block_source_recovery() -> Result<()> {
41+
reth_tracing::init_test_tracing();
42+
43+
tracing::info!("=== STARTING docker_test_remote_block_source_recovery ===");
44+
let env = DockerComposeEnv::new_remote_source("docker_test_remote_block_source_recovery").await?;
45+
46+
let rn_sequencer = env.get_rn_sequencer_provider().await?;
47+
let rn_remote_source = env.get_rn_remote_source_provider().await?;
48+
49+
utils::enable_automatic_sequencing(&rn_sequencer).await?;
50+
51+
let target_block = 10;
52+
utils::wait_for_block(&[&rn_sequencer], target_block).await?;
53+
let sequencer_block = rn_sequencer.get_block_number().await?;
54+
utils::wait_for_block(&[&rn_remote_source], sequencer_block).await?;
55+
56+
tracing::info!("Restarting rollup-node-sequencer container");
57+
env.restart_container(&rn_sequencer).await?;
58+
let rn_sequencer = env.get_rn_sequencer_provider().await?;
59+
utils::enable_automatic_sequencing(&rn_sequencer).await?;
60+
61+
let target_block = sequencer_block + 5;
62+
utils::wait_for_block(&[&rn_sequencer], target_block).await?;
63+
utils::wait_for_block(&[&rn_remote_source], target_block).await?;
64+
utils::assert_blocks_match(&[&rn_sequencer, &rn_remote_source], target_block).await?;
65+
66+
Ok(())
67+
}

0 commit comments

Comments
 (0)