Skip to content

Commit 55d8e8c

Browse files
authored
Bridge Implementation (#17)
1 parent a59549e commit 55d8e8c

30 files changed

+1119
-313
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ Cargo.lock
2323
# Added by cargo
2424

2525
/target
26+
27+
.vscode/

Cargo.toml

+21-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ exclude = [".github/"]
77

88
[workspace]
99
members = [
10+
"bin/bridge",
1011
"crates/engine",
1112
"crates/network",
1213
"crates/scroll-wire"
@@ -104,24 +105,41 @@ too_long_first_doc_paragraph = "allow"
104105
[workspace.dependencies]
105106
# alloy
106107
alloy-primitives = { version = "0.8.15", default-features = false }
108+
alloy-rpc-types-engine = { version = "0.9.2", default-features = false }
107109

108110
# reth
111+
reth-eth-wire-types = { git = "https://github.com/scroll-tech/reth.git" }
112+
reth-network = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
113+
reth-network-api = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
114+
reth-network-peers = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
109115
reth-primitives = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
116+
reth-provider = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
117+
reth-tasks = { git = "https://github.com/scroll-tech/reth.git" }
110118

111119
# reth-scroll
120+
reth-scroll-chainspec = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
121+
reth-scroll-node = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
112122
reth-scroll-primitives = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
113123

124+
# rollup node
125+
scroll-network = { path = "crates/network" }
126+
scroll-wire = { path = "crates/scroll-wire" }
127+
114128
# misc
115129
eyre = "0.6"
130+
futures = { version = "0.3", default-features = false }
131+
secp256k1 = { version = "0.29", default-features = false }
116132
serde = { version = "1.0", default-features = false }
117133
tokio = { version = "1.39", default-features = false }
134+
tokio-stream = { version = "0.1", default-features = false }
118135
tracing = "0.1.0"
119136

120137
[patch.crates-io]
121-
revm = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v53" }
122-
revm-primitives = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v53" }
123-
revm-interpreter = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v53" }
138+
revm = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v54" }
139+
revm-primitives = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v54" }
140+
revm-interpreter = { git = "https://github.com/scroll-tech/revm.git", branch = "scroll-evm-executor/reth/v54" }
124141

125142
ff = { git = "https://github.com/scroll-tech/ff", branch = "feat/sp1" }
126143

127144
alloy-eip2930 = { git = "https://github.com/scroll-tech/alloy-eips", branch = "v0.3.2" }
145+

bin/bridge/Cargo.toml

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
[package]
2+
name = "scroll-bridge"
3+
version.workspace = true
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
license.workspace = true
7+
exclude.workspace = true
8+
9+
[dependencies]
10+
# alloy
11+
alloy-consensus = { version = "0.9.2", default-features = false }
12+
13+
# scroll-alloy
14+
scroll-alloy-consensus = { git = "https://github.com/scroll-tech/reth.git" }
15+
scroll-alloy-rpc-types-engine= { git = "https://github.com/scroll-tech/reth.git" }
16+
17+
# reth
18+
reth-cli-util = { git = "https://github.com/scroll-tech/reth.git" }
19+
reth-eth-wire-types.workspace = true
20+
reth-network.workspace = true
21+
reth-network-peers.workspace = true
22+
reth-node-api = { git = "https://github.com/scroll-tech/reth.git", features = ["scroll"] }
23+
reth-node-builder = { git = "https://github.com/scroll-tech/reth.git", features = ["skip-state-root-validation"] }
24+
reth-node-core = { git = "https://github.com/scroll-tech/reth.git" }
25+
reth-node-types = { git = "https://github.com/scroll-tech/reth.git" }
26+
reth-primitives.workspace = true
27+
reth-provider = { workspace = true, features = ["scroll", "skip-state-root-validation"]}
28+
reth-transaction-pool = { git = "https://github.com/scroll-tech/reth.git" }
29+
30+
# scroll
31+
reth-scroll-chainspec.workspace = true
32+
reth-scroll-cli = { git = "https://github.com/scroll-tech/reth.git", features = ["scroll"] }
33+
reth-scroll-engine-primitives = { git = "https://github.com/scroll-tech/reth.git" }
34+
reth-scroll-node = { workspace = true, features = ["skip-state-root-validation"] }
35+
reth-scroll-primitives.workspace = true
36+
scroll-wire.workspace = true
37+
scroll-network.workspace = true
38+
39+
# misc
40+
clap = { version = "4", features = ["derive", "env"] }
41+
eyre.workspace = true
42+
secp256k1 = { workspace = true, features = [
43+
"global-context",
44+
"recovery",
45+
] }
46+
tokio = { workspace = true, features = ["full"] }
47+
tracing.workspace = true
48+
49+
[dev-dependencies]
50+
# alloy
51+
alloy-primitives = { workspace = true, default-features = false, features = [
52+
"map-foldhash",
53+
] }
54+
alloy-genesis = { version = "0.9.2", default-features = false }
55+
alloy-rpc-types-engine.workspace = true
56+
57+
# reth
58+
reth-e2e-test-utils = { git = "https://github.com/scroll-tech/reth.git" }
59+
reth-payload-builder = { git = "https://github.com/scroll-tech/reth.git" }
60+
reth-rpc-server-types = { git = "https://github.com/scroll-tech/reth.git" }
61+
reth-tasks.workspace = true
62+
reth-tracing = { git = "https://github.com/scroll-tech/reth.git" }
63+
64+
# misc
65+
serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] }
66+
67+
[features]
68+
test-utils = []
69+
70+
[[bin]]
71+
name = "bridge"
72+
path = "src/main.rs"

