Skip to content

Commit c83f22b

Browse files
authored
feat: chain-replication testing (#225)
1 parent edb7d2f commit c83f22b

35 files changed

Lines changed: 18893 additions & 138 deletions

File tree

.gitignore

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ main_node/data
3232
follower_node/data
3333
follower_node/config
3434
follower_node/.env
35-
.local/
36-
.local_gateway_and_follower
37-
.local_main_node
38-
.local_main_node2
35+
36+
.local*
37+
3938
based/.envrc
4039
.envrc
40+
.env
4141

4242
based/data
4343
based/genesis.json

based/Cargo.toml

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ opt-level = 3
3131
alloy-consensus = "0.14.0"
3232
alloy-eips = "0.14.0"
3333
alloy-network = "0.14.0"
34-
alloy-primitives = { version = "1.0.0", default-features = false, features = ["getrandom"] }
34+
alloy-primitives = { version = "1.0.0", default-features = false, features = [
35+
"getrandom",
36+
] }
3537
alloy-provider = "0.14.0"
3638
alloy-rlp = "0.3.11"
3739
alloy-rpc-types = { version = "0.14.0", features = ["engine"] }
@@ -71,11 +73,15 @@ kona-rpc = { git = "https://github.com/op-rs/kona", rev = "88c80aa46975d196c711d
7173
mio = { features = ["net", "os-poll"], version = "1.0.4" }
7274
mio_httpc = { features = ["native"], version = "0.10.6" }
7375
moka = "0.12.10"
74-
op-alloy-consensus = { version = "=0.14.1", default-features = false, features = ["k256"] }
76+
op-alloy-consensus = { version = "=0.14.1", default-features = false, features = [
77+
"k256",
78+
] }
7579
op-alloy-network = "0.14.1"
7680
op-alloy-rpc-types = "0.14.1"
7781
op-alloy-flz = { version = "0.13.0", default-features = false }
78-
op-alloy-rpc-types-engine = { version = "0.14.1", default-features = false, features = ["serde"] }
82+
op-alloy-rpc-types-engine = { version = "0.14.1", default-features = false, features = [
83+
"serde",
84+
] }
7985
op-revm = "3.0.2"
8086
parking_lot = "0.12.3"
8187
paste = "0.1.18"
@@ -91,7 +97,9 @@ reth-db-api = { path = "../reth/crates/storage/db-api" }
9197
reth-db-common = { path = "../reth/crates/storage/db-common" }
9298
reth-evm = { path = "../reth/crates/evm" }
9399
reth-execution-errors = { path = "../reth/crates/evm/execution-errors" }
94-
reth-node-ethereum = { path = "../reth/crates/ethereum/node", features = ["test-utils"] }
100+
reth-node-ethereum = { path = "../reth/crates/ethereum/node", features = [
101+
"test-utils",
102+
] }
95103
reth-node-types = { path = "../reth/crates/node/types" }
96104
reth-optimism-chainspec = { path = "../reth/crates/optimism/chainspec" }
97105
reth-optimism-cli = { path = "../reth/crates/optimism/cli" }
@@ -103,22 +111,32 @@ reth-optimism-primitives = { path = "../reth/crates/optimism/primitives" }
103111
reth-optimism-payload-builder = { path = "../reth/crates/optimism/payload" }
104112
reth-primitives = { path = "../reth/crates/primitives" }
105113
reth-primitives-traits = { path = "../reth/crates/primitives-traits" }
106-
reth-provider = { path = "../reth/crates/storage/provider", features = ["test-utils"] }
114+
reth-provider = { path = "../reth/crates/storage/provider", features = [
115+
"test-utils",
116+
] }
107117
reth-revm = { path = "../reth/crates/revm" }
108118
reth-rpc-builder = { path = "../reth/crates/rpc/rpc-builder" }
109119
reth-rpc-layer = { path = "../reth/crates/rpc/rpc-layer" }
110120
reth-stages-types = { path = "../reth/crates/stages/types" }
111121
reth-storage-api = { path = "../reth/crates/storage/storage-api" }
112122
reth-storage-errors = { path = "../reth/crates/storage/errors" }
113123
reth-trie = { path = "../reth/crates/trie/trie" }
114-
reth-trie-common = { path = "../reth/crates/trie/common", features = ["test-utils"] }
124+
reth-trie-common = { path = "../reth/crates/trie/common", features = [
125+
"test-utils",
126+
] }
115127
reth-trie-db = { path = "../reth/crates/trie/db" }
116128
reth-trie-parallel = { path = "../reth/crates/trie/parallel" }
117-
revm = { version = "22.0.1", features = ["optional_balance_check", "secp256k1", "std"], default-features = false }
129+
revm = { version = "22.0.1", features = [
130+
"optional_balance_check",
131+
"secp256k1",
132+
"std",
133+
], default-features = false }
118134
revm-handler = "3.0.0"
119135
revm-inspector = "3.0.0"
120136
revm-interpreter = "18.0.0"
121-
revm-primitives = { version = "18.0.0", features = ["std"], default-features = false }
137+
revm-primitives = { version = "18.0.0", features = [
138+
"std",
139+
], default-features = false }
122140
rustc-hash = "2.0.0"
123141
serde = { version = "1.0.217", features = ["derive"] }
124142
serde_json = "1.0.137"

based/bin/gateway/src/main.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bop_common::{
1010
time::Duration,
1111
utils::{init_tracing, wait_for_signal},
1212
};
13-
use bop_db::{DatabaseRead, init_database};
13+
use bop_db::{DatabaseRead, DatabaseWrite as _, init_database};
1414
use bop_metrics::MetricsConsumer;
1515
use bop_rpc::{gossiper::Gossiper, start_rpc};
1616
use bop_sequencer::{
@@ -20,7 +20,7 @@ use bop_sequencer::{
2020
use clap::Parser;
2121
use revm_primitives::B256;
2222
use tokio::runtime::Runtime;
23-
use tracing::{error, info};
23+
use tracing::{error, info, warn};
2424

2525
fn main() {
2626
if std::env::var_os("RUST_BACKTRACE").is_none() {
@@ -67,6 +67,15 @@ fn run(args: GatewayArgs) -> eyre::Result<()> {
6767
let evm_config = sequencer_config.evm_config.clone();
6868
let (frag_broadcast_tx, _) = tokio::sync::broadcast::channel(10_000);
6969

70+
if let Some(ref range) = args.replay_blocks_range {
71+
warn!(?range, "Replay range found. Performing DB rollback if necessary");
72+
// Example: if start is 1000, then we should rollback to 998
73+
while db_bop.head_block_number()? > range.start().saturating_sub(2) {
74+
db_bop.roll_back_head()?;
75+
}
76+
info!(db_block = db_bop.head_block_number()?, db_hash = ?db_bop.head_block_hash()?, "New database head");
77+
}
78+
7079
let sequencer_vsync_window = Duration::from_micros(args.vsync_window_us as u64);
7180
let simulator_vsync_window = Duration::from_micros(args.vsync_window_us as u64);
7281

based/crates/common/src/communication/messages.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pub enum EngineApi {
166166
NewPayloadV4 { payload: OpExecutionPayloadV4, versioned_hashes: Vec<B256>, parent_beacon_block_root: B256 },
167167
GetPayloadV4 { payload_id: PayloadId, res: oneshot::Sender<OpExecutionPayloadEnvelopeV4Patch> },
168168
}
169+
169170
impl EngineApi {
170171
pub fn messages_from_block(
171172
block: &BlockSyncMessage,

based/crates/common/src/config.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{net::Ipv4Addr, path::PathBuf, sync::Arc, str::FromStr};
1+
use std::{net::Ipv4Addr, ops::RangeInclusive, path::PathBuf, str::FromStr, sync::Arc};
22

33
use alloy_rpc_types::engine::JwtSecret;
44
use clap::Parser;
@@ -40,9 +40,6 @@ pub struct GatewayArgs {
4040
pub rpc_port_ws: u16,
4141
#[arg(long = "rpc.jwt")]
4242
pub rpc_jwt: String,
43-
/// PortalApi RPC URL
44-
#[arg(long = "portal_rpc.url", default_value = "http://localhost:9998")]
45-
pub portal_rpc_url: Url,
4643
/// Url to an L2 eth api rpc
4744
#[arg(long = "eth_client.url", default_value = "http://localhost:8545")]
4845
pub eth_client_url: Url,
@@ -77,6 +74,11 @@ pub struct GatewayArgs {
7774
/// Mock mode
7875
#[arg(long = "mock")]
7976
pub mock: Option<MockMode>,
77+
/// For chain replication testing, it expresses the range of blocks we want to replay. In
78+
/// particular, assuming the local eth client is at height N <= replay_target, the
79+
/// chain-replication will happen for range N..=replay_target.
80+
#[clap(long = "chain-replay.blocks-range", value_parser = range_inclusive_from_str)]
81+
pub replay_blocks_range: Option<RangeInclusive<u64>>,
8082
/// Enable DEBUG logging
8183
#[arg(long = "debug")]
8284
pub debug: bool,
@@ -202,3 +204,22 @@ impl From<&GatewayArgs> for LoggingConfig {
202204
}
203205
}
204206
}
207+
208+
fn range_inclusive_from_str(s: &str) -> Result<RangeInclusive<u64>, &'static str> {
209+
let parts = s.split("..=").collect::<Vec<_>>();
210+
if parts.len() != 2 {
211+
return Err("Range must be in the format 'start..=end'");
212+
}
213+
214+
let Ok(start) = parts[0].parse::<u64>() else {
215+
return Err("Invalid start of range");
216+
};
217+
let Ok(end) = parts[1].parse::<u64>() else {
218+
return Err("Invalid end of range");
219+
};
220+
if start > end {
221+
return Err("Start of range must be less than or equal to end");
222+
}
223+
224+
Ok(start..=end)
225+
}

based/crates/rpc/src/gossiper.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ impl Gossiper {
3535
let signed = msg.to_signed(&self.signer);
3636
let payload = signed.to_json();
3737

38-
if matches!(signed.message, p2p::VersionedMessage::FragV0(_)) {
39-
if let Err(e) = self.frag_broadcast.send(signed) {
40-
tracing::debug!(" broadcast of frag failed {e}")
41-
}
38+
if matches!(signed.message, p2p::VersionedMessage::FragV0(_)) && self.frag_broadcast.send(signed).is_err() {
39+
tracing::debug!("broadcast of frag failed")
4240
}
4341

44-
let Ok(res) = self.client.post(self.target_rpc.clone()).json(&payload).send() else {
45-
tracing::error!("couldn't send {}", payload);
46-
return;
42+
let res = match self.client.post(self.target_rpc.clone()).json(&payload).send() {
43+
Ok(res) => res,
44+
Err(e) => {
45+
tracing::error!(?e, target = ?self.target_rpc.clone(), "couldn't send payload: {}", payload);
46+
return;
47+
}
4748
};
4849

4950
let code = res.status();

based/crates/sequencer/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bop_common::{
1010
actor::Actor,
1111
communication::{
1212
Connections, ReceiversSpine, SendersSpine, SpineConnections, TrackedSenders,
13-
messages::{self, BlockFetch, BlockSyncError, EngineApi, SimulatorToSequencer, SimulatorToSequencerMsg},
13+
messages::{BlockFetch, BlockSyncError, EngineApi, SimulatorToSequencer, SimulatorToSequencerMsg},
1414
},
1515
custom_v4::OpExecutionPayloadEnvelopeV4Patch,
1616
db::DatabaseWrite,
@@ -133,7 +133,7 @@ where
133133
});
134134

135135
// handle engine API messages from rpc
136-
connections.receive_for(Duration::from_millis(10), |msg: messages::EngineApi, senders| {
136+
connections.receive_for(Duration::from_millis(10), |msg, senders| {
137137
let state = std::mem::take(&mut self.state);
138138
let cur_seq_state = telemetry::system::SequencerState::from(&state);
139139
self.state = state.handle_engine_api(msg, &mut self.data, senders);

chain-replay/.env.example

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
BOP_REPLAY_BLOCKS_RANGE=2000..=2500
2+
BOP_REPLAY_CHAIN_NAME=based-op-sepolia
3+
BOP_REPLAY_FOLLOWER_NODES_COUNT=2
4+
5+
BOP_REPLAY_GATEWAY_PORT=29997
6+
BOP_REPLAY_GATEWAY_AUTH_URL=http://127.0.0.1:29997
7+
BOP_REPLAY_GATEWAY_AUTH_JWT=bfea64567a978df0bcd973699554671aa20b20820bc40ba45dfed46025e2fa10
8+
BOP_REPLAY_GATEWAY_URL=http://127.0.0.1:29998
9+
BOP_REPLAY_GATEWAY_PORT_NO_AUTH=29998
10+
BOP_REPLAY_GATEWAY_SEQUENCING_KEY=42241483bfabbedc7c9d0d466c22c0820c40c886ddbe5b10a60308a032baac58
11+
12+
BOP_REPLAY_KONA_SEQUENCER_GOSSIP_PORT=22303
13+
BOP_REPLAY_KONA_SEQUENCER_DISCOVERY_PORT=22304
14+
BOP_REPLAY_P2P_SEQUENCER_KEY=0x377d4db65cb871eeca771ce5c9db54e90e7413234d2f094acc13ac2d428dbf35
15+
BOP_REPLAY_ROLLUP_FILE_PATH=../../.local_gateway_and_follower_based-op-sepolia/config/rollup.json
16+
17+
BOP_REPLAY_L1_BEACON_RPC_URL=http://34.194.193.217:5052
18+
BOP_REPLAY_L1_RPC_URL=http://34.194.193.217:8545
19+
BOP_REPLAY_L2_EL_VERIFIER_URL=http://18.185.199.51:8545
20+
21+
BOP_REPLAY_BASED_OP_GETH_0_ENGINE_RPC_PORT=28651
22+
BOP_REPLAY_BASED_OP_GETH_0_GOSSIP_PORT=21303
23+
BOP_REPLAY_BASED_OP_GETH_0_RPC_PORT=28645
24+
BOP_REPLAY_BASED_OP_GETH_0_WS_PORT=28646
25+
BOP_REPLAY_BASED_OP_GETH_0_METRICS_PORT=28010
26+
BOP_REPLAY_BASED_OP_NODE_0_GOSSIP_PORT=29103
27+
BOP_REPLAY_BASED_OP_NODE_0_RPC_PORT=28547
28+
BOP_REPLAY_BASED_OP_NODE_0_METRICS_PORT=28011
29+
30+
BOP_REPLAY_BASED_OP_GETH_1_ENGINE_RPC_PORT=28751
31+
BOP_REPLAY_BASED_OP_GETH_1_GOSSIP_PORT=21403
32+
BOP_REPLAY_BASED_OP_GETH_1_RPC_PORT=28745
33+
BOP_REPLAY_BASED_OP_GETH_1_WS_PORT=28746
34+
BOP_REPLAY_BASED_OP_GETH_1_METRICS_PORT=28110
35+
BOP_REPLAY_BASED_OP_NODE_1_GOSSIP_PORT=29203
36+
BOP_REPLAY_BASED_OP_NODE_1_RPC_PORT=28647
37+
BOP_REPLAY_BASED_OP_NODE_1_METRICS_PORT=28111
38+
39+
BOP_REPLAY_METRICS_EXPORTER_PORT=29000
40+
BOP_REPLAY_PROMETHEUS_PORT=29090
41+
BOP_REPLAY_GRAFANA_PORT=23000
42+
43+
BOP_REPLAY_REGISTRY_PORT=28082
44+
BOP_REPLAY_PORTAL=http://127.0.0.1:28081
45+
46+
BOP_REPLAY_RUST_LOG=bop_common=debug,bop_db=debug,bop_metrics=debug,bop_pool=debug,bop_rpc=debug,bop_sequencer=debug,bop_gateway=debug,bop_metrics_exporter=debug,gateway_registry=debug
47+
RUST_LOG=info,chain_replay=debug,discv5::service=error

0 commit comments

Comments
 (0)