Skip to content

Commit

Permalink
Merge pull request #7 from taikoxyz/multiple_l2
Browse files Browse the repository at this point in the history
Multiple L2s
  • Loading branch information
Brechtpd authored Oct 24, 2024
2 parents e1f6420 + 6476cb1 commit 0b0cdda
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 59 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ Currency: ETH
Block explorer: http://127.0.0.1:64003
```

```
chain_id: 167011
name: Gwyneth-2
rpc: http://127.0.0.1:32006
Currency: ETH
Block explorer: http://127.0.0.1:64005
```

```
chain_id: 160010
name: Gwyneth L1
Expand All @@ -35,6 +43,16 @@ Add test accounts that have some ETH to play with:

Rabby/Brave wallet works, but some issues with nonces so you may have to manually input the correct nonce.

# How to add extra layer2s ?

In order to add extra layer 2 networks, you need to increase the the `NUM_L2_CHAINS` in the main function [here](https://github.com/taikoxyz/gwyneth/blob/5f6d3a6ffcf0ea359e4c52bbd94a251aef28b54c/bin/reth/src/main.rs#L15). (Later on it will be a configurational setting - no code !)

If you want infrastructure support too, namingly:

1. Exposing the jspn rpc port to the host machine (since everything is running in Docker with Kurtosis), you need to specify as a config param like [here](https://github.com/taikoxyz/gwyneth/blob/5f6d3a6ffcf0ea359e4c52bbd94a251aef28b54c/packages/protocol/scripts/confs/network_params.yaml#L6). (By default, if you dont specify this param, the first Layer2 port - which is 10110 - will be exposed to the host anyways. You only need to add this param if you are exposing more than 1 ports to the outter world.)

Check failure on line 52 in README.md

View workflow job for this annotation

GitHub Actions / codespell

outter ==> outer
2. Blockscout support: [Here](https://github.com/taikoxyz/gwyneth/blob/5f6d3a6ffcf0ea359e4c52bbd94a251aef28b54c/packages/protocol/scripts/confs/network_params.yaml#L16) you can see a pattern, how to shoot up blockscout service too. If you want 3 layer2 explorers, just use the service name `blockscout_l2_3`.


# reth

[![CI status](https://github.com/paradigmxyz/reth/workflows/unit/badge.svg)][gh-ci]
Expand Down
63 changes: 38 additions & 25 deletions bin/reth/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![allow(missing_docs)]

// We use jemalloc for performance reasons.
#[cfg(all(feature = "jemalloc", unix))]
#[global_allocator]
Expand All @@ -12,44 +11,58 @@ use reth_node_builder::{NodeBuilder, NodeConfig, NodeHandle};
use reth_node_ethereum::EthereumNode;
use reth_tasks::TaskManager;

const BASE_CHAIN_ID: u64 = gwyneth::exex::BASE_CHAIN_ID; // Base chain ID for L2s
const NUM_L2_CHAINS: u64 = 2; // Number of L2 chains to create. Todo: Shall come from config */

fn main() -> eyre::Result<()> {
reth::cli::Cli::parse_args().run(|builder, _| async move {
let tasks = TaskManager::current();
let exec = tasks.executor();

let network_config = NetworkArgs {
discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
..NetworkArgs::default()
};

let chain_spec = ChainSpecBuilder::default()
.chain(gwyneth::exex::CHAIN_ID.into())
.genesis(
serde_json::from_str(include_str!(
"../../../crates/ethereum/node/tests/assets/genesis.json"
))
.unwrap(),
)
.cancun_activated()
.build();
let mut gwyneth_nodes = Vec::new();

for i in 0..NUM_L2_CHAINS {
let chain_id = BASE_CHAIN_ID + i; // Increment by 1 for each L2

let node_config = NodeConfig::test()
.with_chain(chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_static_l2_rpc_ip_and_port(chain_spec.chain.id()));
let chain_spec = ChainSpecBuilder::default()
.chain(chain_id.into())
.genesis(
serde_json::from_str(include_str!(
"../../../crates/ethereum/node/tests/assets/genesis.json"
))
.unwrap(),
)
.cancun_activated()
.build();

let NodeHandle { node: gwyneth_node, node_exit_future: _ } =
NodeBuilder::new(node_config.clone())
.gwyneth_node(exec.clone(), chain_spec.chain.id())
.node(GwynethNode::default())
.launch()
.await?;
let node_config = NodeConfig::test()
.with_chain(chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(
RpcServerArgs::default()
.with_unused_ports()
.with_static_l2_rpc_ip_and_port(chain_id)
);

let NodeHandle { node: gwyneth_node, node_exit_future: _ } =
NodeBuilder::new(node_config.clone())
.gwyneth_node(exec.clone(), chain_spec.chain.id())
.node(GwynethNode::default())
.launch()
.await?;

gwyneth_nodes.push(gwyneth_node);
}

let handle = builder
.node(EthereumNode::default())
.install_exex("Rollup", move |ctx| async {
Ok(gwyneth::exex::Rollup::new(ctx, gwyneth_node).await?.start())
Ok(gwyneth::exex::Rollup::new(ctx, gwyneth_nodes).await?.start())
})
.launch()
.await?;
Expand All @@ -69,4 +82,4 @@ mod tests {
#[command(flatten)]
args: T,
}
}
}
14 changes: 10 additions & 4 deletions crates/gwyneth/src/engine_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use reth_rpc_types::{
use std::{marker::PhantomData, net::Ipv4Addr};
use reth_rpc_builder::constants;

use crate::exex::BASE_CHAIN_ID;

/// Helper for engine api operations
pub struct EngineApiContext<E> {
pub canonical_stream: CanonStateNotificationStream,
Expand Down Expand Up @@ -119,12 +121,16 @@ pub trait RpcServerArgsExEx {
impl RpcServerArgsExEx for RpcServerArgs {
fn with_static_l2_rpc_ip_and_port(mut self, chain_id: u64) -> Self {
self.http = true;
// On the instance the program is running, we wanna have 10111 exposed as the (exex) L2's
// RPC port.
self.http_addr = Ipv4Addr::new(0, 0, 0, 0).into();
self.http_port = 10110u16;
self.ws_port = 10111u16;

// Calculate HTTP and WS ports based on chain_id
let port_offset = (chain_id - BASE_CHAIN_ID) as u16;
self.http_port = 10110 + (port_offset * 100);
self.ws_port = 10111 + (port_offset * 100);

// Set IPC path
self.ipcpath = format!("{}-{}", constants::DEFAULT_IPC_ENDPOINT, chain_id);

self
}
}
70 changes: 41 additions & 29 deletions crates/gwyneth/src/exex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use reth_transaction_pool::{
use RollupContract::{BlockProposed, RollupContractEvents};

const ROLLUP_CONTRACT_ADDRESS: Address = address!("9fCF7D13d10dEdF17d0f24C62f0cf4ED462f65b7");
pub const CHAIN_ID: u64 = 167010;
pub const BASE_CHAIN_ID: u64 = 167010;
const INITIAL_TIMESTAMP: u64 = 1710338135;

pub type GwynethFullNode = FullNode<
Expand Down Expand Up @@ -69,47 +69,44 @@ sol!(RollupContract, "TaikoL1.json");

pub struct Rollup<Node: reth_node_api::FullNodeComponents> {
ctx: ExExContext<Node>,
node: GwynethFullNode,
engine_api: EngineApiContext<GwynethEngineTypes>,
nodes: Vec<GwynethFullNode>,
engine_apis: Vec<EngineApiContext<GwynethEngineTypes>>,
}

impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
pub async fn new(ctx: ExExContext<Node>, node: GwynethFullNode) -> eyre::Result<Self> {
let engine_api = EngineApiContext {
engine_api_client: node.auth_server_handle().http_client(),
canonical_stream: node.provider.canonical_state_stream(),
_marker: PhantomData::<GwynethEngineTypes>,
};
Ok(Self { ctx, node, /* payload_event_stream, */ engine_api })
pub async fn new(ctx: ExExContext<Node>, nodes: Vec<GwynethFullNode>) -> eyre::Result<Self> {
let mut engine_apis = Vec::new();
for node in &nodes {
let engine_api = EngineApiContext {
engine_api_client: node.auth_server_handle().http_client(),
canonical_stream: node.provider.canonical_state_stream(),
_marker: PhantomData::<GwynethEngineTypes>,
};
engine_apis.push(engine_api);
}
Ok(Self { ctx, nodes, /* payload_event_stream, */ engine_apis })
}

pub async fn start(mut self) -> eyre::Result<()> {
// Process all new chain state notifications
while let Some(notification) = self.ctx.notifications.recv().await {
if let Some(reverted_chain) = notification.reverted_chain() {
self.revert(&reverted_chain)?;
}

if let Some(committed_chain) = notification.committed_chain() {
self.commit(&committed_chain).await?;
for i in 0..self.nodes.len() {
self.commit(&committed_chain, i).await?;
}
self.ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?;
}
}

Ok(())
}

/// Process a new chain commit.
///
/// This function decodes all transactions to the rollup contract into events, executes the
/// corresponding actions and inserts the results into the database.
pub async fn commit(&mut self, chain: &Chain) -> eyre::Result<()> {
pub async fn commit(&mut self, chain: &Chain, node_idx: usize) -> eyre::Result<()> {
let events = decode_chain_into_rollup_events(chain);
for (block, _, event) in events {
// TODO: Don't emit ProposeBlock event but directely
// read the function call RollupContractCalls to extract Txs
// let _call = RollupContractCalls::abi_decode(tx.input(), true)?;

if let RollupContractEvents::BlockProposed(BlockProposed {
blockId: block_number,
meta,
Expand All @@ -120,6 +117,19 @@ impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
let transactions: Vec<TransactionSigned> = decode_transactions(&meta.txList);
println!("transactions: {:?}", transactions);

let all_transactions: Vec<TransactionSigned> = decode_transactions(&meta.txList);
let node_chain_id = BASE_CHAIN_ID + (node_idx as u64);

let filtered_transactions: Vec<TransactionSigned> = all_transactions
.into_iter()
.filter(|tx| tx.chain_id() == Some(node_chain_id))
.collect();

if filtered_transactions.len() == 0 {
println!("no transactions for chain: {}", node_chain_id);
continue;
}

let attrs = GwynethPayloadAttributes {
inner: EthPayloadAttributes {
timestamp: block.timestamp,
Expand All @@ -128,7 +138,7 @@ impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
withdrawals: Some(vec![]),
parent_beacon_block_root: Some(B256::ZERO),
},
transactions: Some(transactions.clone()),
transactions: Some(filtered_transactions.clone()),
gas_limit: None,
};

Expand All @@ -148,15 +158,16 @@ impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
let payload_id = builder_attrs.inner.payload_id();
let parrent_beacon_block_root =
builder_attrs.inner.parent_beacon_block_root.unwrap();

// trigger new payload building draining the pool
self.node.payload_builder.new_payload(builder_attrs).await.unwrap();
self.nodes[node_idx].payload_builder.new_payload(builder_attrs).await.unwrap();

// wait for the payload builder to have finished building
let mut payload =
EthBuiltPayload::new(payload_id, SealedBlock::default(), U256::ZERO);
loop {
let result = self.node.payload_builder.best_payload(payload_id).await;
let result = self.nodes[node_idx].payload_builder.best_payload(payload_id).await;

// TODO: There seems to be no result when there's an empty tx list
if let Some(result) = result {
if let Ok(new_payload) = result {
payload = new_payload;
Expand All @@ -174,11 +185,12 @@ impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
}
break;
}

// trigger resolve payload via engine api
self.engine_api.get_payload_v3_value(payload_id).await?;
self.engine_apis[node_idx].get_payload_v3_value(payload_id).await?;

// submit payload to engine api
let block_hash = self
.engine_api
let block_hash = self.engine_apis[node_idx]
.submit_payload(
payload.clone(),
parrent_beacon_block_root,
Expand All @@ -188,7 +200,7 @@ impl<Node: reth_node_api::FullNodeComponents> Rollup<Node> {
.await?;

// trigger forkchoice update via engine api to commit the block to the blockchain
self.engine_api.update_forkchoice(block_hash, block_hash).await?;
self.engine_apis[node_idx].update_forkchoice(block_hash, block_hash).await?;
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/protocol/scripts/confs/network_params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ participants:
el_image: taiko_reth
cl_type: lighthouse
cl_image: sigp/lighthouse:latest
el_extra_params: ["--num_of_l2s", "2"]
cl_extra_params: [--always-prepare-payload, --prepare-payload-lookahead, "12000"]
- el_type: reth
el_image: taiko_reth
cl_type: teku
cl_image: consensys/teku:latest
el_extra_params: ["--num_of_l2s", "2"]
network_params:
network_id: '160010'
additional_services:
- blockscout
- blockscout_l2_1
- blockscout_l2_2
port_publisher:
nat_exit_ip: KURTOSIS_IP_ADDR_PLACEHOLDER
el:
Expand Down

0 comments on commit 0b0cdda

Please sign in to comment.