bin/bridge/assets/genesis.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"config":{"chainId":1,"homesteadBlock":0,"daoForkSupport":true,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"berlinBlock":0,"londonBlock":0,"arrowGlacierBlock":0,"grayGlacierBlock":0,"shanghaiTime":0,"cancunTime":0,"terminalTotalDifficulty":"0x0","terminalTotalDifficultyPassed":true},"nonce":"0x0","timestamp":"0x0","extraData":"0x00","gasLimit":"0x1c9c380","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"0x14dc79964da2c08b23698b3d3cc7ca32193d9955":{"balance":"0xd3c21bcecceda1000000"},"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65":{"balance":"0xd3c21bcecceda1000000"},"0x1cbd3b2770909d4e10f157cabc84c7264073c9ec":{"balance":"0xd3c21bcecceda1000000"},"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f":{"balance":"0xd3c21bcecceda1000000"},"0x2546bcd3c84621e976d8185a91a922ae77ecec30":{"balance":"0xd3c21bcecceda1000000"},"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc":{"balance":"0xd3c21bcecceda1000000"},"0x70997970c51812dc3a010c7d01b50e0d17dc79c8":{"balance":"0xd3c21bcecceda1000000"},"0x71be63f3384f5fb98995898a86b02fb2426c5788":{"balance":"0xd3c21bcecceda1000000"},"0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199":{"balance":"0xd3c21bcecceda1000000"},"0x90f79bf6eb2c4f870365e785982e1f101e93b906":{"balance":"0xd3c21bcecceda1000000"},"0x976ea74026e726554db657fa54763abd0c3a0aa9":{"balance":"0xd3c21bcecceda1000000"},"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc":{"balance":"0xd3c21bcecceda1000000"},"0x9c41de96b2088cdc640c6182dfcf5491dc574a57":{"balance":"0xd3c21bcecceda1000000"},"0xa0ee7a142d267c1f36714e4a8f75612f20a79720":{"balance":"0xd3c21bcecceda1000000"},"0xbcd4042de499d14e55001ccbb24a551f3b954096":{"balance":"0xd3c21bcecceda1000000"},"0xbda5747bfd65f08deb54cb465eb87d40e51b197e":{"balance":"0xd3c21bcecceda1000000"},"0xcd3b766ccdd6ae721141f452c550ca635964ce71":{"balance":"0xd3c21bcecceda1000000"},"0xdd2fd4581271e230360230f9337d5c0430bf44c0":{"balance":"0xd3c21bcecceda1000000"},"0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097":{"balance":"0xd3c21bcecceda1000000"},"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266":{"balance":"0xd3c21bcecceda1000000"},"0xfabb0ac9d68b0b445fb7357272ff202c5651694a":{"balance":"0xd3c21bcecceda1000000"}},"number":"0x0"}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[Unit]
2+
Description=Scroll Mainnet - Reth
3+
After=network.target
4+
StartLimitIntervalSec=0
5+
6+
[Service]
7+
Type=simple
8+
Restart=always
9+
RestartSec=1
10+
User=ubuntu
11+
Group=ubuntu
12+
UMask=0002
13+
EnvironmentFile=/etc/bridge.env
14+
ExecStart=/usr/local/bin/bridge node --chain scroll-mainnet --metrics 0.0.0.0:9001 --debug.etherscan https://api.scrollscan.com/api
15+
16+
[Install]
17+
WantedBy=multi-user.target

bin/bridge/src/import.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use reth_network::{
2+
import::{BlockImport as RethBlockImport, BlockValidation},
3+
NetworkPrimitives,
4+
};
5+
use reth_network_peers::PeerId;
6+
use reth_scroll_node::ScrollNetworkPrimitives;
7+
use reth_scroll_primitives::ScrollBlock;
8+
use scroll_wire::Event;
9+
use secp256k1::ecdsa::Signature;
10+
use std::{
11+
sync::Arc,
12+
task::{Context, Poll},
13+
};
14+
use tokio::sync::mpsc::UnboundedSender;
15+
use tracing::{trace, warn};
16+
17+
const ECDSA_SIGNATURE_LEN: usize = 64;
18+
19+
/// A block import implementation for the eth-wire protocol that sends block to the scroll-wire
20+
/// protocol.
21+
///
22+
/// The block import implementation delegates the block import to the inner block import and then
23+
/// sends the block to the scroll-wire protocol if the block is valid.
24+
#[derive(Debug)]
25+
pub struct BridgeBlockImport {
26+
/// A sender for sending events to the scroll-wire protocol.
27+
to_scroll_network_manager: UnboundedSender<Event>,
28+
/// The inner block import.
29+
inner: Box<dyn RethBlockImport<reth_scroll_primitives::ScrollBlock>>,
30+
}
31+
32+
impl BridgeBlockImport {
33+
/// Creates a new [`BridgeBlockImport`] instance with the provided events sender and inner block
34+
/// import.
35+
pub fn new(
36+
events: UnboundedSender<Event>,
37+
inner_block_import: Box<dyn RethBlockImport<reth_scroll_primitives::ScrollBlock>>,
38+
) -> Self {
39+
Self { to_scroll_network_manager: events, inner: inner_block_import }
40+
}
41+
42+
/// Bridges a new block from the eth-wire protocol to the scroll-wire protocol.
43+
fn bridge_new_block_to_scroll_wire(
44+
&self,
45+
peer_id: PeerId,
46+
block: Arc<reth_eth_wire_types::NewBlock<ScrollBlock>>,
47+
) {
48+
// We create a reference to the extra data of the incoming block.
49+
let extra_data = &block.block.extra_data;
50+
51+
// If we can extract a signature from the extra data we send the block to the scroll-wire
52+
// protocol. The signature is extracted from the last `ECDSA_SIGNATURE_LEN` bytes of the
53+
// extra data field.
54+
if let Some(signature) = extra_data
55+
.len()
56+
.checked_sub(ECDSA_SIGNATURE_LEN)
57+
.and_then(|i| Signature::from_compact(&extra_data[i..]).ok())
58+
{
59+
let block = block.block.clone();
60+
trace!(target: "bridge::import", peer_id = %peer_id, block = ?block, "Received new block from eth-wire protocol");
61+
62+
// We trigger a new block event to be sent to the rollup node's network manager. If this
63+
// results in an error it means the network manager has been dropped.
64+
let _ =
65+
self.to_scroll_network_manager.send(Event::NewBlock { peer_id, block, signature });
66+
} else {
67+
warn!(target: "bridge::import", peer_id = %peer_id, "Failed to extract signature from block extra data");
68+
}
69+
}
70+
}
71+
72+
impl RethBlockImport<reth_scroll_primitives::ScrollBlock> for BridgeBlockImport {
73+
/// This function is called when a new block is received from the network, it delegates the
74+
/// block import to the inner block import.
75+
fn on_new_block(
76+
&mut self,
77+
peer_id: PeerId,
78+
incoming_block: reth_network::message::NewBlockMessage<
79+
<ScrollNetworkPrimitives as NetworkPrimitives>::Block,
80+
>,
81+
) {
82+
// We then delegate the block import to the inner block import.
83+
self.inner.on_new_block(peer_id, incoming_block);
84+
}
85+
86+
/// This function is called when the block import is polled.
87+
///
88+
/// If the block import is ready we check if the block is valid and if it is we send the block
89+
/// to the scroll-wire protocol and then return the outcome.
90+
fn poll(
91+
&mut self,
92+
cx: &mut Context<'_>,
93+
) -> Poll<
94+
reth_network::import::BlockImportOutcome<
95+
<ScrollNetworkPrimitives as NetworkPrimitives>::Block,
96+
>,
97+
> {
98+
if let Poll::Ready(outcome) = self.inner.poll(cx) {
99+
match outcome.result {
100+
Ok(BlockValidation::ValidBlock { ref block }) |
101+
Ok(BlockValidation::ValidHeader { ref block }) => {
102+
self.bridge_new_block_to_scroll_wire(outcome.peer, block.block.clone());
103+
return Poll::Ready(outcome)
104+
}
105+
Err(_) => Poll::Ready(outcome),
106+
}
107+
} else {
108+
return Poll::Pending;
109+
}
110+
}
111+
}

bin/bridge/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//! Scroll Network Bridge Components.
2+
3+
mod import;
4+
pub use import::BridgeBlockImport;
5+
6+
mod network;
7+
pub use network::ScrollBridgeNetworkBuilder;

bin/bridge/src/main.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
mod network;
2+
use network::ScrollBridgeNetworkBuilder;
3+
4+
mod import;
5+
use import::BridgeBlockImport;
6+
7+
#[global_allocator]
8+
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
9+
10+
fn main() {
11+
use clap::Parser;
12+
use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher};
13+
use reth_provider::providers::BlockchainProvider;
14+
use reth_scroll_cli::{Cli, ScrollChainSpecParser, ScrollRollupArgs};
15+
use reth_scroll_node::{ScrollAddOns, ScrollNode};
16+
reth_cli_util::sigsegv_handler::install();
17+
18+
// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
19+
if std::env::var_os("RUST_BACKTRACE").is_none() {
20+
std::env::set_var("RUST_BACKTRACE", "1");
21+
}
22+
23+
if let Err(err) = Cli::<ScrollChainSpecParser, ScrollRollupArgs>::parse()
24+
.run::<_, _, ScrollNode>(|builder, _| async move {
25+
let engine_tree_config = TreeConfig::default()
26+
.with_persistence_threshold(builder.config().engine.persistence_threshold)
27+
.with_memory_block_buffer_target(
28+
builder.config().engine.memory_block_buffer_target,
29+
);
30+
let handle = builder
31+
.with_types_and_provider::<ScrollNode, BlockchainProvider<_>>()
32+
// Override the network builder with the `ScrollBridgeNetworkBuilder`
33+
.with_components(
34+
ScrollNode::components().network(ScrollBridgeNetworkBuilder::default()),
35+
)
36+
.with_add_ons(ScrollAddOns::default())
37+
.launch_with_fn(|builder| {
38+
let launcher = EngineNodeLauncher::new(
39+
builder.task_executor().clone(),
40+
builder.config().datadir(),
41+
engine_tree_config,
42+
);
43+
builder.launch_with(launcher)
44+
})
45+
.await?;
46+
47+
handle.node_exit_future.await
48+
})
49+
{
50+
eprintln!("Error: {err:?}");
51+
std::process::exit(1);
52+
}
53+
}

0 commit comments

Comments
 (0)