From 831d5a74c8a150ec50abda6a98bafc9d8fcf1305 Mon Sep 17 00:00:00 2001 From: Alexey Shekhirin Date: Mon, 15 Jul 2024 11:15:15 +0100 Subject: [PATCH] example(exex): move to a separate repo (#9500) --- Cargo.lock | 183 +------ Cargo.toml | 3 - book/developers/exex/remote.md | 2 +- examples/README.md | 8 +- examples/exex/discv5/Cargo.toml | 33 -- examples/exex/discv5/src/exex/mod.rs | 70 --- examples/exex/discv5/src/main.rs | 29 -- examples/exex/discv5/src/network/cli_ext.rs | 15 - examples/exex/discv5/src/network/mod.rs | 123 ----- examples/exex/in-memory-state/Cargo.toml | 22 - examples/exex/in-memory-state/src/main.rs | 148 ------ examples/exex/minimal/Cargo.toml | 22 - examples/exex/minimal/src/main.rs | 93 ---- examples/exex/op-bridge/Cargo.toml | 28 - .../op-bridge/l1_standard_bridge_abi.json | 1 - examples/exex/op-bridge/src/main.rs | 425 ---------------- examples/exex/rollup/Cargo.toml | 39 -- examples/exex/rollup/rollup_abi.json | 1 - examples/exex/rollup/src/db.rs | 460 ----------------- examples/exex/rollup/src/execution.rs | 481 ------------------ examples/exex/rollup/src/main.rs | 276 ---------- 21 files changed, 3 insertions(+), 2459 deletions(-) delete mode 100644 examples/exex/discv5/Cargo.toml delete mode 100644 examples/exex/discv5/src/exex/mod.rs delete mode 100644 examples/exex/discv5/src/main.rs delete mode 100644 examples/exex/discv5/src/network/cli_ext.rs delete mode 100644 examples/exex/discv5/src/network/mod.rs delete mode 100644 examples/exex/in-memory-state/Cargo.toml delete mode 100644 examples/exex/in-memory-state/src/main.rs delete mode 100644 examples/exex/minimal/Cargo.toml delete mode 100644 examples/exex/minimal/src/main.rs delete mode 100644 examples/exex/op-bridge/Cargo.toml delete mode 100644 examples/exex/op-bridge/l1_standard_bridge_abi.json delete mode 100644 examples/exex/op-bridge/src/main.rs delete mode 100644 examples/exex/rollup/Cargo.toml delete mode 100644 examples/exex/rollup/rollup_abi.json delete mode 100644 examples/exex/rollup/src/db.rs delete mode 100644 examples/exex/rollup/src/execution.rs delete mode 100644 examples/exex/rollup/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 747a4b74f..52184d98f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -570,7 +570,6 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" dependencies = [ - "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", @@ -589,13 +588,11 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" dependencies = [ - "alloy-json-abi", "const-hex", "dunce", "heck 0.5.0", "proc-macro2", "quote", - "serde_json", "syn 2.0.71", "syn-solidity", ] @@ -616,7 +613,6 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", @@ -2443,7 +2439,7 @@ dependencies = [ "enr", "fnv", "futures", - "hashlink 0.8.4", + "hashlink", "hex", "hkdf", "lazy_static", @@ -2871,115 +2867,6 @@ dependencies = [ "reth-rpc-types", ] -[[package]] -name = "example-exex-discv5" -version = "0.0.0" -dependencies = [ - "clap", - "discv5", - "enr", - "eyre", - "futures", - "futures-util", - "reth", - "reth-chainspec", - "reth-discv5", - "reth-exex", - "reth-exex-test-utils", - "reth-network-peers", - "reth-node-api", - "reth-node-ethereum", - "reth-testing-utils", - "reth-tracing", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "example-exex-in-memory-state" -version = "0.0.0" -dependencies = [ - "eyre", - "reth", - "reth-execution-types", - "reth-exex", - "reth-exex-test-utils", - "reth-node-api", - "reth-node-ethereum", - "reth-testing-utils", - "reth-tracing", - "tokio", -] - -[[package]] -name = "example-exex-minimal" -version = "0.0.0" -dependencies = [ - "eyre", - "futures", - "reth", - "reth-execution-types", - "reth-exex", - "reth-exex-test-utils", - "reth-node-api", - "reth-node-ethereum", - "reth-tracing", - "tokio", -] - -[[package]] -name = "example-exex-op-bridge" -version = "0.0.0" -dependencies = [ - "alloy-sol-types", - "eyre", - "futures", - "rand 0.8.5", - "reth", - "reth-execution-types", - "reth-exex", - "reth-exex-test-utils", - "reth-node-api", - "reth-node-ethereum", - "reth-primitives", - "reth-testing-utils", - "reth-tracing", - "rusqlite", - "tempfile", - "tokio", -] - -[[package]] -name = "example-exex-rollup" -version = "0.0.0" -dependencies = [ - "alloy-consensus", - "alloy-genesis", - "alloy-rlp", - "alloy-sol-types", - "eyre", - "foundry-blob-explorers", - "once_cell", - "reth", - "reth-chainspec", - "reth-execution-errors", - "reth-execution-types", - "reth-exex", - "reth-node-api", - "reth-node-ethereum", - "reth-primitives", - "reth-provider", - "reth-revm", - "reth-testing-utils", - "reth-tracing", - "rusqlite", - "secp256k1", - "serde_json", - "tokio", -] - [[package]] name = "example-manual-p2p" version = "0.0.0" @@ -3110,18 +2997,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fast-float" version = "0.2.0" @@ -3230,22 +3105,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "foundry-blob-explorers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195bb5b228e1215c50d828f3e7d48a809a0af2bc0120462710ea5e7fcba3cbe2" -dependencies = [ - "alloy-chains", - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "chrono", - "reqwest", - "serde", -] - [[package]] name = "fragile" version = "2.0.0" @@ -3560,15 +3419,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "hdrhistogram" version = "7.5.4" @@ -4756,17 +4606,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "libsqlite3-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -9089,20 +8928,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rusqlite" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" -dependencies = [ - "bitflags 2.6.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink 0.9.1", - "libsqlite3-sys", - "smallvec", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -10811,12 +10636,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "vergen" version = "8.3.2" diff --git a/Cargo.toml b/Cargo.toml index f109a59c1..9461d605f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,6 @@ members = [ "examples/custom-node-components/", "examples/custom-payload-builder/", "examples/db-access", - "examples/exex/*", "examples/manual-p2p/", "examples/network-txpool/", "examples/network/", @@ -137,8 +136,6 @@ members = [ "examples/rpc-db/", "examples/txpool-tracing/", "examples/custom-rlpx-subprotocol", - "examples/exex/minimal/", - "examples/exex/op-bridge/", "testing/ef-tests/", "testing/testing-utils", ] diff --git a/book/developers/exex/remote.md b/book/developers/exex/remote.md index e0caa72f6..e70ce06ce 100644 --- a/book/developers/exex/remote.md +++ b/book/developers/exex/remote.md @@ -82,7 +82,7 @@ This method streams notifications to the client. A proper way to represent the notification would be to define all fields in the schema, but it goes beyond the scope of this chapter. -For an example of a full schema, see the [Remote ExEx](https://github.com/paradigmxyz/reth-exex-grpc/blob/22b26f7beca1c74577d28be3b3838eb352747be0/proto/exex.proto) example. +For an example of a full schema, see the [Remote ExEx](https://github.com/paradigmxyz/reth-exex-examples/blob/1f74410740ac996276a84ee72003f4f9cf041491/remote/proto/exex.proto) example. diff --git a/examples/README.md b/examples/README.md index b24b7387f..b7847c904 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,13 +24,7 @@ to make a PR! ## ExEx -| Example | Description | -| ----------------------------------------- | --------------------------------------------------------------------------------------------------- | -| [In Memory State](./exex/in-memory-state) | Illustrates an ExEx that tracks the plain state in memory | -| [Minimal](./exex/minimal) | Illustrates how to build a simple ExEx | -| [OP Bridge](./exex/op-bridge) | Illustrates an ExEx that decodes Optimism deposit and withdrawal receipts from L1 | -| [Rollup](./exex/rollup) | Illustrates a rollup ExEx that derives the state from L1 | -| [Discv5 as ExEx](./exex/discv5) | Illustrates an ExEx that runs discv5 discovery stack | +See examples in a [dedicated repository](https://github.com/paradigmxyz/reth-exex-examples). ## RPC diff --git a/examples/exex/discv5/Cargo.toml b/examples/exex/discv5/Cargo.toml deleted file mode 100644 index b1777cfa1..000000000 --- a/examples/exex/discv5/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "example-exex-discv5" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -discv5.workspace = true -enr.workspace = true - -reth-discv5.workspace = true -reth.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-ethereum.workspace = true -reth-network-peers.workspace = true -reth-tracing.workspace = true -futures.workspace = true - -clap.workspace = true -reth-chainspec.workspace = true -serde_json.workspace = true -tokio.workspace = true -tokio-stream.workspace = true -futures-util.workspace = true - -tracing.workspace = true -eyre.workspace = true - -[dev-dependencies] -reth-exex-test-utils.workspace = true -reth-testing-utils.workspace = true diff --git a/examples/exex/discv5/src/exex/mod.rs b/examples/exex/discv5/src/exex/mod.rs deleted file mode 100644 index 4631f3929..000000000 --- a/examples/exex/discv5/src/exex/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -use eyre::Result; -use futures::{Future, FutureExt}; -use reth_exex::{ExExContext, ExExEvent, ExExNotification}; -use reth_node_api::FullNodeComponents; -use reth_tracing::tracing::info; -use std::{ - pin::Pin, - task::{ready, Context, Poll}, -}; -use tracing::error; - -use crate::network::DiscV5ExEx; - -/// The ExEx struct, representing the initialization and execution of the ExEx. -pub struct ExEx { - exex: ExExContext, - disc_v5: DiscV5ExEx, -} - -impl ExEx { - pub fn new(exex: ExExContext, disc_v5: DiscV5ExEx) -> Self { - Self { exex, disc_v5 } - } -} - -impl Future for ExEx { - type Output = Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // Poll the Discv5 future until its drained - loop { - match self.disc_v5.poll_unpin(cx) { - Poll::Ready(Ok(())) => { - info!("Discv5 task completed successfully"); - } - Poll::Ready(Err(e)) => { - error!(?e, "Discv5 task encountered an error"); - return Poll::Ready(Err(e)); - } - Poll::Pending => { - // Exit match and continue to poll notifications - break; - } - } - } - - // Continuously poll the ExExContext notifications - loop { - if let Some(notification) = ready!(self.exex.notifications.poll_recv(cx)) { - match ¬ification { - ExExNotification::ChainCommitted { new } => { - info!(committed_chain = ?new.range(), "Received commit"); - } - ExExNotification::ChainReorged { old, new } => { - info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg"); - } - ExExNotification::ChainReverted { old } => { - info!(reverted_chain = ?old.range(), "Received revert"); - } - } - - if let Some(committed_chain) = notification.committed_chain() { - self.exex - .events - .send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; - } - } - } - } -} diff --git a/examples/exex/discv5/src/main.rs b/examples/exex/discv5/src/main.rs deleted file mode 100644 index 237432605..000000000 --- a/examples/exex/discv5/src/main.rs +++ /dev/null @@ -1,29 +0,0 @@ -use clap::Parser; - -use exex::ExEx; -use network::{cli_ext::Discv5ArgsExt, DiscV5ExEx}; -use reth_node_ethereum::EthereumNode; - -mod exex; -mod network; - -fn main() -> eyre::Result<()> { - reth::cli::Cli::::parse().run(|builder, args| async move { - let tcp_port = args.tcp_port; - let udp_port = args.udp_port; - - let handle = builder - .node(EthereumNode::default()) - .install_exex("exex-discv5", move |ctx| async move { - // start Discv5 task - let disc_v5 = DiscV5ExEx::new(tcp_port, udp_port).await?; - - // start exex task with discv5 - Ok(ExEx::new(ctx, disc_v5)) - }) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -} diff --git a/examples/exex/discv5/src/network/cli_ext.rs b/examples/exex/discv5/src/network/cli_ext.rs deleted file mode 100644 index 1eb864de3..000000000 --- a/examples/exex/discv5/src/network/cli_ext.rs +++ /dev/null @@ -1,15 +0,0 @@ -use clap::Args; - -pub const DEFAULT_DISCOVERY_PORT: u16 = 30304; -pub const DEFAULT_RLPX_PORT: u16 = 30303; - -#[derive(Debug, Clone, Args)] -pub(crate) struct Discv5ArgsExt { - /// TCP port used by RLPx - #[clap(long = "exex-discv5.tcp-port", default_value_t = DEFAULT_RLPX_PORT)] - pub tcp_port: u16, - - /// UDP port used for discovery - #[clap(long = "exex-discv5.udp-port", default_value_t = DEFAULT_DISCOVERY_PORT)] - pub udp_port: u16, -} diff --git a/examples/exex/discv5/src/network/mod.rs b/examples/exex/discv5/src/network/mod.rs deleted file mode 100644 index ebab28342..000000000 --- a/examples/exex/discv5/src/network/mod.rs +++ /dev/null @@ -1,123 +0,0 @@ -#![allow(dead_code)] - -use discv5::{enr::secp256k1::rand, Enr, Event, ListenConfig}; -use reth::network::config::SecretKey; -use reth_discv5::{enr::EnrCombinedKeyWrapper, Config, Discv5}; -use reth_network_peers::NodeRecord; -use reth_tracing::tracing::info; -use std::{ - future::Future, - net::SocketAddr, - pin::Pin, - task::{ready, Context, Poll}, -}; -use tokio::sync::mpsc; - -pub(crate) mod cli_ext; - -/// Helper struct to manage a discovery node using discv5. -pub(crate) struct DiscV5ExEx { - /// The inner discv5 instance. - inner: Discv5, - /// The node record of the discv5 instance. - node_record: NodeRecord, - /// The events stream of the discv5 instance. - events: mpsc::Receiver, -} - -impl DiscV5ExEx { - /// Starts a new discv5 node. - pub async fn new(udp_port: u16, tcp_port: u16) -> eyre::Result { - let secret_key = SecretKey::new(&mut rand::thread_rng()); - - let discv5_addr: SocketAddr = format!("127.0.0.1:{udp_port}").parse()?; - let rlpx_addr: SocketAddr = format!("127.0.0.1:{tcp_port}").parse()?; - - let discv5_listen_config = ListenConfig::from(discv5_addr); - let discv5_config = Config::builder(rlpx_addr) - .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build()) - .build(); - - let (discv5, events, node_record) = Discv5::start(&secret_key, discv5_config).await?; - Ok(Self { inner: discv5, events, node_record }) - } - - /// Adds a node to the table if its not already present. - pub fn add_node(&mut self, enr: Enr) -> eyre::Result<()> { - let reth_enr: enr::Enr = EnrCombinedKeyWrapper(enr.clone()).into(); - self.inner.add_node(reth_enr)?; - Ok(()) - } - - /// Returns the local ENR of the discv5 node. - pub fn local_enr(&self) -> Enr { - self.inner.with_discv5(|discv5| discv5.local_enr()) - } -} - -impl Future for DiscV5ExEx { - type Output = eyre::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut(); - loop { - match ready!(this.events.poll_recv(cx)) { - Some(evt) => { - if let Event::SessionEstablished(enr, socket_addr) = evt { - info!(?enr, ?socket_addr, "Session established with a new peer."); - } - } - None => return Poll::Ready(Ok(())), - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::network::DiscV5ExEx; - use tracing::info; - - #[tokio::test] - async fn can_establish_discv5_session_with_peer() { - reth_tracing::init_test_tracing(); - let mut node_1 = DiscV5ExEx::new(30301, 30303).await.unwrap(); - let node_1_enr = node_1.local_enr(); - - let mut node_2 = DiscV5ExEx::new(30302, 30303).await.unwrap(); - - let node_2_enr = node_2.local_enr(); - - info!(?node_1_enr, ?node_2_enr, "Started discovery nodes."); - - // add node_2 to node_1 table - node_1.add_node(node_2_enr.clone()).unwrap(); - - // verify node_2 is in node_1 table - assert!(node_1 - .inner - .with_discv5(|discv5| discv5.table_entries_id().contains(&node_2_enr.node_id()))); - - // send ping from node_1 to node_2 - node_1.inner.with_discv5(|discv5| discv5.send_ping(node_2_enr.clone())).await.unwrap(); - - // verify they both established a session - let event_2_v5 = node_2.events.recv().await.unwrap(); - let event_1_v5 = node_1.events.recv().await.unwrap(); - assert!(matches!( - event_1_v5, - discv5::Event::SessionEstablished(node, socket) if node == node_2_enr && socket == node_2_enr.udp4_socket().unwrap().into() - )); - assert!(matches!( - event_2_v5, - discv5::Event::SessionEstablished(node, socket) if node == node_1_enr && socket == node_1_enr.udp4_socket().unwrap().into() - )); - - // verify node_1 is in - let event_2_v5 = node_2.events.recv().await.unwrap(); - assert!(matches!( - event_2_v5, - discv5::Event::NodeInserted { node_id, replaced } if node_id == node_1_enr.node_id() && replaced.is_none() - )); - } -} diff --git a/examples/exex/in-memory-state/Cargo.toml b/examples/exex/in-memory-state/Cargo.toml deleted file mode 100644 index b79808cb1..000000000 --- a/examples/exex/in-memory-state/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "example-exex-in-memory-state" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -reth.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-ethereum.workspace = true -reth-tracing.workspace = true -reth-execution-types.workspace = true - -eyre.workspace = true - -[dev-dependencies] -reth-exex-test-utils.workspace = true -reth-testing-utils.workspace = true - -tokio.workspace = true diff --git a/examples/exex/in-memory-state/src/main.rs b/examples/exex/in-memory-state/src/main.rs deleted file mode 100644 index c56cdcf50..000000000 --- a/examples/exex/in-memory-state/src/main.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![warn(unused_crate_dependencies)] - -use reth_execution_types::ExecutionOutcome; -use reth_exex::{ExExContext, ExExEvent, ExExNotification}; -use reth_node_api::FullNodeComponents; -use reth_node_ethereum::EthereumNode; -use reth_tracing::tracing::info; -use std::{ - future::Future, - pin::Pin, - task::{ready, Context, Poll}, -}; - -/// An ExEx that keeps track of the entire state in memory -struct InMemoryStateExEx { - /// The context of the ExEx - ctx: ExExContext, - /// Execution outcome of the chain - execution_outcome: ExecutionOutcome, -} - -impl InMemoryStateExEx { - /// Create a new instance of the ExEx - fn new(ctx: ExExContext) -> Self { - Self { ctx, execution_outcome: ExecutionOutcome::default() } - } -} - -impl Future for InMemoryStateExEx { - type Output = eyre::Result<()>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - while let Some(notification) = ready!(this.ctx.notifications.poll_recv(cx)) { - match ¬ification { - ExExNotification::ChainCommitted { new } => { - info!(committed_chain = ?new.range(), "Received commit"); - } - ExExNotification::ChainReorged { old, new } => { - // revert to block before the reorg - this.execution_outcome.revert_to(new.first().number - 1); - info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg"); - } - ExExNotification::ChainReverted { old } => { - this.execution_outcome.revert_to(old.first().number - 1); - info!(reverted_chain = ?old.range(), "Received revert"); - } - }; - - if let Some(committed_chain) = notification.committed_chain() { - // extend the state with the new chain - this.execution_outcome.extend(committed_chain.execution_outcome().clone()); - this.ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; - } - } - - Poll::Ready(Ok(())) - } -} - -fn main() -> eyre::Result<()> { - reth::cli::Cli::parse_args().run(|builder, _| async move { - let handle = builder - .node(EthereumNode::default()) - .install_exex("in-memory-state", |ctx| async move { Ok(InMemoryStateExEx::new(ctx)) }) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -} - -#[cfg(test)] -mod tests { - use reth::revm::db::BundleState; - use reth_execution_types::{Chain, ExecutionOutcome}; - use reth_exex_test_utils::{test_exex_context, PollOnce}; - use reth_testing_utils::generators::{self, random_block, random_receipt}; - use std::pin::pin; - - #[tokio::test] - async fn test_exex() -> eyre::Result<()> { - let mut rng = &mut generators::rng(); - - let (ctx, handle) = test_exex_context().await?; - let mut exex = pin!(super::InMemoryStateExEx::new(ctx)); - - let mut expected_state = ExecutionOutcome::default(); - - // Generate first block and its state - let block_1 = random_block(&mut rng, 0, None, Some(1), None) - .seal_with_senders() - .ok_or(eyre::eyre!("failed to recover senders"))?; - let block_number_1 = block_1.number; - let execution_outcome1 = ExecutionOutcome::new( - BundleState::default(), - vec![random_receipt(&mut rng, &block_1.body[0], None)].into(), - block_1.number, - vec![], - ); - // Extend the expected state with the first block - expected_state.extend(execution_outcome1.clone()); - - // Send a notification to the Execution Extension that the chain with the first block has - // been committed - handle - .send_notification_chain_committed(Chain::new(vec![block_1], execution_outcome1, None)) - .await?; - exex.poll_once().await?; - - // Assert that the state of the first block has been added to the total state - assert_eq!(exex.as_mut().execution_outcome, expected_state); - - // Generate second block and its state - let block_2 = random_block(&mut rng, 1, None, Some(2), None) - .seal_with_senders() - .ok_or(eyre::eyre!("failed to recover senders"))?; - let execution_outcome2 = ExecutionOutcome::new( - BundleState::default(), - vec![random_receipt(&mut rng, &block_2.body[0], None)].into(), - block_2.number, - vec![], - ); - // Extend the expected execution outcome with the second block - expected_state.extend(execution_outcome2.clone()); - - // Send a notification to the Execution Extension that the chain with the second block has - // been committed - let chain_2 = Chain::new(vec![block_2], execution_outcome2, None); - handle.send_notification_chain_committed(chain_2.clone()).await?; - exex.poll_once().await?; - - // Assert that the execution outcome of the second block has been added to the total state - assert_eq!(exex.as_mut().execution_outcome, expected_state); - - // Send a notification to the Execution Extension that the chain with the second block has - // been reverted - handle.send_notification_chain_reverted(chain_2).await?; - exex.poll_once().await?; - - // Assert that the execution outcome of the second block has been reverted - expected_state.revert_to(block_number_1); - assert_eq!(exex.as_mut().execution_outcome, expected_state); - - Ok(()) - } -} diff --git a/examples/exex/minimal/Cargo.toml b/examples/exex/minimal/Cargo.toml deleted file mode 100644 index 6cf958904..000000000 --- a/examples/exex/minimal/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "example-exex-minimal" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -reth.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-ethereum.workspace = true -reth-tracing.workspace = true -reth-execution-types.workspace = true - -eyre.workspace = true -futures.workspace = true - -[dev-dependencies] -reth-exex-test-utils.workspace = true - -tokio.workspace = true diff --git a/examples/exex/minimal/src/main.rs b/examples/exex/minimal/src/main.rs deleted file mode 100644 index cb7af242e..000000000 --- a/examples/exex/minimal/src/main.rs +++ /dev/null @@ -1,93 +0,0 @@ -use futures::Future; -use reth_exex::{ExExContext, ExExEvent, ExExNotification}; -use reth_node_api::FullNodeComponents; -use reth_node_ethereum::EthereumNode; -use reth_tracing::tracing::info; - -/// The initialization logic of the ExEx is just an async function. -/// -/// During initialization you can wait for resources you need to be up for the ExEx to function, -/// like a database connection. -async fn exex_init( - ctx: ExExContext, -) -> eyre::Result>> { - Ok(exex(ctx)) -} - -/// An ExEx is just a future, which means you can implement all of it in an async function! -/// -/// This ExEx just prints out whenever either a new chain of blocks being added, or a chain of -/// blocks being re-orged. After processing the chain, emits an [ExExEvent::FinishedHeight] event. -async fn exex(mut ctx: ExExContext) -> eyre::Result<()> { - while let Some(notification) = ctx.notifications.recv().await { - match ¬ification { - ExExNotification::ChainCommitted { new } => { - info!(committed_chain = ?new.range(), "Received commit"); - } - ExExNotification::ChainReorged { old, new } => { - info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg"); - } - ExExNotification::ChainReverted { old } => { - info!(reverted_chain = ?old.range(), "Received revert"); - } - }; - - if let Some(committed_chain) = notification.committed_chain() { - ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; - } - } - - Ok(()) -} - -fn main() -> eyre::Result<()> { - reth::cli::Cli::parse_args().run(|builder, _| async move { - let handle = builder - .node(EthereumNode::default()) - .install_exex("Minimal", exex_init) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -} - -#[cfg(test)] -mod tests { - use reth_execution_types::{Chain, ExecutionOutcome}; - use reth_exex_test_utils::{test_exex_context, PollOnce}; - use std::pin::pin; - - #[tokio::test] - async fn test_exex() -> eyre::Result<()> { - // Initialize a test Execution Extension context with all dependencies - let (ctx, mut handle) = test_exex_context().await?; - - // Save the current head of the chain to check the finished height against it later - let head = ctx.head; - - // Send a notification to the Execution Extension that the chain has been committed - handle - .send_notification_chain_committed(Chain::from_block( - handle.genesis.clone(), - ExecutionOutcome::default(), - None, - )) - .await?; - - // Initialize the Execution Extension - let mut exex = pin!(super::exex_init(ctx).await?); - - // Check that the Execution Extension did not emit any events until we polled it - handle.assert_events_empty(); - - // Poll the Execution Extension once to process incoming notifications - exex.poll_once().await?; - - // Check that the Execution Extension emitted a `FinishedHeight` event with the correct - // height - handle.assert_event_finished_height(head.number)?; - - Ok(()) - } -} diff --git a/examples/exex/op-bridge/Cargo.toml b/examples/exex/op-bridge/Cargo.toml deleted file mode 100644 index 38693a2c5..000000000 --- a/examples/exex/op-bridge/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "example-exex-op-bridge" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -reth.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-ethereum.workspace = true -reth-primitives.workspace = true -reth-execution-types.workspace = true -reth-tracing.workspace = true - -eyre.workspace = true -futures.workspace = true -alloy-sol-types = { workspace = true, features = ["json"] } -rusqlite = { version = "0.31.0", features = ["bundled"] } - -[dev-dependencies] -reth-exex-test-utils.workspace = true -reth-testing-utils.workspace = true - -tokio.workspace = true -rand.workspace = true -tempfile.workspace = true diff --git a/examples/exex/op-bridge/l1_standard_bridge_abi.json b/examples/exex/op-bridge/l1_standard_bridge_abi.json deleted file mode 100644 index 1316308c2..000000000 --- a/examples/exex/op-bridge/l1_standard_bridge_abi.json +++ /dev/null @@ -1 +0,0 @@ -[{"inputs":[{"internalType":"address payable","name":"_messenger","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"localToken","type":"address"},{"indexed":true,"internalType":"address","name":"remoteToken","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC20BridgeFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"localToken","type":"address"},{"indexed":true,"internalType":"address","name":"remoteToken","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC20BridgeInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":true,"internalType":"address","name":"l2Token","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC20DepositInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"l1Token","type":"address"},{"indexed":true,"internalType":"address","name":"l2Token","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ERC20WithdrawalFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ETHBridgeFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ETHBridgeInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ETHDepositInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"ETHWithdrawalFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"inputs":[],"name":"MESSENGER","outputs":[{"internalType":"contract CrossDomainMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OTHER_BRIDGE","outputs":[{"internalType":"contract StandardBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeERC20To","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"bridgeETHTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"address","name":"_l2Token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"depositERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"address","name":"_l2Token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"depositERC20To","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint32","name":"_minGasLimit","type":"uint32"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"depositETHTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"deposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_localToken","type":"address"},{"internalType":"address","name":"_remoteToken","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"finalizeBridgeERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"finalizeBridgeETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"address","name":"_l2Token","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"finalizeERC20Withdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_extraData","type":"bytes"}],"name":"finalizeETHWithdrawal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract SuperchainConfig","name":"_superchainConfig","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"l2TokenBridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messenger","outputs":[{"internalType":"contract CrossDomainMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"otherBridge","outputs":[{"internalType":"contract StandardBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"superchainConfig","outputs":[{"internalType":"contract SuperchainConfig","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/examples/exex/op-bridge/src/main.rs b/examples/exex/op-bridge/src/main.rs deleted file mode 100644 index 3c69572f2..000000000 --- a/examples/exex/op-bridge/src/main.rs +++ /dev/null @@ -1,425 +0,0 @@ -use alloy_sol_types::{sol, SolEventInterface}; -use futures::Future; -use reth_execution_types::Chain; -use reth_exex::{ExExContext, ExExEvent}; -use reth_node_api::FullNodeComponents; -use reth_node_ethereum::EthereumNode; -use reth_primitives::{address, Address, Log, SealedBlockWithSenders, TransactionSigned}; -use reth_tracing::tracing::info; -use rusqlite::Connection; - -sol!(L1StandardBridge, "l1_standard_bridge_abi.json"); -use crate::L1StandardBridge::{ETHBridgeFinalized, ETHBridgeInitiated, L1StandardBridgeEvents}; - -const OP_BRIDGES: [Address; 6] = [ - address!("3154Cf16ccdb4C6d922629664174b904d80F2C35"), - address!("3a05E5d33d7Ab3864D53aaEc93c8301C1Fa49115"), - address!("697402166Fbf2F22E970df8a6486Ef171dbfc524"), - address!("99C9fc46f92E8a1c0deC1b1747d010903E884bE1"), - address!("735aDBbE72226BD52e818E7181953f42E3b0FF21"), - address!("3B95bC951EE0f553ba487327278cAc44f29715E5"), -]; - -/// Initializes the ExEx. -/// -/// Opens up a SQLite database and creates the tables (if they don't exist). -async fn init( - ctx: ExExContext, - mut connection: Connection, -) -> eyre::Result>> { - create_tables(&mut connection)?; - - Ok(op_bridge_exex(ctx, connection)) -} - -/// Create SQLite tables if they do not exist. -fn create_tables(connection: &mut Connection) -> rusqlite::Result<()> { - // Create deposits and withdrawals tables - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS deposits ( - id INTEGER PRIMARY KEY, - block_number INTEGER NOT NULL, - tx_hash TEXT NOT NULL UNIQUE, - contract_address TEXT NOT NULL, - "from" TEXT NOT NULL, - "to" TEXT NOT NULL, - amount TEXT NOT NULL - ); - "#, - (), - )?; - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS withdrawals ( - id INTEGER PRIMARY KEY, - block_number INTEGER NOT NULL, - tx_hash TEXT NOT NULL UNIQUE, - contract_address TEXT NOT NULL, - "from" TEXT NOT NULL, - "to" TEXT NOT NULL, - amount TEXT NOT NULL - ); - "#, - (), - )?; - - // Create a bridge contract addresses table and insert known ones with their respective - // names - connection.execute( - r#" - CREATE TABLE IF NOT EXISTS contracts ( - id INTEGER PRIMARY KEY, - address TEXT NOT NULL UNIQUE, - name TEXT NOT NULL - ); - "#, - (), - )?; - connection.execute( - r#" - INSERT OR IGNORE INTO contracts (address, name) - VALUES - ('0x3154Cf16ccdb4C6d922629664174b904d80F2C35', 'Base'), - ('0x3a05E5d33d7Ab3864D53aaEc93c8301C1Fa49115', 'Blast'), - ('0x697402166Fbf2F22E970df8a6486Ef171dbfc524', 'Blast'), - ('0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1', 'Optimism'), - ('0x735aDBbE72226BD52e818E7181953f42E3b0FF21', 'Mode'), - ('0x3B95bC951EE0f553ba487327278cAc44f29715E5', 'Manta'); - "#, - (), - )?; - - info!("Initialized database tables"); - - Ok(()) -} - -/// An example of ExEx that listens to ETH bridging events from OP Stack chains -/// and stores deposits and withdrawals in a SQLite database. -async fn op_bridge_exex( - mut ctx: ExExContext, - connection: Connection, -) -> eyre::Result<()> { - // Process all new chain state notifications - while let Some(notification) = ctx.notifications.recv().await { - // Revert all deposits and withdrawals - if let Some(reverted_chain) = notification.reverted_chain() { - let events = decode_chain_into_events(&reverted_chain); - - let mut deposits = 0; - let mut withdrawals = 0; - - for (_, tx, _, event) in events { - match event { - // L1 -> L2 deposit - L1StandardBridgeEvents::ETHBridgeInitiated(ETHBridgeInitiated { .. }) => { - let deleted = connection.execute( - "DELETE FROM deposits WHERE tx_hash = ?;", - (tx.hash().to_string(),), - )?; - deposits += deleted; - } - // L2 -> L1 withdrawal - L1StandardBridgeEvents::ETHBridgeFinalized(ETHBridgeFinalized { .. }) => { - let deleted = connection.execute( - "DELETE FROM withdrawals WHERE tx_hash = ?;", - (tx.hash().to_string(),), - )?; - withdrawals += deleted; - } - _ => continue, - } - } - - info!(block_range = ?reverted_chain.range(), %deposits, %withdrawals, "Reverted chain events"); - } - - // Insert all new deposits and withdrawals - if let Some(committed_chain) = notification.committed_chain() { - let events = decode_chain_into_events(&committed_chain); - - let mut deposits = 0; - let mut withdrawals = 0; - - for (block, tx, log, event) in events { - match event { - // L1 -> L2 deposit - L1StandardBridgeEvents::ETHBridgeInitiated(ETHBridgeInitiated { - amount, - from, - to, - .. - }) => { - let inserted = connection.execute( - r#" - INSERT INTO deposits (block_number, tx_hash, contract_address, "from", "to", amount) - VALUES (?, ?, ?, ?, ?, ?) - "#, - ( - block.number, - tx.hash().to_string(), - log.address.to_string(), - from.to_string(), - to.to_string(), - amount.to_string(), - ), - )?; - deposits += inserted; - } - // L2 -> L1 withdrawal - L1StandardBridgeEvents::ETHBridgeFinalized(ETHBridgeFinalized { - amount, - from, - to, - .. - }) => { - let inserted = connection.execute( - r#" - INSERT INTO withdrawals (block_number, tx_hash, contract_address, "from", "to", amount) - VALUES (?, ?, ?, ?, ?, ?) - "#, - ( - block.number, - tx.hash().to_string(), - log.address.to_string(), - from.to_string(), - to.to_string(), - amount.to_string(), - ), - )?; - withdrawals += inserted; - } - _ => continue, - }; - } - - info!(block_range = ?committed_chain.range(), %deposits, %withdrawals, "Committed chain events"); - - // Send a finished height event, signaling the node that we don't need any blocks below - // this height anymore - ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; - } - } - - Ok(()) -} - -/// Decode chain of blocks into a flattened list of receipt logs, and filter only -/// [L1StandardBridgeEvents]. -fn decode_chain_into_events( - chain: &Chain, -) -> impl Iterator -{ - chain - // Get all blocks and receipts - .blocks_and_receipts() - // Get all receipts - .flat_map(|(block, receipts)| { - block - .body - .iter() - .zip(receipts.iter().flatten()) - .map(move |(tx, receipt)| (block, tx, receipt)) - }) - // Get all logs from expected bridge contracts - .flat_map(|(block, tx, receipt)| { - receipt - .logs - .iter() - .filter(|log| OP_BRIDGES.contains(&log.address)) - .map(move |log| (block, tx, log)) - }) - // Decode and filter bridge events - .filter_map(|(block, tx, log)| { - L1StandardBridgeEvents::decode_raw_log(log.topics(), &log.data.data, true) - .ok() - .map(|event| (block, tx, log, event)) - }) -} - -fn main() -> eyre::Result<()> { - reth::cli::Cli::parse_args().run(|builder, _| async move { - let handle = builder - .node(EthereumNode::default()) - .install_exex("OPBridge", |ctx| async move { - let connection = Connection::open("op_bridge.db")?; - init(ctx, connection).await - }) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -} - -#[cfg(test)] -mod tests { - use std::pin::pin; - - use alloy_sol_types::SolEvent; - use reth::revm::db::BundleState; - use reth_execution_types::{Chain, ExecutionOutcome}; - use reth_exex_test_utils::{test_exex_context, PollOnce}; - use reth_primitives::{ - Address, Block, Header, Log, Receipt, Transaction, TransactionSigned, TxKind, TxLegacy, - TxType, U256, - }; - use reth_testing_utils::generators::sign_tx_with_random_key_pair; - use rusqlite::Connection; - - use crate::{L1StandardBridge, OP_BRIDGES}; - - /// Given the address of a bridge contract and an event, construct a transaction signed with a - /// random private key and a receipt for that transaction. - fn construct_tx_and_receipt( - to: Address, - event: E, - ) -> eyre::Result<(TransactionSigned, Receipt)> { - let tx = Transaction::Legacy(TxLegacy { to: TxKind::Call(to), ..Default::default() }); - let log = Log::new( - to, - event.encode_topics().into_iter().map(|topic| topic.0).collect(), - event.encode_data().into(), - ) - .ok_or_else(|| eyre::eyre!("failed to encode event"))?; - #[allow(clippy::needless_update)] // side-effect of optimism fields - let receipt = Receipt { - tx_type: TxType::Legacy, - success: true, - cumulative_gas_used: 0, - logs: vec![log], - ..Default::default() - }; - Ok((sign_tx_with_random_key_pair(&mut rand::thread_rng(), tx), receipt)) - } - - #[tokio::test] - async fn test_exex() -> eyre::Result<()> { - // Initialize the test Execution Extension context with all dependencies - let (ctx, handle) = test_exex_context().await?; - // Create a temporary database file, so we can access it later for assertions - let db_file = tempfile::NamedTempFile::new()?; - - // Initialize the ExEx - let mut exex = pin!(super::init(ctx, Connection::open(&db_file)?).await?); - - // Generate random "from" and "to" addresses for deposit and withdrawal events - let from_address = Address::random(); - let to_address = Address::random(); - - // Construct deposit event, transaction and receipt - let deposit_event = L1StandardBridge::ETHBridgeInitiated { - from: from_address, - to: to_address, - amount: U256::from(100), - extraData: Default::default(), - }; - let (deposit_tx, deposit_tx_receipt) = - construct_tx_and_receipt(OP_BRIDGES[0], deposit_event.clone())?; - - // Construct withdrawal event, transaction and receipt - let withdrawal_event = L1StandardBridge::ETHBridgeFinalized { - from: from_address, - to: to_address, - amount: U256::from(200), - extraData: Default::default(), - }; - let (withdrawal_tx, withdrawal_tx_receipt) = - construct_tx_and_receipt(OP_BRIDGES[1], withdrawal_event.clone())?; - - // Construct a block - let block = Block { - header: Header::default(), - body: vec![deposit_tx, withdrawal_tx], - ..Default::default() - } - .seal_slow() - .seal_with_senders() - .ok_or_else(|| eyre::eyre!("failed to recover senders"))?; - - // Construct a chain - let chain = Chain::new( - vec![block.clone()], - ExecutionOutcome::new( - BundleState::default(), - vec![deposit_tx_receipt, withdrawal_tx_receipt].into(), - block.number, - vec![block.requests.clone().unwrap_or_default()], - ), - None, - ); - - // Send a notification that the chain has been committed - handle.send_notification_chain_committed(chain.clone()).await?; - // Poll the ExEx once, it will process the notification that we just sent - exex.poll_once().await?; - - let connection = Connection::open(&db_file)?; - - // Assert that the deposit event was parsed correctly and inserted into the database - let deposits: Vec<(u64, String, String, String, String, String)> = connection - .prepare(r#"SELECT block_number, contract_address, "from", "to", amount, tx_hash FROM deposits"#)? - .query_map([], |row| { - Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?)) - })? - .collect::, _>>()?; - assert_eq!(deposits.len(), 1); - assert_eq!( - deposits[0], - ( - block.number, - OP_BRIDGES[0].to_string(), - from_address.to_string(), - to_address.to_string(), - deposit_event.amount.to_string(), - block.body[0].hash().to_string() - ) - ); - - // Assert that the withdrawal event was parsed correctly and inserted into the database - let withdrawals: Vec<(u64, String, String, String, String, String)> = connection - .prepare(r#"SELECT block_number, contract_address, "from", "to", amount, tx_hash FROM withdrawals"#)? - .query_map([], |row| { - Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?)) - })? - .collect::, _>>()?; - assert_eq!(withdrawals.len(), 1); - assert_eq!( - withdrawals[0], - ( - block.number, - OP_BRIDGES[1].to_string(), - from_address.to_string(), - to_address.to_string(), - withdrawal_event.amount.to_string(), - block.body[1].hash().to_string() - ) - ); - - // Send a notification that the same chain has been reverted - handle.send_notification_chain_reverted(chain).await?; - // Poll the ExEx once, it will process the notification that we just sent - exex.poll_once().await?; - - // Assert that the deposit was removed from the database - let deposits = connection - .prepare(r#"SELECT block_number, contract_address, "from", "to", amount, tx_hash FROM deposits"#)? - .query_map([], |_| { - Ok(()) - })? - .count(); - assert_eq!(deposits, 0); - - // Assert that the withdrawal was removed from the database - let withdrawals = connection - .prepare(r#"SELECT block_number, contract_address, "from", "to", amount, tx_hash FROM withdrawals"#)? - .query_map([], |_| { - Ok(()) - })? - .count(); - assert_eq!(withdrawals, 0); - - Ok(()) - } -} diff --git a/examples/exex/rollup/Cargo.toml b/examples/exex/rollup/Cargo.toml deleted file mode 100644 index 665d23f22..000000000 --- a/examples/exex/rollup/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "example-exex-rollup" -version = "0.0.0" -publish = false -edition.workspace = true -license.workspace = true - -[dependencies] -# reth -reth.workspace = true -reth-chainspec.workspace = true -reth-exex.workspace = true -reth-node-api.workspace = true -reth-node-ethereum.workspace = true -reth-primitives.workspace = true -reth-execution-errors.workspace = true -reth-execution-types.workspace = true -reth-provider.workspace = true -reth-revm.workspace = true -reth-tracing.workspace = true - -# async -tokio.workspace = true - -# misc -alloy-consensus = { workspace = true, features = ["kzg"] } -alloy-genesis.workspace = true -alloy-rlp.workspace = true -alloy-sol-types = { workspace = true, features = ["json"] } -eyre.workspace = true -foundry-blob-explorers = "0.1" -once_cell.workspace = true -rusqlite = { version = "0.31.0", features = ["bundled"] } -serde_json.workspace = true - -[dev-dependencies] -reth-testing-utils.workspace = true -secp256k1.workspace = true - diff --git a/examples/exex/rollup/rollup_abi.json b/examples/exex/rollup/rollup_abi.json deleted file mode 100644 index d7278e9f6..000000000 --- a/examples/exex/rollup/rollup_abi.json +++ /dev/null @@ -1 +0,0 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"defaultRollupChainId","type":"uint256","internalType":"uint256"},{"name":"admin","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"fallback","stateMutability":"payable"},{"type":"receive","stateMutability":"payable"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"SEQUENCER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"acceptDefaultAdminTransfer","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"beginDefaultAdminTransfer","inputs":[{"name":"newAdmin","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"blockCommitment","inputs":[{"name":"header","type":"tuple","internalType":"struct Zenith.BlockHeader","components":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"sequence","type":"uint256","internalType":"uint256"},{"name":"confirmBy","type":"uint256","internalType":"uint256"},{"name":"gasLimit","type":"uint256","internalType":"uint256"},{"name":"rewardAddress","type":"address","internalType":"address"}]},{"name":"blockDataHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"commit","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"cancelDefaultAdminTransfer","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"changeDefaultAdminDelay","inputs":[{"name":"newDelay","type":"uint48","internalType":"uint48"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"defaultAdmin","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"defaultAdminDelay","inputs":[],"outputs":[{"name":"","type":"uint48","internalType":"uint48"}],"stateMutability":"view"},{"type":"function","name":"defaultAdminDelayIncreaseWait","inputs":[],"outputs":[{"name":"","type":"uint48","internalType":"uint48"}],"stateMutability":"view"},{"type":"function","name":"enter","inputs":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"rollupRecipient","type":"address","internalType":"address"},{"name":"token","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"enter","inputs":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"rollupRecipient","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"fulfillExits","inputs":[{"name":"orders","type":"tuple[]","internalType":"struct Passage.ExitOrder[]","components":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"token","type":"address","internalType":"address"},{"name":"recipient","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"lastSubmittedAtBlock","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"nextSequence","inputs":[{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"pendingDefaultAdmin","inputs":[],"outputs":[{"name":"newAdmin","type":"address","internalType":"address"},{"name":"schedule","type":"uint48","internalType":"uint48"}],"stateMutability":"view"},{"type":"function","name":"pendingDefaultAdminDelay","inputs":[],"outputs":[{"name":"newDelay","type":"uint48","internalType":"uint48"},{"name":"schedule","type":"uint48","internalType":"uint48"}],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"rollbackDefaultAdminDelay","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"submitBlock","inputs":[{"name":"header","type":"tuple","internalType":"struct Zenith.BlockHeader","components":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"sequence","type":"uint256","internalType":"uint256"},{"name":"confirmBy","type":"uint256","internalType":"uint256"},{"name":"gasLimit","type":"uint256","internalType":"uint256"},{"name":"rewardAddress","type":"address","internalType":"address"}]},{"name":"blockDataHash","type":"bytes32","internalType":"bytes32"},{"name":"v","type":"uint8","internalType":"uint8"},{"name":"r","type":"bytes32","internalType":"bytes32"},{"name":"s","type":"bytes32","internalType":"bytes32"},{"name":"blockData","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"event","name":"BlockData","inputs":[{"name":"blockData","type":"bytes","indexed":false,"internalType":"bytes"}],"anonymous":false},{"type":"event","name":"BlockSubmitted","inputs":[{"name":"sequencer","type":"address","indexed":true,"internalType":"address"},{"name":"header","type":"tuple","indexed":true,"internalType":"struct Zenith.BlockHeader","components":[{"name":"rollupChainId","type":"uint256","internalType":"uint256"},{"name":"sequence","type":"uint256","internalType":"uint256"},{"name":"confirmBy","type":"uint256","internalType":"uint256"},{"name":"gasLimit","type":"uint256","internalType":"uint256"},{"name":"rewardAddress","type":"address","internalType":"address"}]},{"name":"blockDataHash","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"DefaultAdminDelayChangeCanceled","inputs":[],"anonymous":false},{"type":"event","name":"DefaultAdminDelayChangeScheduled","inputs":[{"name":"newDelay","type":"uint48","indexed":false,"internalType":"uint48"},{"name":"effectSchedule","type":"uint48","indexed":false,"internalType":"uint48"}],"anonymous":false},{"type":"event","name":"DefaultAdminTransferCanceled","inputs":[],"anonymous":false},{"type":"event","name":"DefaultAdminTransferScheduled","inputs":[{"name":"newAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"acceptSchedule","type":"uint48","indexed":false,"internalType":"uint48"}],"anonymous":false},{"type":"event","name":"Enter","inputs":[{"name":"rollupChainId","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"token","type":"address","indexed":true,"internalType":"address"},{"name":"rollupRecipient","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ExitFilled","inputs":[{"name":"rollupChainId","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"token","type":"address","indexed":true,"internalType":"address"},{"name":"hostRecipient","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlEnforcedDefaultAdminDelay","inputs":[{"name":"schedule","type":"uint48","internalType":"uint48"}]},{"type":"error","name":"AccessControlEnforcedDefaultAdminRules","inputs":[]},{"type":"error","name":"AccessControlInvalidDefaultAdmin","inputs":[{"name":"defaultAdmin","type":"address","internalType":"address"}]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"BadSequence","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"BadSignature","inputs":[{"name":"derivedSequencer","type":"address","internalType":"address"}]},{"type":"error","name":"BlockExpired","inputs":[]},{"type":"error","name":"OneRollupBlockPerHostBlock","inputs":[]},{"type":"error","name":"OrderExpired","inputs":[]},{"type":"error","name":"SafeCastOverflowedUintDowncast","inputs":[{"name":"bits","type":"uint8","internalType":"uint8"},{"name":"value","type":"uint256","internalType":"uint256"}]}],"bytecode":{"object":"0x60a060405234801561000f575f80fd5b50604051611a98380380611a9883398101604081905261002e916101ae565b608082905262015180816001600160a01b03811661006557604051636116401160e11b81525f600482015260240160405180910390fd5b600180546001600160d01b0316600160d01b65ffffffffffff85160217905561008e5f82610098565b50505050506101e8565b5f826100f4575f6100b16002546001600160a01b031690565b6001600160a01b0316146100d857604051631fe1e13d60e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384161790555b6100fe8383610107565b90505b92915050565b5f828152602081815260408083206001600160a01b038516845290915281205460ff166101a7575f838152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905561015f3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610101565b505f610101565b5f80604083850312156101bf575f80fd5b825160208401519092506001600160a01b03811681146101dd575f80fd5b809150509250929050565b6080516118916102075f395f818161018e01526101ba01526118915ff3fe608060405260043610610184575f3560e01c80637e82bb01116100d0578063c7bc4a6211610089578063cf6eefb711610063578063cf6eefb7146104b7578063d547741f146104f1578063d602b9fd14610510578063ea3b9ba114610524576101b5565b8063c7bc4a6214610470578063cc8463c81461048f578063cefc1429146104a3576101b5565b80637e82bb011461039b57806384ef8ffc146103c65780638da5cb5b146103f757806391d148541461040b578063a1eda53c1461042a578063a217fddf1461045d576101b5565b806336568abe1161013d5780634842855c116101175780634842855c1461031a578063634e93da1461033e578063649a5ec71461035d5780637e5692741461037c576101b5565b806336568abe146102d557806336702119146102f45780633805c6bd14610307576101b5565b806301ffc9a7146101df578063022d63fb146102135780630aa6220b1461023b5780631e6637201461024f578063248a9ca3146102885780632f2ff15d146102b6576101b5565b366101b5576101b37f000000000000000000000000000000000000000000000000000000000000000033610532565b005b6101b37f000000000000000000000000000000000000000000000000000000000000000033610532565b3480156101ea575f80fd5b506101fe6101f93660046114a8565b61057b565b60405190151581526020015b60405180910390f35b34801561021e575f80fd5b50620697805b60405165ffffffffffff909116815260200161020a565b348015610246575f80fd5b506101b36105a5565b34801561025a575f80fd5b5061027a6102693660046114cf565b60036020525f908152604090205481565b60405190815260200161020a565b348015610293575f80fd5b5061027a6102a23660046114cf565b5f9081526020819052604090206001015490565b3480156102c1575f80fd5b506101b36102d0366004611501565b6105ba565b3480156102e0575f80fd5b506101b36102ef366004611501565b6105e6565b6101b361030236600461152b565b610691565b6101b361031536600461159a565b610960565b348015610325575f80fd5b5061027a6d53455155454e4345525f524f4c4560901b81565b348015610349575f80fd5b506101b36103583660046115db565b610a31565b348015610368575f80fd5b506101b36103773660046115f4565b610a44565b348015610387575f80fd5b506101b361039636600461169a565b610a57565b3480156103a6575f80fd5b5061027a6103b53660046114cf565b60046020525f908152604090205481565b3480156103d1575f80fd5b506002546001600160a01b03165b6040516001600160a01b03909116815260200161020a565b348015610402575f80fd5b506103df610aa6565b348015610416575f80fd5b506101fe610425366004611501565b610abe565b348015610435575f80fd5b5061043e610ae6565b6040805165ffffffffffff93841681529290911660208301520161020a565b348015610468575f80fd5b5061027a5f81565b34801561047b575f80fd5b5061027a61048a36600461174d565b610b38565b34801561049a575f80fd5b50610224610bcd565b3480156104ae575f80fd5b506101b3610c2b565b3480156104c2575f80fd5b506104cb610c6a565b604080516001600160a01b03909316835265ffffffffffff90911660208301520161020a565b3480156104fc575f80fd5b506101b361050b366004611501565b610c8b565b34801561051b575f80fd5b506101b3610cb3565b6101b3610532366004611501565b604080518381523460208201526001600160a01b038316915f917fe8a7ca8155e14d9cc8faeecec58a97268da95a2327cc892593748ce37cc6953f910160405180910390a35050565b5f6001600160e01b031982166318a4c3c360e11b148061059f575061059f82610cc5565b92915050565b5f6105af81610cf9565b6105b7610d03565b50565b816105d857604051631fe1e13d60e11b815260040160405180910390fd5b6105e28282610d0f565b5050565b8115801561060157506002546001600160a01b038281169116145b15610687575f80610610610c6a565b90925090506001600160a01b038216151580610632575065ffffffffffff8116155b8061064557504265ffffffffffff821610155b15610672576040516319ca5ebb60e01b815265ffffffffffff821660048201526024015b60405180910390fd5b50506001805465ffffffffffff60a01b191690555b6105e28282610d33565b345f5b8281101561095a575f8484838181106106af576106af611776565b90506080020160200160208101906106c791906115db565b6001600160a01b03160361077e578383828181106106e7576106e7611776565b90506080020160400160208101906106ff91906115db565b6001600160a01b03166108fc85858481811061071d5761071d611776565b9050608002016060013590811502906040515f60405180830381858888f1935050505015801561074f573d5f803e3d5ffd5b5083838281811061076257610762611776565b9050608002016060013582610777919061179e565b9150610875565b83838281811061079057610790611776565b90506080020160200160208101906107a891906115db565b6001600160a01b03166323b872dd338686858181106107c9576107c9611776565b90506080020160400160208101906107e191906115db565b8787868181106107f3576107f3611776565b6040516001600160e01b031960e088901b1681526001600160a01b039586166004820152949093166024850152506060608090920201013560448201526064016020604051808303815f875af115801561084f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061087391906117b1565b505b83838281811061088757610887611776565b905060800201604001602081019061089f91906115db565b6001600160a01b03168484838181106108ba576108ba611776565b90506080020160200160208101906108d291906115db565b6001600160a01b03167fe93d7a771f81dc20f1d474f6868677269fdfa09830508e48edb0aa4d6569983386868581811061090e5761090e611776565b9050608002015f013587878681811061092957610929611776565b9050608002016060013560405161094a929190918252602082015260400190565b60405180910390a3600101610694565b50505050565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd906064016020604051808303815f875af11580156109b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109d491906117b1565b50826001600160a01b0316826001600160a01b03167fe8a7ca8155e14d9cc8faeecec58a97268da95a2327cc892593748ce37cc6953f8684604051610a23929190918252602082015260400190565b60405180910390a350505050565b5f610a3b81610cf9565b6105e282610d6b565b5f610a4e81610cf9565b6105e282610ddd565b610a648787878787610e4c565b7fc030727dea5440ebb1789967645e2595e4e67cf55821175a3f9f8b33aff41fa58282604051610a959291906117d0565b60405180910390a150505050505050565b5f610ab96002546001600160a01b031690565b905090565b5f918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6002545f90600160d01b900465ffffffffffff168015158015610b1157504265ffffffffffff821610155b610b1c575f80610b30565b600254600160a01b900465ffffffffffff16815b915091509091565b81516020808401516060808601516040808801516080909801518151710696e6974342e73657175656e6365722e76360741b8188015246603282015260528101979097526072870194909452609286019190915260b28501969096521b6bffffffffffffffffffffffff191660d283015260e68083019390935283518083039093018352610106909101909252805191012090565b6002545f90600160d01b900465ffffffffffff168015158015610bf757504265ffffffffffff8216105b610c1257600154600160d01b900465ffffffffffff16610c25565b600254600160a01b900465ffffffffffff165b91505090565b5f610c34610c6a565b509050336001600160a01b03821614610c6257604051636116401160e11b8152336004820152602401610669565b6105b7611047565b6001546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81610ca957604051631fe1e13d60e11b815260040160405180910390fd5b6105e282826110dd565b5f610cbd81610cf9565b6105b7611101565b5f6001600160e01b03198216637965db0b60e01b148061059f57506301ffc9a760e01b6001600160e01b031983161461059f565b6105b7813361110b565b610d0d5f80611144565b565b5f82815260208190526040902060010154610d2981610cf9565b61095a8383611203565b6001600160a01b0381163314610d5c5760405163334bd91960e11b815260040160405180910390fd5b610d668282611270565b505050565b5f610d74610bcd565b610d7d426112ac565b610d8791906117fe565b9050610d9382826112e2565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b5f610de78261135f565b610df0426112ac565b610dfa91906117fe565b9050610e068282611144565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b910160405180910390a15050565b84515f90815260036020526040812080549082610e6883611824565b91905055905085602001518114610e9557604051635f64988d60e11b815260048101829052602401610669565b8560400151421115610eba576040516378fd448d60e01b815260040160405180910390fd5b5f610ec58787610b38565b604080515f8082526020820180845284905260ff89169282019290925260608101879052608081018690529192509060019060a0016020604051602081039080840390855afa158015610f1a573d5f803e3d5ffd5b505050602060405103519050610f416d53455155454e4345525f524f4c4560901b82610abe565b610f6957604051639a7d38d960e01b81526001600160a01b0382166004820152602401610669565b87515f90815260046020526040902054439003610f9957604051632ce0494b60e01b815260040160405180910390fd5b87515f908152600460205260409081902043905551610ff2908990815181526020808301519082015260408083015190820152606080830151908201526080918201516001600160a01b03169181019190915260a00190565b6040518091039020816001600160a01b03167f9c5702b5639f451bda4f9dba7fdf9d125a675ccddd315b81ce962d3ddd986a238960405161103591815260200190565b60405180910390a35050505050505050565b5f80611051610c6a565b915091506110668165ffffffffffff16151590565b158061107a57504265ffffffffffff821610155b156110a2576040516319ca5ebb60e01b815265ffffffffffff82166004820152602401610669565b6110bd5f6110b86002546001600160a01b031690565b611270565b506110c85f83611203565b5050600180546001600160d01b031916905550565b5f828152602081905260409020600101546110f781610cf9565b61095a8383611270565b610d0d5f806112e2565b6111158282610abe565b6105e25760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610669565b600254600160d01b900465ffffffffffff1680156111c6574265ffffffffffff8216101561119d57600254600180546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b029190911790556111c6565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5905f90a15b50600280546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b5f8261125f575f61121c6002546001600160a01b031690565b6001600160a01b03161461124357604051631fe1e13d60e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384161790555b61126983836113b0565b9392505050565b5f8215801561128c57506002546001600160a01b038381169116145b156112a257600280546001600160a01b03191690555b611269838361143f565b5f65ffffffffffff8211156112de576040516306dfcc6560e41b81526030600482015260248101839052604401610669565b5090565b5f6112eb610c6a565b6001805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061132d90508165ffffffffffff16151590565b15610d66576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109905f90a1505050565b5f80611369610bcd565b90508065ffffffffffff168365ffffffffffff16116113915761138c838261183c565b611269565b61126965ffffffffffff8416620697805f828218828410028218611269565b5f6113bb8383610abe565b611438575f838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556113f03390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161059f565b505f61059f565b5f61144a8383610abe565b15611438575f838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161059f565b5f602082840312156114b8575f80fd5b81356001600160e01b031981168114611269575f80fd5b5f602082840312156114df575f80fd5b5035919050565b80356001600160a01b03811681146114fc575f80fd5b919050565b5f8060408385031215611512575f80fd5b82359150611522602084016114e6565b90509250929050565b5f806020838503121561153c575f80fd5b823567ffffffffffffffff80821115611553575f80fd5b818501915085601f830112611566575f80fd5b813581811115611574575f80fd5b8660208260071b8501011115611588575f80fd5b60209290920196919550909350505050565b5f805f80608085870312156115ad575f80fd5b843593506115bd602086016114e6565b92506115cb604086016114e6565b9396929550929360600135925050565b5f602082840312156115eb575f80fd5b611269826114e6565b5f60208284031215611604575f80fd5b813565ffffffffffff81168114611269575f80fd5b5f60a08284031215611629575f80fd5b60405160a0810181811067ffffffffffffffff8211171561165857634e487b7160e01b5f52604160045260245ffd5b80604052508091508235815260208301356020820152604083013560408201526060830135606082015261168e608084016114e6565b60808201525092915050565b5f805f805f805f610140888a0312156116b1575f80fd5b6116bb8989611619565b965060a0880135955060c088013560ff811681146116d7575f80fd5b945060e08801359350610100880135925061012088013567ffffffffffffffff80821115611703575f80fd5b818a0191508a601f830112611716575f80fd5b813581811115611724575f80fd5b8b6020828501011115611735575f80fd5b60208301945080935050505092959891949750929550565b5f8060c0838503121561175e575f80fd5b6117688484611619565b9460a0939093013593505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b8181038181111561059f5761059f61178a565b5f602082840312156117c1575f80fd5b81518015158114611269575f80fd5b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b65ffffffffffff81811683821601908082111561181d5761181d61178a565b5092915050565b5f600182016118355761183561178a565b5060010190565b65ffffffffffff82811682821603908082111561181d5761181d61178a56fea2646970667358221220111de8e40c8e2761ed9ab04f385dfef1dffcd646c5a270f4fc3dc0858a0d605764736f6c63430008190033","sourceMap":"281:7248:35:-:0;;;3619:155;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2256:44:34;;;;3753:6:35;3761:5;-1:-1:-1;;;;;2384:33:23;;2380:115;;2440:44;;-1:-1:-1;;;2440:44:23;;2481:1;2440:44;;;516:51:38;489:18;;2440:44:23;;;;;;;2380:115;2504:13;:28;;-1:-1:-1;;;;;2504:28:23;-1:-1:-1;;;2504:28:23;;;;;;;2542:51;-1:-1:-1;2573:19:23;2542:10;:51::i;:::-;;2308:292;;3619:155:35;;281:7248;;5509:370:23;5595:4;5615;5611:214;;5687:1;5661:14;6786:20;;-1:-1:-1;;;;;6786:20:23;;6707:106;5661:14;-1:-1:-1;;;;;5661:28:23;;5657:114;;5716:40;;-1:-1:-1;;;5716:40:23;;;;;;;;;;;5657:114;5784:20;:30;;-1:-1:-1;;;;;;5784:30:23;-1:-1:-1;;;;;5784:30:23;;;;;5611:214;5841:31;5858:4;5864:7;5841:16;:31::i;:::-;5834:38;;5509:370;;;;;:::o;6179:316:21:-;6256:4;2954:12;;;;;;;;;;;-1:-1:-1;;;;;2954:29:21;;;;;;;;;;;;6272:217;;6315:6;:12;;;;;;;;;;;-1:-1:-1;;;;;6315:29:21;;;;;;;;;:36;;-1:-1:-1;;6315:36:21;6347:4;6315:36;;;6397:12;735:10:27;;656:96;6397:12:21;-1:-1:-1;;;;;6370:40:21;6388:7;-1:-1:-1;;;;;6370:40:21;6382:4;6370:40;;;;;;;;;;-1:-1:-1;6431:4:21;6424:11;;6272:217;-1:-1:-1;6473:5:21;6466:12;;14:351:38;93:6;101;154:2;142:9;133:7;129:23;125:32;122:52;;;170:1;167;160:12;122:52;193:16;;252:2;237:18;;231:25;193:16;;-1:-1:-1;;;;;;285:31:38;;275:42;;265:70;;331:1;328;321:12;265:70;354:5;344:15;;;14:351;;;;;:::o;370:203::-;281:7248:35;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x608060405260043610610184575f3560e01c80637e82bb01116100d0578063c7bc4a6211610089578063cf6eefb711610063578063cf6eefb7146104b7578063d547741f146104f1578063d602b9fd14610510578063ea3b9ba114610524576101b5565b8063c7bc4a6214610470578063cc8463c81461048f578063cefc1429146104a3576101b5565b80637e82bb011461039b57806384ef8ffc146103c65780638da5cb5b146103f757806391d148541461040b578063a1eda53c1461042a578063a217fddf1461045d576101b5565b806336568abe1161013d5780634842855c116101175780634842855c1461031a578063634e93da1461033e578063649a5ec71461035d5780637e5692741461037c576101b5565b806336568abe146102d557806336702119146102f45780633805c6bd14610307576101b5565b806301ffc9a7146101df578063022d63fb146102135780630aa6220b1461023b5780631e6637201461024f578063248a9ca3146102885780632f2ff15d146102b6576101b5565b366101b5576101b37f000000000000000000000000000000000000000000000000000000000000000033610532565b005b6101b37f000000000000000000000000000000000000000000000000000000000000000033610532565b3480156101ea575f80fd5b506101fe6101f93660046114a8565b61057b565b60405190151581526020015b60405180910390f35b34801561021e575f80fd5b50620697805b60405165ffffffffffff909116815260200161020a565b348015610246575f80fd5b506101b36105a5565b34801561025a575f80fd5b5061027a6102693660046114cf565b60036020525f908152604090205481565b60405190815260200161020a565b348015610293575f80fd5b5061027a6102a23660046114cf565b5f9081526020819052604090206001015490565b3480156102c1575f80fd5b506101b36102d0366004611501565b6105ba565b3480156102e0575f80fd5b506101b36102ef366004611501565b6105e6565b6101b361030236600461152b565b610691565b6101b361031536600461159a565b610960565b348015610325575f80fd5b5061027a6d53455155454e4345525f524f4c4560901b81565b348015610349575f80fd5b506101b36103583660046115db565b610a31565b348015610368575f80fd5b506101b36103773660046115f4565b610a44565b348015610387575f80fd5b506101b361039636600461169a565b610a57565b3480156103a6575f80fd5b5061027a6103b53660046114cf565b60046020525f908152604090205481565b3480156103d1575f80fd5b506002546001600160a01b03165b6040516001600160a01b03909116815260200161020a565b348015610402575f80fd5b506103df610aa6565b348015610416575f80fd5b506101fe610425366004611501565b610abe565b348015610435575f80fd5b5061043e610ae6565b6040805165ffffffffffff93841681529290911660208301520161020a565b348015610468575f80fd5b5061027a5f81565b34801561047b575f80fd5b5061027a61048a36600461174d565b610b38565b34801561049a575f80fd5b50610224610bcd565b3480156104ae575f80fd5b506101b3610c2b565b3480156104c2575f80fd5b506104cb610c6a565b604080516001600160a01b03909316835265ffffffffffff90911660208301520161020a565b3480156104fc575f80fd5b506101b361050b366004611501565b610c8b565b34801561051b575f80fd5b506101b3610cb3565b6101b3610532366004611501565b604080518381523460208201526001600160a01b038316915f917fe8a7ca8155e14d9cc8faeecec58a97268da95a2327cc892593748ce37cc6953f910160405180910390a35050565b5f6001600160e01b031982166318a4c3c360e11b148061059f575061059f82610cc5565b92915050565b5f6105af81610cf9565b6105b7610d03565b50565b816105d857604051631fe1e13d60e11b815260040160405180910390fd5b6105e28282610d0f565b5050565b8115801561060157506002546001600160a01b038281169116145b15610687575f80610610610c6a565b90925090506001600160a01b038216151580610632575065ffffffffffff8116155b8061064557504265ffffffffffff821610155b15610672576040516319ca5ebb60e01b815265ffffffffffff821660048201526024015b60405180910390fd5b50506001805465ffffffffffff60a01b191690555b6105e28282610d33565b345f5b8281101561095a575f8484838181106106af576106af611776565b90506080020160200160208101906106c791906115db565b6001600160a01b03160361077e578383828181106106e7576106e7611776565b90506080020160400160208101906106ff91906115db565b6001600160a01b03166108fc85858481811061071d5761071d611776565b9050608002016060013590811502906040515f60405180830381858888f1935050505015801561074f573d5f803e3d5ffd5b5083838281811061076257610762611776565b9050608002016060013582610777919061179e565b9150610875565b83838281811061079057610790611776565b90506080020160200160208101906107a891906115db565b6001600160a01b03166323b872dd338686858181106107c9576107c9611776565b90506080020160400160208101906107e191906115db565b8787868181106107f3576107f3611776565b6040516001600160e01b031960e088901b1681526001600160a01b039586166004820152949093166024850152506060608090920201013560448201526064016020604051808303815f875af115801561084f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061087391906117b1565b505b83838281811061088757610887611776565b905060800201604001602081019061089f91906115db565b6001600160a01b03168484838181106108ba576108ba611776565b90506080020160200160208101906108d291906115db565b6001600160a01b03167fe93d7a771f81dc20f1d474f6868677269fdfa09830508e48edb0aa4d6569983386868581811061090e5761090e611776565b9050608002015f013587878681811061092957610929611776565b9050608002016060013560405161094a929190918252602082015260400190565b60405180910390a3600101610694565b50505050565b6040516323b872dd60e01b8152336004820152306024820152604481018290526001600160a01b038316906323b872dd906064016020604051808303815f875af11580156109b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109d491906117b1565b50826001600160a01b0316826001600160a01b03167fe8a7ca8155e14d9cc8faeecec58a97268da95a2327cc892593748ce37cc6953f8684604051610a23929190918252602082015260400190565b60405180910390a350505050565b5f610a3b81610cf9565b6105e282610d6b565b5f610a4e81610cf9565b6105e282610ddd565b610a648787878787610e4c565b7fc030727dea5440ebb1789967645e2595e4e67cf55821175a3f9f8b33aff41fa58282604051610a959291906117d0565b60405180910390a150505050505050565b5f610ab96002546001600160a01b031690565b905090565b5f918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6002545f90600160d01b900465ffffffffffff168015158015610b1157504265ffffffffffff821610155b610b1c575f80610b30565b600254600160a01b900465ffffffffffff16815b915091509091565b81516020808401516060808601516040808801516080909801518151710696e6974342e73657175656e6365722e76360741b8188015246603282015260528101979097526072870194909452609286019190915260b28501969096521b6bffffffffffffffffffffffff191660d283015260e68083019390935283518083039093018352610106909101909252805191012090565b6002545f90600160d01b900465ffffffffffff168015158015610bf757504265ffffffffffff8216105b610c1257600154600160d01b900465ffffffffffff16610c25565b600254600160a01b900465ffffffffffff165b91505090565b5f610c34610c6a565b509050336001600160a01b03821614610c6257604051636116401160e11b8152336004820152602401610669565b6105b7611047565b6001546001600160a01b03811691600160a01b90910465ffffffffffff1690565b81610ca957604051631fe1e13d60e11b815260040160405180910390fd5b6105e282826110dd565b5f610cbd81610cf9565b6105b7611101565b5f6001600160e01b03198216637965db0b60e01b148061059f57506301ffc9a760e01b6001600160e01b031983161461059f565b6105b7813361110b565b610d0d5f80611144565b565b5f82815260208190526040902060010154610d2981610cf9565b61095a8383611203565b6001600160a01b0381163314610d5c5760405163334bd91960e11b815260040160405180910390fd5b610d668282611270565b505050565b5f610d74610bcd565b610d7d426112ac565b610d8791906117fe565b9050610d9382826112e2565b60405165ffffffffffff821681526001600160a01b038316907f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed69060200160405180910390a25050565b5f610de78261135f565b610df0426112ac565b610dfa91906117fe565b9050610e068282611144565b6040805165ffffffffffff8085168252831660208201527ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b910160405180910390a15050565b84515f90815260036020526040812080549082610e6883611824565b91905055905085602001518114610e9557604051635f64988d60e11b815260048101829052602401610669565b8560400151421115610eba576040516378fd448d60e01b815260040160405180910390fd5b5f610ec58787610b38565b604080515f8082526020820180845284905260ff89169282019290925260608101879052608081018690529192509060019060a0016020604051602081039080840390855afa158015610f1a573d5f803e3d5ffd5b505050602060405103519050610f416d53455155454e4345525f524f4c4560901b82610abe565b610f6957604051639a7d38d960e01b81526001600160a01b0382166004820152602401610669565b87515f90815260046020526040902054439003610f9957604051632ce0494b60e01b815260040160405180910390fd5b87515f908152600460205260409081902043905551610ff2908990815181526020808301519082015260408083015190820152606080830151908201526080918201516001600160a01b03169181019190915260a00190565b6040518091039020816001600160a01b03167f9c5702b5639f451bda4f9dba7fdf9d125a675ccddd315b81ce962d3ddd986a238960405161103591815260200190565b60405180910390a35050505050505050565b5f80611051610c6a565b915091506110668165ffffffffffff16151590565b158061107a57504265ffffffffffff821610155b156110a2576040516319ca5ebb60e01b815265ffffffffffff82166004820152602401610669565b6110bd5f6110b86002546001600160a01b031690565b611270565b506110c85f83611203565b5050600180546001600160d01b031916905550565b5f828152602081905260409020600101546110f781610cf9565b61095a8383611270565b610d0d5f806112e2565b6111158282610abe565b6105e25760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610669565b600254600160d01b900465ffffffffffff1680156111c6574265ffffffffffff8216101561119d57600254600180546001600160d01b0316600160a01b90920465ffffffffffff16600160d01b029190911790556111c6565b6040517f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5905f90a15b50600280546001600160a01b0316600160a01b65ffffffffffff948516026001600160d01b031617600160d01b9290931691909102919091179055565b5f8261125f575f61121c6002546001600160a01b031690565b6001600160a01b03161461124357604051631fe1e13d60e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384161790555b61126983836113b0565b9392505050565b5f8215801561128c57506002546001600160a01b038381169116145b156112a257600280546001600160a01b03191690555b611269838361143f565b5f65ffffffffffff8211156112de576040516306dfcc6560e41b81526030600482015260248101839052604401610669565b5090565b5f6112eb610c6a565b6001805465ffffffffffff8616600160a01b026001600160d01b03199091166001600160a01b03881617179055915061132d90508165ffffffffffff16151590565b15610d66576040517f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109905f90a1505050565b5f80611369610bcd565b90508065ffffffffffff168365ffffffffffff16116113915761138c838261183c565b611269565b61126965ffffffffffff8416620697805f828218828410028218611269565b5f6113bb8383610abe565b611438575f838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556113f03390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161059f565b505f61059f565b5f61144a8383610abe565b15611438575f838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a450600161059f565b5f602082840312156114b8575f80fd5b81356001600160e01b031981168114611269575f80fd5b5f602082840312156114df575f80fd5b5035919050565b80356001600160a01b03811681146114fc575f80fd5b919050565b5f8060408385031215611512575f80fd5b82359150611522602084016114e6565b90509250929050565b5f806020838503121561153c575f80fd5b823567ffffffffffffffff80821115611553575f80fd5b818501915085601f830112611566575f80fd5b813581811115611574575f80fd5b8660208260071b8501011115611588575f80fd5b60209290920196919550909350505050565b5f805f80608085870312156115ad575f80fd5b843593506115bd602086016114e6565b92506115cb604086016114e6565b9396929550929360600135925050565b5f602082840312156115eb575f80fd5b611269826114e6565b5f60208284031215611604575f80fd5b813565ffffffffffff81168114611269575f80fd5b5f60a08284031215611629575f80fd5b60405160a0810181811067ffffffffffffffff8211171561165857634e487b7160e01b5f52604160045260245ffd5b80604052508091508235815260208301356020820152604083013560408201526060830135606082015261168e608084016114e6565b60808201525092915050565b5f805f805f805f610140888a0312156116b1575f80fd5b6116bb8989611619565b965060a0880135955060c088013560ff811681146116d7575f80fd5b945060e08801359350610100880135925061012088013567ffffffffffffffff80821115611703575f80fd5b818a0191508a601f830112611716575f80fd5b813581811115611724575f80fd5b8b6020828501011115611735575f80fd5b60208301945080935050505092959891949750929550565b5f8060c0838503121561175e575f80fd5b6117688484611619565b9460a0939093013593505050565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b8181038181111561059f5761059f61178a565b5f602082840312156117c1575f80fd5b81518015158114611269575f80fd5b60208152816020820152818360408301375f818301604090810191909152601f909201601f19160101919050565b65ffffffffffff81811683821601908082111561181d5761181d61178a565b5092915050565b5f600182016118355761183561178a565b5060010190565b65ffffffffffff82811682821603908082111561181d5761181d61178a56fea2646970667358221220111de8e40c8e2761ed9ab04f385dfef1dffcd646c5a270f4fc3dc0858a0d605764736f6c63430008190033","sourceMap":"281:7248:35:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2632:39:34;2638:20;2660:10;2632:5;:39::i;:::-;281:7248:35;;2447:39:34;2453:20;2475:10;2447:5;:39::i;2667:219:23:-;;;;;;;;;;-1:-1:-1;2667:219:23;;;;;:::i;:::-;;:::i;:::-;;;470:14:38;;463:22;445:41;;433:2;418:18;2667:219:23;;;;;;;;7766:108;;;;;;;;;;-1:-1:-1;7861:6:23;7766:108;;;671:14:38;659:27;;;641:46;;629:2;614:18;7766:108:23;497:196:38;10927:126:23;;;;;;;;;;;;;:::i;1478:47:35:-;;;;;;;;;;-1:-1:-1;1478:47:35;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1029:25:38;;;1017:2;1002:18;1478:47:35;883:177:38;3810:120:21;;;;;;;;;;-1:-1:-1;3810:120:21;;;;;:::i;:::-;3875:7;3901:12;;;;;;;;;;:22;;;;3810:120;3198:265:23;;;;;;;;;;-1:-1:-1;3198:265:23;;;;;:::i;:::-;;:::i;4515:566::-;;;;;;;;;;-1:-1:-1;4515:566:23;;;;;:::i;:::-;;:::i;5794:881:34:-;;;;;;:::i;:::-;;:::i;3733:254::-;;;;;;:::i;:::-;;:::i;1256:66:35:-;;;;;;;;;;;;-1:-1:-1;;;1256:66:35;;8068:150:23;;;;;;;;;;-1:-1:-1;8068:150:23;;;;;:::i;:::-;;:::i;10296:145::-;;;;;;;;;;-1:-1:-1;10296:145:23;;;;;:::i;:::-;;:::i;5410:287:35:-;;;;;;;;;;-1:-1:-1;5410:287:35;;;;;:::i;:::-;;:::i;1708:55::-;;;;;;;;;;-1:-1:-1;1708:55:35;;;;;:::i;:::-;;;;;;;;;;;;;;6707:106:23;;;;;;;;;;-1:-1:-1;6786:20:23;;-1:-1:-1;;;;;6786:20:23;6707:106;;;-1:-1:-1;;;;;5436:32:38;;;5418:51;;5406:2;5391:18;6707:106:23;5272:203:38;2942:93:23;;;;;;;;;;;;;:::i;2854:136:21:-;;;;;;;;;;-1:-1:-1;2854:136:21;;;;;:::i;:::-;;:::i;7432:261:23:-;;;;;;;;;;;;;:::i;:::-;;;;5660:14:38;5701:15;;;5683:34;;5753:15;;;;5748:2;5733:18;;5726:43;5623:18;7432:261:23;5480:295:38;2187:49:21;;;;;;;;;;-1:-1:-1;2187:49:21;2232:4;2187:49;;7068:459:35;;;;;;;;;;-1:-1:-1;7068:459:35;;;;;:::i;:::-;;:::i;7130:229:23:-;;;;;;;;;;;;;:::i;9146:344::-;;;;;;;;;;;;;:::i;6886:171::-;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;6281:32:38;;;6263:51;;6362:14;6350:27;;;6345:2;6330:18;;6323:55;6236:18;6886:171:23;6091:293:38;3563:267:23;;;;;;;;;;-1:-1:-1;3563:267:23;;;;;:::i;:::-;;:::i;8706:128::-;;;;;;;;;;;;;:::i;3056:160:34:-;;;;;;:::i;:::-;3149:60;;;6822:25:38;;;3199:9:34;6878:2:38;6863:18;;6856:34;-1:-1:-1;;;;;3149:60:34;;;3178:1;;3149:60;;6795:18:38;3149:60:34;;;;;;;3056:160;;:::o;2667:219:23:-;2752:4;-1:-1:-1;;;;;;2775:64:23;;-1:-1:-1;;;2775:64:23;;:104;;;2843:36;2867:11;2843:23;:36::i;:::-;2768:111;2667:219;-1:-1:-1;;2667:219:23:o;10927:126::-;2232:4:21;2464:16;2232:4;2464:10;:16::i;:::-;11018:28:23::1;:26;:28::i;:::-;10927:126:::0;:::o;3198:265::-;3317:4;3313:104;;3366:40;;-1:-1:-1;;;3366:40:23;;;;;;;;;;;3313:104;3426:30;3442:4;3448:7;3426:15;:30::i;:::-;3198:265;;:::o;4515:566::-;4637:26;;:55;;;;-1:-1:-1;6786:20:23;;-1:-1:-1;;;;;4667:25:23;;;6786:20;;4667:25;4637:55;4633:399;;;4709:23;4734:15;4753:21;:19;:21::i;:::-;4708:66;;-1:-1:-1;4708:66:23;-1:-1:-1;;;;;;4792:29:23;;;;;:58;;-1:-1:-1;14557:13:23;;;;4792:58;:91;;;-1:-1:-1;14785:15:23;14774:26;;;;4854:29;4792:91;4788:185;;;4910:48;;-1:-1:-1;;;4910:48:23;;671:14:38;659:27;;4910:48:23;;;641:46:38;614:18;;4910:48:23;;;;;;;;4788:185;-1:-1:-1;;4993:28:23;4986:35;;-1:-1:-1;;;;4986:35:23;;;4633:399;5041:33;5060:4;5066:7;5041:18;:33::i;5794:881:34:-;5895:9;5872:20;5914:755;5934:17;;;5914:755;;;6033:1;6006:6;;6013:1;6006:9;;;;;;;:::i;:::-;;;;;;:15;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6006:29:34;;6002:527;;6121:6;;6128:1;6121:9;;;;;;;:::i;:::-;;;;;;:19;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6113:37:34;:55;6151:6;;6158:1;6151:9;;;;;;;:::i;:::-;;;;;;:16;;;6113:55;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6320:6;;6327:1;6320:9;;;;;;;:::i;:::-;;;;;;:16;;;6304:32;;;;;:::i;:::-;;;6002:527;;;6434:6;;6441:1;6434:9;;;;;;;:::i;:::-;;;;;;:15;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6427:36:34;;6464:10;6476:6;;6483:1;6476:9;;;;;;;:::i;:::-;;;;;;:19;;;;;;;;;;:::i;:::-;6497:6;;6504:1;6497:9;;;;;;;:::i;:::-;6427:87;;-1:-1:-1;;;;;;6427:87:34;;;;;;;-1:-1:-1;;;;;7556:15:38;;;6427:87:34;;;7538:34:38;7608:15;;;;7588:18;;;7581:43;-1:-1:-1;6497:16:34;:9;;;;;:16;;7640:18:38;;;7633:34;7473:18;;6427:87:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;6002:527;6620:6;;6627:1;6620:9;;;;;;;:::i;:::-;;;;;;:19;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6567:91:34;6603:6;;6610:1;6603:9;;;;;;;:::i;:::-;;;;;;:15;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6567:91:34;;6578:6;;6585:1;6578:9;;;;;;;:::i;:::-;;;;;;:23;;;6641:6;;6648:1;6641:9;;;;;;;:::i;:::-;;;;;;:16;;;6567:91;;;;;;6822:25:38;;;6878:2;6863:18;;6856:34;6810:2;6795:18;;6648:248;6567:91:34;;;;;;;;5953:3;;5914:755;;;;5862:813;5794:881;;:::o;3733:254::-;3852:61;;-1:-1:-1;;;3852:61:34;;3879:10;3852:61;;;7538:34:38;3899:4:34;7588:18:38;;;7581:43;7640:18;;;7633:34;;;-1:-1:-1;;;;;3852:26:34;;;;;7473:18:38;;3852:61:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;3956:15;-1:-1:-1;;;;;3928:52:34;3949:5;-1:-1:-1;;;;;3928:52:34;;3934:13;3973:6;3928:52;;;;;;6822:25:38;;;6878:2;6863:18;;6856:34;6810:2;6795:18;;6648:248;3928:52:34;;;;;;;;3733:254;;;;:::o;8068:150:23:-;2232:4:21;2464:16;2232:4;2464:10;:16::i;:::-;8175:36:23::1;8202:8;8175:26;:36::i;10296:145::-:0;2232:4:21;2464:16;2232:4;2464:10;:16::i;:::-;10400:34:23::1;10425:8;10400:24;:34::i;5410:287:35:-:0;5611:44;5624:6;5632:13;5647:1;5650;5653;5611:12;:44::i;:::-;5670:20;5680:9;;5670:20;;;;;;;:::i;:::-;;;;;;;;5410:287;;;;;;;:::o;2942:93:23:-;2988:7;3014:14;6786:20;;-1:-1:-1;;;;;6786:20:23;;6707:106;3014:14;3007:21;;2942:93;:::o;2854:136:21:-;2931:4;2954:12;;;;;;;;;;;-1:-1:-1;;;;;2954:29:21;;;;;;;;;;;;;;;2854:136::o;7432:261:23:-;7552:21;;7497:15;;-1:-1:-1;;;7552:21:23;;;;14557:13;;;7591:57;;;;-1:-1:-1;14785:15:23;14774:26;;;;7619:29;7591:57;7590:96;;7681:1;7684;7590:96;;;7653:13;;-1:-1:-1;;;7653:13:23;;;;7668:8;7590:96;7583:103;;;;7432:261;;:::o;7068:459:35:-;7304:20;;7338:15;;;;;7367;;;;;7396:16;;;;;7426:20;;;;;7213:270;;-1:-1:-1;;;7213:270:35;;;8751:33:38;7277:13:35;8800:12:38;;;8793:28;8837:12;;;8830:28;;;;8874:12;;;8867:28;;;;8911:13;;;8904:29;;;;8949:13;;;8942:29;;;;9006:15;-1:-1:-1;;9002:53:38;8987:13;;;8980:76;9072:13;;;;9065:29;;;;7213:270:35;;;;;;;;;;9110:13:38;;;;7213:270:35;;;7502:18;;;;;;7068:459::o;7130:229:23:-;7224:21;;7188:6;;-1:-1:-1;;;7224:21:23;;;;14557:13;;;7263:56;;;;-1:-1:-1;14785:15:23;14774:26;;;;7291:28;7262:90;;7339:13;;-1:-1:-1;;;7339:13:23;;;;7262:90;;;7323:13;;-1:-1:-1;;;7323:13:23;;;;7262:90;7255:97;;;7130:229;:::o;9146:344::-;9210:23;9239:21;:19;:21::i;:::-;-1:-1:-1;9209:51:23;-1:-1:-1;735:10:27;-1:-1:-1;;;;;9274:31:23;;;9270:175;;9388:46;;-1:-1:-1;;;9388:46:23;;735:10:27;9388:46:23;;;5418:51:38;5391:18;;9388:46:23;5272:203:38;9270:175:23;9454:29;:27;:29::i;6886:171::-;6999:20;;-1:-1:-1;;;;;6999:20:23;;;-1:-1:-1;;;7021:28:23;;;;;;6886:171::o;3563:267::-;3683:4;3679:104;;3732:40;;-1:-1:-1;;;3732:40:23;;;;;;;;;;;3679:104;3792:31;3809:4;3815:7;3792:16;:31::i;8706:128::-;2232:4:21;2464:16;2232:4;2464:10;:16::i;:::-;8798:29:23::1;:27;:29::i;2565:202:21:-:0;2650:4;-1:-1:-1;;;;;;2673:47:21;;-1:-1:-1;;;2673:47:21;;:87;;-1:-1:-1;;;;;;;;;;862:40:29;;;2724:36:21;763:146:29;3199:103:21;3265:30;3276:4;735:10:27;3265::21;:30::i;11180:94:23:-;11245:22;11262:1;11265;11245:16;:22::i;:::-;11180:94::o;4226:136:21:-;3875:7;3901:12;;;;;;;;;;:22;;;2464:16;2475:4;2464:10;:16::i;:::-;4330:25:::1;4341:4;4347:7;4330:10;:25::i;5328:245::-:0;-1:-1:-1;;;;;5421:34:21;;735:10:27;5421:34:21;5417:102;;5478:30;;-1:-1:-1;;;5478:30:21;;;;;;;;;;;5417:102;5529:37;5541:4;5547:18;5529:11;:37::i;:::-;;5328:245;;:::o;8345:288:23:-;8426:18;8484:19;:17;:19::i;:::-;8447:34;8465:15;8447:17;:34::i;:::-;:56;;;;:::i;:::-;8426:77;;8513:46;8537:8;8547:11;8513:23;:46::i;:::-;8574:52;;671:14:38;659:27;;641:46;;-1:-1:-1;;;;;8574:52:23;;;;;629:2:38;614:18;8574:52:23;;;;;;;8416:217;8345:288;:::o;10566:::-;10644:18;10702:26;10719:8;10702:16;:26::i;:::-;10665:34;10683:15;10665:17;:34::i;:::-;:63;;;;:::i;:::-;10644:84;;10738:39;10755:8;10765:11;10738:16;:39::i;:::-;10792:55;;;5660:14:38;5701:15;;;5683:34;;5753:15;;5748:2;5733:18;;5726:43;10792:55:23;;5623:18:38;10792:55:23;;;;;;;10634:220;10566:288;:::o;5703:1152:35:-;5931:20;;5894:21;5918:34;;;:12;:34;;;;;:36;;;5894:21;5918:36;;;:::i;:::-;;;;;5894:60;;5985:6;:15;;;5968:13;:32;5964:71;;6009:26;;-1:-1:-1;;;6009:26:35;;;;;1029:25:38;;;1002:18;;6009:26:35;883:177:38;5964:71:35;6121:6;:16;;;6103:15;:34;6099:61;;;6146:14;;-1:-1:-1;;;6146:14:35;;;;;;;;;;;6099:61;6232:19;6254:38;6270:6;6278:13;6254:15;:38::i;:::-;6322:31;;;6302:17;6322:31;;;;;;;;;9682:25:38;;;9755:4;9743:17;;9723:18;;;9716:45;;;;9777:18;;;9770:34;;;9820:18;;;9813:34;;;6232:60:35;;-1:-1:-1;6302:17:35;6322:31;;9654:19:38;;6322:31:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6302:51;;6440:34;-1:-1:-1;;;6464:9:35;6440:7;:34::i;:::-;6435:71;;6483:23;;-1:-1:-1;;;6483:23:35;;-1:-1:-1;;;;;5436:32:38;;6483:23:35;;;5418:51:38;5391:18;;6483:23:35;5272:203:38;6435:71:35;6621:20;;6600:42;;;;:20;:42;;;;;;6646:12;6600:58;;6596:99;;6667:28;;-1:-1:-1;;;6667:28:35;;;;;;;;;;;6596:99;6726:20;;6705:42;;;;:20;:42;;;;;;;6750:12;6705:57;;6800:48;;;6726:6;;10059:13:38;;10047:26;;10123:4;10111:17;;;10105:24;10089:14;;;10082:48;10178:2;10166:15;;;10160:22;10146:12;;;10139:44;10231:2;10219:15;;;10213:22;10199:12;;;10192:44;10289:3;10277:16;;;10271:23;-1:-1:-1;;;;;10267:49:38;10252:13;;;10245:72;;;;10304:3;10333:13;;9858:494;6800:48:35;;;;;;;;6815:9;-1:-1:-1;;;;;6800:48:35;;6834:13;6800:48;;;;1029:25:38;;1017:2;1002:18;;883:177;6800:48:35;;;;;;;;5815:1040;;;5703:1152;;;;;:::o;9618:474:23:-;9685:16;9703:15;9722:21;:19;:21::i;:::-;9684:59;;;;9758:24;9773:8;14557:13;;;;;14471:106;9758:24;9757:25;:58;;;-1:-1:-1;14785:15:23;14774:26;;;;9786:29;9757:58;9753:144;;;9838:48;;-1:-1:-1;;;9838:48:23;;671:14:38;659:27;;9838:48:23;;;641:46:38;614:18;;9838:48:23;497:196:38;9753:144:23;9906:47;2232:4:21;9938:14:23;6786:20;;-1:-1:-1;;;;;6786:20:23;;6707:106;9938:14;9906:11;:47::i;:::-;-1:-1:-1;9963:40:23;2232:4:21;9994:8:23;9963:10;:40::i;:::-;-1:-1:-1;;10020:20:23;10013:27;;-1:-1:-1;;;;;;10050:35:23;;;-1:-1:-1;9618:474:23:o;4642:138:21:-;3875:7;3901:12;;;;;;;;;;:22;;;2464:16;2475:4;2464:10;:16::i;:::-;4747:26:::1;4759:4;4765:7;4747:11;:26::i;8962:111:23:-:0;9028:38;9060:1;9064;9028:23;:38::i;3432:197:21:-;3520:22;3528:4;3534:7;3520;:22::i;:::-;3515:108;;3565:47;;-1:-1:-1;;;3565:47:21;;-1:-1:-1;;;;;10549:32:38;;3565:47:21;;;10531:51:38;10598:18;;;10591:34;;;10504:18;;3565:47:21;10357:274:38;13741:585:23;13843:21;;-1:-1:-1;;;13843:21:23;;;;14557:13;;13875:365;;14785:15;14774:26;;;;13922:308;;;14040:13;;14024;:29;;-1:-1:-1;;;;;14024:29:23;-1:-1:-1;;;14040:13:23;;;;;-1:-1:-1;;;14024:29:23;;;;;;;13922:308;;;14182:33;;;;;;;13922:308;-1:-1:-1;14250:13:23;:24;;-1:-1:-1;;;;;14284:35:23;-1:-1:-1;;;14250:24:23;;;;;-1:-1:-1;;;;;14284:35:23;;-1:-1:-1;;;14284:35:23;;;;;;;;;;;;;;13741:585::o;5509:370::-;5595:4;5615;5611:214;;5687:1;5661:14;6786:20;;-1:-1:-1;;;;;6786:20:23;;6707:106;5661:14;-1:-1:-1;;;;;5661:28:23;;5657:114;;5716:40;;-1:-1:-1;;;5716:40:23;;;;;;;;;;;5657:114;5784:20;:30;;-1:-1:-1;;;;;;5784:30:23;-1:-1:-1;;;;;5784:30:23;;;;;5611:214;5841:31;5858:4;5864:7;5841:16;:31::i;:::-;5834:38;5509:370;-1:-1:-1;;;5509:370:23:o;5946:271::-;6033:4;6053:26;;:55;;;;-1:-1:-1;6786:20:23;;-1:-1:-1;;;;;6083:25:23;;;6786:20;;6083:25;6053:55;6049:113;;;6131:20;6124:27;;-1:-1:-1;;;;;;6124:27:23;;;6049:113;6178:32;6196:4;6202:7;6178:17;:32::i;14296:213:32:-;14352:6;14382:16;14374:24;;14370:103;;;14421:41;;-1:-1:-1;;;14421:41:32;;14452:2;14421:41;;;10817:36:38;10869:18;;;10862:34;;;10790:18;;14421:41:32;10636:266:38;14370:103:32;-1:-1:-1;14496:5:32;14296:213::o;13062:525:23:-;13154:18;13176:21;:19;:21::i;:::-;13208:20;:31;;13249:42;;;-1:-1:-1;;;13249:42:23;-1:-1:-1;;;;;;13249:42:23;;;-1:-1:-1;;;;;13208:31:23;;13249:42;;;;13151:46;-1:-1:-1;13403:27:23;;-1:-1:-1;13151:46:23;14557:13;;;;;14471:106;13403:27;13399:182;;;13540:30;;;;;;;13141:446;13062:525;;:::o;11621:1249::-;11695:6;11713:19;11735;:17;:19::i;:::-;11713:41;;12684:12;12673:23;;:8;:23;;;:190;;12840:23;12855:8;12840:12;:23;:::i;:::-;12673:190;;;12722:51;;;;7861:6;3429:7:31;3066:5;;;3463;;;3065:36;3060:42;;3455:20;2825:294;6179:316:21;6256:4;6277:22;6285:4;6291:7;6277;:22::i;:::-;6272:217;;6315:6;:12;;;;;;;;;;;-1:-1:-1;;;;;6315:29:21;;;;;;;;;:36;;-1:-1:-1;;6315:36:21;6347:4;6315:36;;;6397:12;735:10:27;;656:96;6397:12:21;-1:-1:-1;;;;;6370:40:21;6388:7;-1:-1:-1;;;;;6370:40:21;6382:4;6370:40;;;;;;;;;;-1:-1:-1;6431:4:21;6424:11;;6272:217;-1:-1:-1;6473:5:21;6466:12;;6730:317;6808:4;6828:22;6836:4;6842:7;6828;:22::i;:::-;6824:217;;;6898:5;6866:12;;;;;;;;;;;-1:-1:-1;;;;;6866:29:21;;;;;;;;;;:37;;-1:-1:-1;;6866:37:21;;;6922:40;735:10:27;;6866:12:21;;6922:40;;6898:5;6922:40;-1:-1:-1;6983:4:21;6976:11;;14:286:38;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:38;;209:43;;199:71;;266:1;263;256:12;698:180;757:6;810:2;798:9;789:7;785:23;781:32;778:52;;;826:1;823;816:12;778:52;-1:-1:-1;849:23:38;;698:180;-1:-1:-1;698:180:38:o;1432:173::-;1500:20;;-1:-1:-1;;;;;1549:31:38;;1539:42;;1529:70;;1595:1;1592;1585:12;1529:70;1432:173;;;:::o;1610:254::-;1678:6;1686;1739:2;1727:9;1718:7;1714:23;1710:32;1707:52;;;1755:1;1752;1745:12;1707:52;1791:9;1778:23;1768:33;;1820:38;1854:2;1843:9;1839:18;1820:38;:::i;:::-;1810:48;;1610:254;;;;;:::o;1869:645::-;1985:6;1993;2046:2;2034:9;2025:7;2021:23;2017:32;2014:52;;;2062:1;2059;2052:12;2014:52;2102:9;2089:23;2131:18;2172:2;2164:6;2161:14;2158:34;;;2188:1;2185;2178:12;2158:34;2226:6;2215:9;2211:22;2201:32;;2271:7;2264:4;2260:2;2256:13;2252:27;2242:55;;2293:1;2290;2283:12;2242:55;2333:2;2320:16;2359:2;2351:6;2348:14;2345:34;;;2375:1;2372;2365:12;2345:34;2428:7;2423:2;2413:6;2410:1;2406:14;2402:2;2398:23;2394:32;2391:45;2388:65;;;2449:1;2446;2439:12;2388:65;2480:2;2472:11;;;;;2502:6;;-1:-1:-1;1869:645:38;;-1:-1:-1;;;;1869:645:38:o;2519:397::-;2605:6;2613;2621;2629;2682:3;2670:9;2661:7;2657:23;2653:33;2650:53;;;2699:1;2696;2689:12;2650:53;2735:9;2722:23;2712:33;;2764:38;2798:2;2787:9;2783:18;2764:38;:::i;:::-;2754:48;;2821:38;2855:2;2844:9;2840:18;2821:38;:::i;:::-;2519:397;;;;-1:-1:-1;2811:48:38;;2906:2;2891:18;2878:32;;-1:-1:-1;;2519:397:38:o;2921:186::-;2980:6;3033:2;3021:9;3012:7;3008:23;3004:32;3001:52;;;3049:1;3046;3039:12;3001:52;3072:29;3091:9;3072:29;:::i;3112:280::-;3170:6;3223:2;3211:9;3202:7;3198:23;3194:32;3191:52;;;3239:1;3236;3229:12;3191:52;3278:9;3265:23;3328:14;3321:5;3317:26;3310:5;3307:37;3297:65;;3358:1;3355;3348:12;3397:779;3455:5;3503:4;3491:9;3486:3;3482:19;3478:30;3475:50;;;3521:1;3518;3511:12;3475:50;3554:2;3548:9;3596:4;3588:6;3584:17;3667:6;3655:10;3652:22;3631:18;3619:10;3616:34;3613:62;3610:185;;;3717:10;3712:3;3708:20;3705:1;3698:31;3752:4;3749:1;3742:15;3780:4;3777:1;3770:15;3610:185;3815:10;3811:2;3804:22;;3844:6;3835:15;;3887:9;3874:23;3866:6;3859:39;3959:2;3948:9;3944:18;3931:32;3926:2;3918:6;3914:15;3907:57;4025:2;4014:9;4010:18;3997:32;3992:2;3984:6;3980:15;3973:57;4091:2;4080:9;4076:18;4063:32;4058:2;4050:6;4046:15;4039:57;4130:39;4164:3;4153:9;4149:19;4130:39;:::i;:::-;4124:3;4116:6;4112:16;4105:65;;3397:779;;;;:::o;4181:1086::-;4324:6;4332;4340;4348;4356;4364;4372;4425:3;4413:9;4404:7;4400:23;4396:33;4393:53;;;4442:1;4439;4432:12;4393:53;4465:49;4506:7;4495:9;4465:49;:::i;:::-;4455:59;;4561:3;4550:9;4546:19;4533:33;4523:43;;4616:3;4605:9;4601:19;4588:33;4661:4;4654:5;4650:16;4643:5;4640:27;4630:55;;4681:1;4678;4671:12;4630:55;4704:5;-1:-1:-1;4756:3:38;4741:19;;4728:33;;-1:-1:-1;4808:3:38;4793:19;;4780:33;;-1:-1:-1;4864:3:38;4849:19;;4836:33;4888:18;4918:14;;;4915:34;;;4945:1;4942;4935:12;4915:34;4983:6;4972:9;4968:22;4958:32;;5028:7;5021:4;5017:2;5013:13;5009:27;4999:55;;5050:1;5047;5040:12;4999:55;5090:2;5077:16;5116:2;5108:6;5105:14;5102:34;;;5132:1;5129;5122:12;5102:34;5179:7;5172:4;5163:6;5159:2;5155:15;5151:26;5148:39;5145:59;;;5200:1;5197;5190:12;5145:59;5231:4;5227:2;5223:13;5213:23;;5255:6;5245:16;;;;;4181:1086;;;;;;;;;;:::o;5780:306::-;5878:6;5886;5939:3;5927:9;5918:7;5914:23;5910:33;5907:53;;;5956:1;5953;5946:12;5907:53;5979:49;6020:7;6009:9;5979:49;:::i;:::-;5969:59;6075:3;6060:19;;;;6047:33;;-1:-1:-1;;;5780:306:38:o;6901:127::-;6962:10;6957:3;6953:20;6950:1;6943:31;6993:4;6990:1;6983:15;7017:4;7014:1;7007:15;7033:127;7094:10;7089:3;7085:20;7082:1;7075:31;7125:4;7122:1;7115:15;7149:4;7146:1;7139:15;7165:128;7232:9;;;7253:11;;;7250:37;;;7267:18;;:::i;7678:277::-;7745:6;7798:2;7786:9;7777:7;7773:23;7769:32;7766:52;;;7814:1;7811;7804:12;7766:52;7846:9;7840:16;7899:5;7892:13;7885:21;7878:5;7875:32;7865:60;;7921:1;7918;7911:12;7960:388;8117:2;8106:9;8099:21;8156:6;8151:2;8140:9;8136:18;8129:34;8213:6;8205;8200:2;8189:9;8185:18;8172:48;8269:1;8240:22;;;8264:2;8236:31;;;8229:42;;;;8332:2;8311:15;;;-1:-1:-1;;8307:29:38;8292:45;8288:54;;7960:388;-1:-1:-1;7960:388:38:o;9134:176::-;9201:14;9235:10;;;9247;;;9231:27;;9270:11;;;9267:37;;;9284:18;;:::i;:::-;9267:37;9134:176;;;;:::o;9315:135::-;9354:3;9375:17;;;9372:43;;9395:18;;:::i;:::-;-1:-1:-1;9442:1:38;9431:13;;9315:135::o;10907:179::-;10975:14;11022:10;;;11010;;;11006:27;;11045:12;;;11042:38;;;11060:18;;:::i","linkReferences":{},"immutableReferences":{"49726":[{"start":398,"length":32},{"start":442,"length":32}]}},"methodIdentifiers":{"DEFAULT_ADMIN_ROLE()":"a217fddf","SEQUENCER_ROLE()":"4842855c","acceptDefaultAdminTransfer()":"cefc1429","beginDefaultAdminTransfer(address)":"634e93da","blockCommitment((uint256,uint256,uint256,uint256,address),bytes32)":"c7bc4a62","cancelDefaultAdminTransfer()":"d602b9fd","changeDefaultAdminDelay(uint48)":"649a5ec7","defaultAdmin()":"84ef8ffc","defaultAdminDelay()":"cc8463c8","defaultAdminDelayIncreaseWait()":"022d63fb","enter(uint256,address)":"ea3b9ba1","enter(uint256,address,address,uint256)":"3805c6bd","fulfillExits((uint256,address,address,uint256)[])":"36702119","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","lastSubmittedAtBlock(uint256)":"7e82bb01","nextSequence(uint256)":"1e663720","owner()":"8da5cb5b","pendingDefaultAdmin()":"cf6eefb7","pendingDefaultAdminDelay()":"a1eda53c","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","rollbackDefaultAdminDelay()":"0aa6220b","submitBlock((uint256,uint256,uint256,uint256,address),bytes32,uint8,bytes32,bytes32,bytes)":"7e569274","supportsInterface(bytes4)":"01ffc9a7"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"defaultRollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint48\",\"name\":\"schedule\",\"type\":\"uint48\"}],\"name\":\"AccessControlEnforcedDefaultAdminDelay\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AccessControlEnforcedDefaultAdminRules\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"defaultAdmin\",\"type\":\"address\"}],\"name\":\"AccessControlInvalidDefaultAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"}],\"name\":\"BadSequence\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"derivedSequencer\",\"type\":\"address\"}],\"name\":\"BadSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BlockExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OneRollupBlockPerHostBlock\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OrderExpired\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"bits\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"SafeCastOverflowedUintDowncast\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"blockData\",\"type\":\"bytes\"}],\"name\":\"BlockData\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sequence\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"confirmBy\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"rewardAddress\",\"type\":\"address\"}],\"indexed\":true,\"internalType\":\"struct Zenith.BlockHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockDataHash\",\"type\":\"bytes32\"}],\"name\":\"BlockSubmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"DefaultAdminDelayChangeCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint48\",\"name\":\"newDelay\",\"type\":\"uint48\"},{\"indexed\":false,\"internalType\":\"uint48\",\"name\":\"effectSchedule\",\"type\":\"uint48\"}],\"name\":\"DefaultAdminDelayChangeScheduled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"DefaultAdminTransferCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint48\",\"name\":\"acceptSchedule\",\"type\":\"uint48\"}],\"name\":\"DefaultAdminTransferScheduled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rollupRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Enter\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"hostRecipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ExitFilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SEQUENCER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptDefaultAdminTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"beginDefaultAdminTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sequence\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"confirmBy\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"rewardAddress\",\"type\":\"address\"}],\"internalType\":\"struct Zenith.BlockHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"blockDataHash\",\"type\":\"bytes32\"}],\"name\":\"blockCommitment\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"commit\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"cancelDefaultAdminTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint48\",\"name\":\"newDelay\",\"type\":\"uint48\"}],\"name\":\"changeDefaultAdminDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultAdminDelay\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"defaultAdminDelayIncreaseWait\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"rollupRecipient\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"enter\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"rollupRecipient\",\"type\":\"address\"}],\"name\":\"enter\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct Passage.ExitOrder[]\",\"name\":\"orders\",\"type\":\"tuple[]\"}],\"name\":\"fulfillExits\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"lastSubmittedAtBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"nextSequence\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingDefaultAdmin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"},{\"internalType\":\"uint48\",\"name\":\"schedule\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingDefaultAdminDelay\",\"outputs\":[{\"internalType\":\"uint48\",\"name\":\"newDelay\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"schedule\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"rollbackDefaultAdminDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rollupChainId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sequence\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"confirmBy\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"rewardAddress\",\"type\":\"address\"}],\"internalType\":\"struct Zenith.BlockHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"blockDataHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"blockData\",\"type\":\"bytes\"}],\"name\":\"submitBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlEnforcedDefaultAdminDelay(uint48)\":[{\"details\":\"The delay for transferring the default admin delay is enforced and the operation must wait until `schedule`. NOTE: `schedule` can be 0 indicating there's no transfer scheduled.\"}],\"AccessControlEnforcedDefaultAdminRules()\":[{\"details\":\"At least one of the following rules was violated: - The `DEFAULT_ADMIN_ROLE` must only be managed by itself. - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time. - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.\"}],\"AccessControlInvalidDefaultAdmin(address)\":[{\"details\":\"The new default admin is not a valid default admin.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"BadSequence(uint256)\":[{\"details\":\"Blocks must be submitted in strict monotonic increasing order.\",\"params\":{\"expected\":\"- the correct next sequence number for the given rollup chainId.\"}}],\"BadSignature(address)\":[{\"params\":{\"derivedSequencer\":\"- the derived signer of the block data that is not a permissioned sequencer.\"}}],\"SafeCastOverflowedUintDowncast(uint8,uint256)\":[{\"details\":\"Value doesn't fit in an uint of `bits` size.\"}]},\"events\":{\"BlockSubmitted(address,(uint256,uint256,uint256,uint256,address),bytes32)\":{\"params\":{\"blockDataHash\":\"- keccak256(blockData). the Node will discard the block if the hash doens't match.\",\"header\":\"- the block header information for the block.\",\"sequencer\":\"- the address of the sequencer that signed the block.\"}},\"DefaultAdminDelayChangeCanceled()\":{\"details\":\"Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.\"},\"DefaultAdminDelayChangeScheduled(uint48,uint48)\":{\"details\":\"Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next delay to be applied between default admin transfer after `effectSchedule` has passed.\"},\"DefaultAdminTransferCanceled()\":{\"details\":\"Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.\"},\"DefaultAdminTransferScheduled(address,uint48)\":{\"details\":\"Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule` passes.\"},\"Enter(uint256,address,address,uint256)\":{\"params\":{\"amount\":\"- The amount of the token entering the rollup.\",\"rollupRecipient\":\"- The recipient of the token on the rollup.\",\"token\":\"- The address of the token entering the rollup.\"}},\"ExitFilled(uint256,address,address,uint256)\":{\"params\":{\"amount\":\"- The amount of the token transferred to the recipient.\",\"hostRecipient\":\"- The recipient of the token on host.\",\"token\":\"- The address of the token transferred to the recipient.\"}},\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted signaling this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"}},\"kind\":\"dev\",\"methods\":{\"acceptDefaultAdminTransfer()\":{\"details\":\"Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. After calling the function: - `DEFAULT_ADMIN_ROLE` should be granted to the caller. - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder. - {pendingDefaultAdmin} should be reset to zero values. Requirements: - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`. - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.\"},\"beginDefaultAdminTransfer(address)\":{\"details\":\"Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance after the current timestamp plus a {defaultAdminDelay}. Requirements: - Only can be called by the current {defaultAdmin}. Emits a DefaultAdminRoleChangeStarted event.\"},\"blockCommitment((uint256,uint256,uint256,uint256,address),bytes32)\":{\"params\":{\"header\":\"- the header information for the rollup block.\"},\"returns\":{\"commit\":\"- the hash of the encoded block details.\"}},\"cancelDefaultAdminTransfer()\":{\"details\":\"Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function. Requirements: - Only can be called by the current {defaultAdmin}. May emit a DefaultAdminTransferCanceled event.\"},\"changeDefaultAdminDelay(uint48)\":{\"details\":\"Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting into effect after the current timestamp plus a {defaultAdminDelay}. This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay} set before calling. The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin} complete transfer (including acceptance). The schedule is designed for two scenarios: - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by {defaultAdminDelayIncreaseWait}. - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change. Requirements: - Only can be called by the current {defaultAdmin}. Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.\"},\"constructor\":{\"details\":\"See `AccessControlDefaultAdminRules` for information on contract administration. - Admin role can grant and revoke Sequencer roles. - Admin role can be transferred via two-step process with a 1 day timelock.\",\"params\":{\"admin\":\"- the address that will be the initial admin.\"}},\"defaultAdmin()\":{\"details\":\"Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.\"},\"defaultAdminDelay()\":{\"details\":\"Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started. This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set the acceptance schedule. NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function returns the new delay. See {changeDefaultAdminDelay}.\"},\"defaultAdminDelayIncreaseWait()\":{\"details\":\"Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay}) to take effect. Default to 5 days. When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overrode for a custom {defaultAdminDelay} increase scheduling. IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise, there's a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).\"},\"enter(uint256,address)\":{\"custom:emits\":\"Enter indicating the amount of Ether to mint on the rollup & its recipient.\",\"details\":\"Permanently burns the entire msg.value by locking it in this contract.\",\"params\":{\"rollupChainId\":\"- The rollup chain to enter.\",\"rollupRecipient\":\"- The recipient of the Ether on the rollup.\"}},\"enter(uint256,address,address,uint256)\":{\"custom:emits\":\"Enter indicating the amount of tokens to mint on the rollup & its recipient.\",\"details\":\"Permanently burns the token amount by locking it in this contract.\",\"params\":{\"amount\":\"- The amount of the ERC20 token to transfer to the rollup.\",\"rollupChainId\":\"- The rollup chain to enter.\",\"rollupRecipient\":\"- The recipient of the Ether on the rollup.\",\"token\":\"- The address of the ERC20 token on the Host.\"}},\"fulfillExits((uint256,address,address,uint256)[])\":{\"custom:emits\":\"ExitFilled for each exit order fulfilled.\",\"details\":\"Builder SHOULD call `filfillExits` atomically with `submitBlock`. Builder SHOULD set a block expiration time that is AT MOST the minimum of all exit order deadlines; this way, `fulfillExits` + `submitBlock` will revert atomically on mainnet if any exit orders have expired. Otherwise, `filfillExits` may mine on mainnet, while `submitExit` reverts on the rollup, and the Builder can't collect the corresponding value on the rollup.Called by the Builder atomically with a transaction calling `submitBlock`. The user-submitted transactions initiating the ExitOrders on the rollup must be included by the Builder in the rollup block submitted via `submitBlock`.The user transfers tokenIn on the rollup, and receives tokenOut on host.The Builder receives tokenIn on the rollup, and transfers tokenOut to the user on host.The rollup STF MUST NOT apply `submitExit` transactions to the rollup state UNLESS a corresponding ExitFilled event is emitted on host in the same block.If the user submits multiple exit transactions for the same token in the same rollup block, the Builder may transfer the cumulative tokenOut to the user in a single ExitFilled event. The rollup STF will apply the user's exit transactions on the rollup up to the point that sum(tokenOut) is lte the ExitFilled amount. TODO: add option to fulfill ExitOrders with native ETH? or is it sufficient to only allow users to exit via WETH?\",\"params\":{\"orders\":\"The exit orders to fulfill\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"owner()\":{\"details\":\"See {IERC5313-owner}.\"},\"pendingDefaultAdmin()\":{\"details\":\"Returns a tuple of a `newAdmin` and an accept schedule. After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role by calling {acceptDefaultAdminTransfer}, completing the role transfer. A zero value only in `acceptSchedule` indicates no pending admin transfer. NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.\"},\"pendingDefaultAdminDelay()\":{\"details\":\"Returns a tuple of `newDelay` and an effect schedule. After the `schedule` passes, the `newDelay` will get into effect immediately for every new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}. A zero value only in `effectSchedule` indicates no pending delay change. NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay} will be zero after the effect schedule.\"},\"renounceRole(bytes32,address)\":{\"details\":\"See {AccessControl-renounceRole}. For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule has also passed when calling this function. After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions. NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin}, thereby disabling any functionality that is only available for it, and the possibility of reassigning a non-administrated role.\"},\"revokeRole(bytes32,address)\":{\"details\":\"See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\"},\"rollbackDefaultAdminDelay()\":{\"details\":\"Cancels a scheduled {defaultAdminDelay} change. Requirements: - Only can be called by the current {defaultAdmin}. May emit a DefaultAdminDelayChangeCanceled event.\"},\"submitBlock((uint256,uint256,uint256,uint256,address),bytes32,uint8,bytes32,bytes32,bytes)\":{\"custom:emits\":\"BlockSubmitted if the block is successfully submitted.BlockData to expose the block calldata; as a convenience until calldata tracing is implemented in the Node.\",\"custom:reverts\":\"BadSequence if the sequence number is not the next block for the given rollup chainId.BlockExpired if the confirmBy time has passed.BadSignature if the signer is not a permissioned sequencer, OR if the signature provided commits to a different header.OneRollupBlockPerHostBlock if attempting to submit a second rollup block within one host block.\",\"details\":\"Blocks are submitted by Builders, with an attestation to the block data signed by a Sequencer.including blockDataHash allows the sequencer to sign over finalized block data, without needing to calldatacopy the `blockData` param.\",\"params\":{\"blockData\":\"- block data information. could be packed blob hashes, or direct rlp-encoded transctions. blockData is ignored by the contract logic.\",\"blockDataHash\":\"- keccak256(blockData). the Node will discard the block if the hash doens't match.\",\"header\":\"- the header information for the rollup block.\",\"r\":\"- the r component of the Sequencer's ECSDA signature over the block header.\",\"s\":\"- the s component of the Sequencer's ECSDA signature over the block header.\",\"v\":\"- the v component of the Sequencer's ECSDA signature over the block header.\"}},\"supportsInterface(bytes4)\":{\"details\":\"See {IERC165-supportsInterface}.\"}},\"version\":1},\"userdoc\":{\"errors\":{\"BadSequence(uint256)\":[{\"notice\":\"Thrown when a block submission is attempted with a sequence number that is not the next block for the rollup chainId.\"}],\"BadSignature(address)\":[{\"notice\":\"Thrown when a block submission is attempted with a signature by a non-permissioned sequencer, OR when signature is produced over different data than is provided.\"}],\"BlockExpired()\":[{\"notice\":\"Thrown when a block submission is attempted when the confirmBy time has passed.\"}],\"OneRollupBlockPerHostBlock()\":[{\"notice\":\"Thrown when attempting to submit more than one rollup block per host block\"}],\"OrderExpired()\":[{\"notice\":\"Thrown when attempting to fulfill an exit order with a deadline that has passed.\"}]},\"events\":{\"BlockData(bytes)\":{\"notice\":\"Emit the entire block data for easy visibility\"},\"BlockSubmitted(address,(uint256,uint256,uint256,uint256,address),bytes32)\":{\"notice\":\"Emitted when a new rollup block is successfully submitted.\"},\"Enter(uint256,address,address,uint256)\":{\"notice\":\"Emitted when tokens enter the rollup.\"},\"ExitFilled(uint256,address,address,uint256)\":{\"notice\":\"Emitted when an exit order is fulfilled by the Builder.\"}},\"kind\":\"user\",\"methods\":{\"SEQUENCER_ROLE()\":{\"notice\":\"Role that allows a key to sign commitments to rollup blocks.\"},\"blockCommitment((uint256,uint256,uint256,uint256,address),bytes32)\":{\"notice\":\"Construct hash of block details that the sequencer signs.\"},\"constructor\":{\"notice\":\"Initializes the Admin role.\"},\"enter(uint256,address)\":{\"notice\":\"Allows native Ether to enter the rollup.\"},\"enter(uint256,address,address,uint256)\":{\"notice\":\"Allows ERC20s to enter the rollup.\"},\"fulfillExits((uint256,address,address,uint256)[])\":{\"notice\":\"Fulfills exit orders by transferring tokenOut to the recipient\"},\"lastSubmittedAtBlock(uint256)\":{\"notice\":\"The host block number that a block was last submitted at for a given rollup chainId. rollupChainId => host blockNumber that block was last submitted at\"},\"nextSequence(uint256)\":{\"notice\":\"The sequence number of the next block that can be submitted for a given rollup chainId. rollupChainId => nextSequence number\"},\"submitBlock((uint256,uint256,uint256,uint256,address),bytes32,uint8,bytes32,bytes32,bytes)\":{\"notice\":\"Submit a rollup block with block data submitted via calldata.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Zenith.sol\":\"Zenith\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts/contracts/access/AccessControl.sol\":{\"keccak256\":\"0xa0e92d42942f4f57c5be50568dac11e9d00c93efcb458026e18d2d9b9b2e7308\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://46326c0bb1e296b67185e81c918e0b40501b8b6386165855df0a3f3c634b6a80\",\"dweb:/ipfs/QmTwyrDYtsxsk6pymJTK94PnEpzsmkpUxFuzEiakDopy4Z\"]},\"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60\",\"dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y\"]},\"lib/openzeppelin-contracts/contracts/access/extensions/AccessControlDefaultAdminRules.sol\":{\"keccak256\":\"0xd5e43578dce2678fbd458e1221dc37b20e983ecce4a314b422704f07d6015c5b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9ea4d9ae3392dc9db1ef4d7ebef84ce7fa243dc14abb46e68eb2eb60d2cd0e93\",\"dweb:/ipfs/QmRfjyDoLWF74EgmpcGkWZM7Kx1LgHN8dZHBxAnU9vPH46\"]},\"lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sol\":{\"keccak256\":\"0xc2dbeddf97707bf012827013b4a072bacbe56ad3219c405e30fd2a959e8a5413\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://281289e424c30c2ea92fc25598315117410404cf76a756663ad39ba18fd38b48\",\"dweb:/ipfs/Qma3wmq2cjxpfkKKM7JrvyJzzohsNWNNWsnaf3jVNBD65v\"]},\"lib/openzeppelin-contracts/contracts/interfaces/IERC5313.sol\":{\"keccak256\":\"0x22412c268e74cc3cbf550aecc2f7456f6ac40783058e219cfe09f26f4d396621\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0b841021f25480424d2359de4869e60e77f790f52e8e85f07aa389543024b559\",\"dweb:/ipfs/QmV7U5ehV5xe3QrbE8ErxfWSSzK1T1dGeizXvYPjWpNDGq\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009\",\"dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN\"]},\"lib/openzeppelin-contracts/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]},\"lib/openzeppelin-contracts/contracts/utils/Panic.sol\":{\"keccak256\":\"0x29074fe5a74bb024c57b3570abf6c74d8bceed3438694d470fd0166a3ecd196a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f4f8435ccbc56e384f4cc9ac9ff491cf30a82f2beac00e33ccc2cf8af3f77cc3\",\"dweb:/ipfs/QmUKJXxTe6nn1qfgnX8xbnboNNAPUuEmJyGqMZCKNiFBgn\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x6fac27fb1885a1d9fd2ce3f8fac4e44a6596ca4d44207c9ef2541ba8c941291e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2079378abdb36baec15c23bc2353b73a3d28d1d0610b436b0c1c4e6fa61d65c9\",\"dweb:/ipfs/QmVZkRFMzKW7sLaugKSTbMNnUBKWF3QDsoMi5uoQFyVMjf\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e\",\"dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX\"]},\"lib/openzeppelin-contracts/contracts/utils/math/Math.sol\":{\"keccak256\":\"0x3233b02fcf2b20a41cce60a62e43c7e5a67a55b738ec1db842a82452e6aa170d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://231c75d18bc6973533dfa7d58d2b97e504ca4e21d703a5c8b0ec31475e97db67\",\"dweb:/ipfs/QmPJ29HDuFceD1FDr4CnjYYtvaQ234wGAfojZpL3RXFG26\"]},\"lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol\":{\"keccak256\":\"0x8cd59334ed58b8884cd1f775afc9400db702e674e5d6a7a438c655b9de788d7e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://99e62c7de7318f413b6352e3f2704ca23e7725ff144e43c8bd574d12dbf29047\",\"dweb:/ipfs/QmSEXG2rBx1VxU2uFTWdiChjDvA4osEY2mesjmoVeVhHko\"]},\"src/Passage.sol\":{\"keccak256\":\"0x81016c92006558f93c028e3d4f61ddad8ff870b956edaa19ad2ccd68ec5d292a\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://dc70a7d97b18e988ce9560f4fabbf9caea3c6178f64fab056b1cf63d27bef6c5\",\"dweb:/ipfs/QmeJDLqvLdhkbWfyLHdYUPoGz7XHWw3zpe8YTCMQE9MacX\"]},\"src/Zenith.sol\":{\"keccak256\":\"0x0febef21c15ebf62421e25337341a8a11a6dd5b5dc2e9ea967a2d4769469ecd6\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://405a8eb90f834ab216e96d40b8c1cfd98c4bc4e71399b09c04ef4123eb3bb1ab\",\"dweb:/ipfs/QmVakr7Upoe2tgU1jQSZUgXE1UASAuHh9kReZ2mfgCsdha\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.25+commit.b61c2a91"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"uint256","name":"defaultRollupChainId","type":"uint256"},{"internalType":"address","name":"admin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"uint48","name":"schedule","type":"uint48"}],"type":"error","name":"AccessControlEnforcedDefaultAdminDelay"},{"inputs":[],"type":"error","name":"AccessControlEnforcedDefaultAdminRules"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"}],"type":"error","name":"AccessControlInvalidDefaultAdmin"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"}],"type":"error","name":"BadSequence"},{"inputs":[{"internalType":"address","name":"derivedSequencer","type":"address"}],"type":"error","name":"BadSignature"},{"inputs":[],"type":"error","name":"BlockExpired"},{"inputs":[],"type":"error","name":"OneRollupBlockPerHostBlock"},{"inputs":[],"type":"error","name":"OrderExpired"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"type":"error","name":"SafeCastOverflowedUintDowncast"},{"inputs":[{"internalType":"bytes","name":"blockData","type":"bytes","indexed":false}],"type":"event","name":"BlockData","anonymous":false},{"inputs":[{"internalType":"address","name":"sequencer","type":"address","indexed":true},{"internalType":"struct Zenith.BlockHeader","name":"header","type":"tuple","components":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"uint256","name":"sequence","type":"uint256"},{"internalType":"uint256","name":"confirmBy","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"address","name":"rewardAddress","type":"address"}],"indexed":true},{"internalType":"bytes32","name":"blockDataHash","type":"bytes32","indexed":false}],"type":"event","name":"BlockSubmitted","anonymous":false},{"inputs":[],"type":"event","name":"DefaultAdminDelayChangeCanceled","anonymous":false},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48","indexed":false},{"internalType":"uint48","name":"effectSchedule","type":"uint48","indexed":false}],"type":"event","name":"DefaultAdminDelayChangeScheduled","anonymous":false},{"inputs":[],"type":"event","name":"DefaultAdminTransferCanceled","anonymous":false},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address","indexed":true},{"internalType":"uint48","name":"acceptSchedule","type":"uint48","indexed":false}],"type":"event","name":"DefaultAdminTransferScheduled","anonymous":false},{"inputs":[{"internalType":"uint256","name":"rollupChainId","type":"uint256","indexed":false},{"internalType":"address","name":"token","type":"address","indexed":true},{"internalType":"address","name":"rollupRecipient","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"Enter","anonymous":false},{"inputs":[{"internalType":"uint256","name":"rollupChainId","type":"uint256","indexed":false},{"internalType":"address","name":"token","type":"address","indexed":true},{"internalType":"address","name":"hostRecipient","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"ExitFilled","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[],"stateMutability":"payable","type":"fallback"},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"SEQUENCER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"acceptDefaultAdminTransfer"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"beginDefaultAdminTransfer"},{"inputs":[{"internalType":"struct Zenith.BlockHeader","name":"header","type":"tuple","components":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"uint256","name":"sequence","type":"uint256"},{"internalType":"uint256","name":"confirmBy","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"address","name":"rewardAddress","type":"address"}]},{"internalType":"bytes32","name":"blockDataHash","type":"bytes32"}],"stateMutability":"view","type":"function","name":"blockCommitment","outputs":[{"internalType":"bytes32","name":"commit","type":"bytes32"}]},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"cancelDefaultAdminTransfer"},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"}],"stateMutability":"nonpayable","type":"function","name":"changeDefaultAdminDelay"},{"inputs":[],"stateMutability":"view","type":"function","name":"defaultAdmin","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"defaultAdminDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"defaultAdminDelayIncreaseWait","outputs":[{"internalType":"uint48","name":"","type":"uint48"}]},{"inputs":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"address","name":"rollupRecipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"payable","type":"function","name":"enter"},{"inputs":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"address","name":"rollupRecipient","type":"address"}],"stateMutability":"payable","type":"function","name":"enter"},{"inputs":[{"internalType":"struct Passage.ExitOrder[]","name":"orders","type":"tuple[]","components":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]}],"stateMutability":"payable","type":"function","name":"fulfillExits"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function","name":"lastSubmittedAtBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function","name":"nextSequence","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"pendingDefaultAdmin","outputs":[{"internalType":"address","name":"newAdmin","type":"address"},{"internalType":"uint48","name":"schedule","type":"uint48"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"pendingDefaultAdminDelay","outputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"},{"internalType":"uint48","name":"schedule","type":"uint48"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[],"stateMutability":"nonpayable","type":"function","name":"rollbackDefaultAdminDelay"},{"inputs":[{"internalType":"struct Zenith.BlockHeader","name":"header","type":"tuple","components":[{"internalType":"uint256","name":"rollupChainId","type":"uint256"},{"internalType":"uint256","name":"sequence","type":"uint256"},{"internalType":"uint256","name":"confirmBy","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"address","name":"rewardAddress","type":"address"}]},{"internalType":"bytes32","name":"blockDataHash","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"bytes","name":"blockData","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"submitBlock"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[],"stateMutability":"payable","type":"receive"}],"devdoc":{"kind":"dev","methods":{"acceptDefaultAdminTransfer()":{"details":"Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. After calling the function: - `DEFAULT_ADMIN_ROLE` should be granted to the caller. - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder. - {pendingDefaultAdmin} should be reset to zero values. Requirements: - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`. - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed."},"beginDefaultAdminTransfer(address)":{"details":"Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance after the current timestamp plus a {defaultAdminDelay}. Requirements: - Only can be called by the current {defaultAdmin}. Emits a DefaultAdminRoleChangeStarted event."},"blockCommitment((uint256,uint256,uint256,uint256,address),bytes32)":{"params":{"header":"- the header information for the rollup block."},"returns":{"commit":"- the hash of the encoded block details."}},"cancelDefaultAdminTransfer()":{"details":"Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}. A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function. Requirements: - Only can be called by the current {defaultAdmin}. May emit a DefaultAdminTransferCanceled event."},"changeDefaultAdminDelay(uint48)":{"details":"Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting into effect after the current timestamp plus a {defaultAdminDelay}. This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay} set before calling. The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin} complete transfer (including acceptance). The schedule is designed for two scenarios: - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by {defaultAdminDelayIncreaseWait}. - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change. Requirements: - Only can be called by the current {defaultAdmin}. Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event."},"constructor":{"details":"See `AccessControlDefaultAdminRules` for information on contract administration. - Admin role can grant and revoke Sequencer roles. - Admin role can be transferred via two-step process with a 1 day timelock.","params":{"admin":"- the address that will be the initial admin."}},"defaultAdmin()":{"details":"Returns the address of the current `DEFAULT_ADMIN_ROLE` holder."},"defaultAdminDelay()":{"details":"Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started. This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set the acceptance schedule. NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function returns the new delay. See {changeDefaultAdminDelay}."},"defaultAdminDelayIncreaseWait()":{"details":"Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay}) to take effect. Default to 5 days. When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overrode for a custom {defaultAdminDelay} increase scheduling. IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise, there's a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds)."},"enter(uint256,address)":{"custom:emits":"Enter indicating the amount of Ether to mint on the rollup & its recipient.","details":"Permanently burns the entire msg.value by locking it in this contract.","params":{"rollupChainId":"- The rollup chain to enter.","rollupRecipient":"- The recipient of the Ether on the rollup."}},"enter(uint256,address,address,uint256)":{"custom:emits":"Enter indicating the amount of tokens to mint on the rollup & its recipient.","details":"Permanently burns the token amount by locking it in this contract.","params":{"amount":"- The amount of the ERC20 token to transfer to the rollup.","rollupChainId":"- The rollup chain to enter.","rollupRecipient":"- The recipient of the Ether on the rollup.","token":"- The address of the ERC20 token on the Host."}},"fulfillExits((uint256,address,address,uint256)[])":{"custom:emits":"ExitFilled for each exit order fulfilled.","details":"Builder SHOULD call `filfillExits` atomically with `submitBlock`. Builder SHOULD set a block expiration time that is AT MOST the minimum of all exit order deadlines; this way, `fulfillExits` + `submitBlock` will revert atomically on mainnet if any exit orders have expired. Otherwise, `filfillExits` may mine on mainnet, while `submitExit` reverts on the rollup, and the Builder can't collect the corresponding value on the rollup.Called by the Builder atomically with a transaction calling `submitBlock`. The user-submitted transactions initiating the ExitOrders on the rollup must be included by the Builder in the rollup block submitted via `submitBlock`.The user transfers tokenIn on the rollup, and receives tokenOut on host.The Builder receives tokenIn on the rollup, and transfers tokenOut to the user on host.The rollup STF MUST NOT apply `submitExit` transactions to the rollup state UNLESS a corresponding ExitFilled event is emitted on host in the same block.If the user submits multiple exit transactions for the same token in the same rollup block, the Builder may transfer the cumulative tokenOut to the user in a single ExitFilled event. The rollup STF will apply the user's exit transactions on the rollup up to the point that sum(tokenOut) is lte the ExitFilled amount. TODO: add option to fulfill ExitOrders with native ETH? or is it sufficient to only allow users to exit via WETH?","params":{"orders":"The exit orders to fulfill"}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"owner()":{"details":"See {IERC5313-owner}."},"pendingDefaultAdmin()":{"details":"Returns a tuple of a `newAdmin` and an accept schedule. After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role by calling {acceptDefaultAdminTransfer}, completing the role transfer. A zero value only in `acceptSchedule` indicates no pending admin transfer. NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced."},"pendingDefaultAdminDelay()":{"details":"Returns a tuple of `newDelay` and an effect schedule. After the `schedule` passes, the `newDelay` will get into effect immediately for every new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}. A zero value only in `effectSchedule` indicates no pending delay change. NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay} will be zero after the effect schedule."},"renounceRole(bytes32,address)":{"details":"See {AccessControl-renounceRole}. For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule has also passed when calling this function. After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions. NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin}, thereby disabling any functionality that is only available for it, and the possibility of reassigning a non-administrated role."},"revokeRole(bytes32,address)":{"details":"See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`."},"rollbackDefaultAdminDelay()":{"details":"Cancels a scheduled {defaultAdminDelay} change. Requirements: - Only can be called by the current {defaultAdmin}. May emit a DefaultAdminDelayChangeCanceled event."},"submitBlock((uint256,uint256,uint256,uint256,address),bytes32,uint8,bytes32,bytes32,bytes)":{"custom:emits":"BlockSubmitted if the block is successfully submitted.BlockData to expose the block calldata; as a convenience until calldata tracing is implemented in the Node.","custom:reverts":"BadSequence if the sequence number is not the next block for the given rollup chainId.BlockExpired if the confirmBy time has passed.BadSignature if the signer is not a permissioned sequencer, OR if the signature provided commits to a different header.OneRollupBlockPerHostBlock if attempting to submit a second rollup block within one host block.","details":"Blocks are submitted by Builders, with an attestation to the block data signed by a Sequencer.including blockDataHash allows the sequencer to sign over finalized block data, without needing to calldatacopy the `blockData` param.","params":{"blockData":"- block data information. could be packed blob hashes, or direct rlp-encoded transctions. blockData is ignored by the contract logic.","blockDataHash":"- keccak256(blockData). the Node will discard the block if the hash doens't match.","header":"- the header information for the rollup block.","r":"- the r component of the Sequencer's ECSDA signature over the block header.","s":"- the s component of the Sequencer's ECSDA signature over the block header.","v":"- the v component of the Sequencer's ECSDA signature over the block header."}},"supportsInterface(bytes4)":{"details":"See {IERC165-supportsInterface}."}},"version":1},"userdoc":{"kind":"user","methods":{"SEQUENCER_ROLE()":{"notice":"Role that allows a key to sign commitments to rollup blocks."},"blockCommitment((uint256,uint256,uint256,uint256,address),bytes32)":{"notice":"Construct hash of block details that the sequencer signs."},"constructor":{"notice":"Initializes the Admin role."},"enter(uint256,address)":{"notice":"Allows native Ether to enter the rollup."},"enter(uint256,address,address,uint256)":{"notice":"Allows ERC20s to enter the rollup."},"fulfillExits((uint256,address,address,uint256)[])":{"notice":"Fulfills exit orders by transferring tokenOut to the recipient"},"lastSubmittedAtBlock(uint256)":{"notice":"The host block number that a block was last submitted at for a given rollup chainId. rollupChainId => host blockNumber that block was last submitted at"},"nextSequence(uint256)":{"notice":"The sequence number of the next block that can be submitted for a given rollup chainId. rollupChainId => nextSequence number"},"submitBlock((uint256,uint256,uint256,uint256,address),bytes32,uint8,bytes32,bytes32,bytes)":{"notice":"Submit a rollup block with block data submitted via calldata."}},"version":1}},"settings":{"remappings":["@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"src/Zenith.sol":"Zenith"},"evmVersion":"cancun","libraries":{}},"sources":{"lib/openzeppelin-contracts/contracts/access/AccessControl.sol":{"keccak256":"0xa0e92d42942f4f57c5be50568dac11e9d00c93efcb458026e18d2d9b9b2e7308","urls":["bzz-raw://46326c0bb1e296b67185e81c918e0b40501b8b6386165855df0a3f3c634b6a80","dweb:/ipfs/QmTwyrDYtsxsk6pymJTK94PnEpzsmkpUxFuzEiakDopy4Z"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol":{"keccak256":"0xc503b1464e90b1cf79d81239f719f81c35ff646b17b638c87fe87a1d7bc5d94d","urls":["bzz-raw://381076837654e98f1d5dfc3909a3ebb80e2c86a97d662b507320701e09cb7a60","dweb:/ipfs/QmWGwdWe9JWx2ae3n8EhWuY6ipWo6shVg9bct6y5og7v9Y"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/access/extensions/AccessControlDefaultAdminRules.sol":{"keccak256":"0xd5e43578dce2678fbd458e1221dc37b20e983ecce4a314b422704f07d6015c5b","urls":["bzz-raw://9ea4d9ae3392dc9db1ef4d7ebef84ce7fa243dc14abb46e68eb2eb60d2cd0e93","dweb:/ipfs/QmRfjyDoLWF74EgmpcGkWZM7Kx1LgHN8dZHBxAnU9vPH46"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sol":{"keccak256":"0xc2dbeddf97707bf012827013b4a072bacbe56ad3219c405e30fd2a959e8a5413","urls":["bzz-raw://281289e424c30c2ea92fc25598315117410404cf76a756663ad39ba18fd38b48","dweb:/ipfs/Qma3wmq2cjxpfkKKM7JrvyJzzohsNWNNWsnaf3jVNBD65v"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/IERC5313.sol":{"keccak256":"0x22412c268e74cc3cbf550aecc2f7456f6ac40783058e219cfe09f26f4d396621","urls":["bzz-raw://0b841021f25480424d2359de4869e60e77f790f52e8e85f07aa389543024b559","dweb:/ipfs/QmV7U5ehV5xe3QrbE8ErxfWSSzK1T1dGeizXvYPjWpNDGq"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0xee2337af2dc162a973b4be6d3f7c16f06298259e0af48c5470d2839bfa8a22f4","urls":["bzz-raw://30c476b4b2f405c1bb3f0bae15b006d129c80f1bfd9d0f2038160a3bb9745009","dweb:/ipfs/Qmb3VcuDufv6xbHeVgksC4tHpc5gKYVqBEwjEXW72XzSvN"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/Panic.sol":{"keccak256":"0x29074fe5a74bb024c57b3570abf6c74d8bceed3438694d470fd0166a3ecd196a","urls":["bzz-raw://f4f8435ccbc56e384f4cc9ac9ff491cf30a82f2beac00e33ccc2cf8af3f77cc3","dweb:/ipfs/QmUKJXxTe6nn1qfgnX8xbnboNNAPUuEmJyGqMZCKNiFBgn"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol":{"keccak256":"0x6fac27fb1885a1d9fd2ce3f8fac4e44a6596ca4d44207c9ef2541ba8c941291e","urls":["bzz-raw://2079378abdb36baec15c23bc2353b73a3d28d1d0610b436b0c1c4e6fa61d65c9","dweb:/ipfs/QmVZkRFMzKW7sLaugKSTbMNnUBKWF3QDsoMi5uoQFyVMjf"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"keccak256":"0xc859863e3bda7ec3cddf6dafe2ffe91bcbe648d1395b856b839c32ee9617c44c","urls":["bzz-raw://a9d5417888b873cf2225ed5d50b2a67be97c1504134a2a580512168d587ad82e","dweb:/ipfs/QmNr5fTb2heFW658NZn7dDnofZgFvQTnNxKRJ3wdnR1skX"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/math/Math.sol":{"keccak256":"0x3233b02fcf2b20a41cce60a62e43c7e5a67a55b738ec1db842a82452e6aa170d","urls":["bzz-raw://231c75d18bc6973533dfa7d58d2b97e504ca4e21d703a5c8b0ec31475e97db67","dweb:/ipfs/QmPJ29HDuFceD1FDr4CnjYYtvaQ234wGAfojZpL3RXFG26"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol":{"keccak256":"0x8cd59334ed58b8884cd1f775afc9400db702e674e5d6a7a438c655b9de788d7e","urls":["bzz-raw://99e62c7de7318f413b6352e3f2704ca23e7725ff144e43c8bd574d12dbf29047","dweb:/ipfs/QmSEXG2rBx1VxU2uFTWdiChjDvA4osEY2mesjmoVeVhHko"],"license":"MIT"},"src/Passage.sol":{"keccak256":"0x81016c92006558f93c028e3d4f61ddad8ff870b956edaa19ad2ccd68ec5d292a","urls":["bzz-raw://dc70a7d97b18e988ce9560f4fabbf9caea3c6178f64fab056b1cf63d27bef6c5","dweb:/ipfs/QmeJDLqvLdhkbWfyLHdYUPoGz7XHWw3zpe8YTCMQE9MacX"],"license":"UNLICENSED"},"src/Zenith.sol":{"keccak256":"0x0febef21c15ebf62421e25337341a8a11a6dd5b5dc2e9ea967a2d4769469ecd6","urls":["bzz-raw://405a8eb90f834ab216e96d40b8c1cfd98c4bc4e71399b09c04ef4123eb3bb1ab","dweb:/ipfs/QmVakr7Upoe2tgU1jQSZUgXE1UASAuHh9kReZ2mfgCsdha"],"license":"UNLICENSED"}},"version":1},"id":35} \ No newline at end of file diff --git a/examples/exex/rollup/src/db.rs b/examples/exex/rollup/src/db.rs deleted file mode 100644 index dcc8b435e..000000000 --- a/examples/exex/rollup/src/db.rs +++ /dev/null @@ -1,460 +0,0 @@ -use std::{ - collections::{hash_map::Entry, HashMap}, - str::FromStr, - sync::{Arc, Mutex, MutexGuard}, -}; - -use reth_primitives::{ - revm_primitives::{AccountInfo, Bytecode}, - Address, Bytes, SealedBlockWithSenders, StorageEntry, B256, U256, -}; -use reth_provider::{bundle_state::StorageRevertsIter, OriginalValuesKnown}; -use reth_revm::db::{ - states::{PlainStorageChangeset, PlainStorageRevert}, - BundleState, -}; -use rusqlite::Connection; - -/// Type used to initialize revms bundle state. -type BundleStateInit = - HashMap, Option, HashMap)>; - -/// Types used inside RevertsInit to initialize revms reverts. -pub type AccountRevertInit = (Option>, Vec); - -/// Type used to initialize revms reverts. -pub type RevertsInit = HashMap; - -pub struct Database { - connection: Arc>, -} - -impl Database { - /// Create new database with the provided connection. - pub fn new(connection: Connection) -> eyre::Result { - let database = Self { connection: Arc::new(Mutex::new(connection)) }; - database.create_tables()?; - Ok(database) - } - - fn connection(&self) -> MutexGuard<'_, Connection> { - self.connection.lock().expect("failed to acquire database lock") - } - - fn create_tables(&self) -> eyre::Result<()> { - self.connection().execute_batch( - "CREATE TABLE IF NOT EXISTS block ( - id INTEGER PRIMARY KEY, - number TEXT UNIQUE, - data TEXT - ); - CREATE TABLE IF NOT EXISTS account ( - id INTEGER PRIMARY KEY, - address TEXT UNIQUE, - data TEXT - ); - CREATE TABLE IF NOT EXISTS account_revert ( - id INTEGER PRIMARY KEY, - block_number TEXT, - address TEXT, - data TEXT, - UNIQUE (block_number, address) - ); - CREATE TABLE IF NOT EXISTS storage ( - id INTEGER PRIMARY KEY, - address TEXT, - key TEXT, - data TEXT, - UNIQUE (address, key) - ); - CREATE TABLE IF NOT EXISTS storage_revert ( - id INTEGER PRIMARY KEY, - block_number TEXT, - address TEXT, - key TEXT, - data TEXT, - UNIQUE (block_number, address, key) - ); - CREATE TABLE IF NOT EXISTS bytecode ( - id INTEGER PRIMARY KEY, - hash TEXT UNIQUE, - data TEXT - );", - )?; - Ok(()) - } - - /// Insert block with bundle into the database. - pub fn insert_block_with_bundle( - &self, - block: &SealedBlockWithSenders, - bundle: BundleState, - ) -> eyre::Result<()> { - let mut connection = self.connection(); - let tx = connection.transaction()?; - - tx.execute( - "INSERT INTO block (number, data) VALUES (?, ?)", - (block.header.number.to_string(), serde_json::to_string(block)?), - )?; - - let (changeset, reverts) = bundle.into_plain_state_and_reverts(OriginalValuesKnown::Yes); - - for (address, account) in changeset.accounts { - if let Some(account) = account { - tx.execute( - "INSERT INTO account (address, data) VALUES (?, ?) ON CONFLICT(address) DO UPDATE SET data = excluded.data", - (address.to_string(), serde_json::to_string(&account)?), - )?; - } else { - tx.execute("DELETE FROM account WHERE address = ?", (address.to_string(),))?; - } - } - - if reverts.accounts.len() > 1 { - eyre::bail!("too many blocks in account reverts"); - } - if let Some(account_reverts) = reverts.accounts.into_iter().next() { - for (address, account) in account_reverts { - tx.execute( - "INSERT INTO account_revert (block_number, address, data) VALUES (?, ?, ?) ON CONFLICT(block_number, address) DO UPDATE SET data = excluded.data", - (block.header.number.to_string(), address.to_string(), serde_json::to_string(&account)?), - )?; - } - } - - for PlainStorageChangeset { address, wipe_storage, storage } in changeset.storage { - if wipe_storage { - tx.execute("DELETE FROM storage WHERE address = ?", (address.to_string(),))?; - } - - for (key, data) in storage { - tx.execute( - "INSERT INTO storage (address, key, data) VALUES (?, ?, ?) ON CONFLICT(address, key) DO UPDATE SET data = excluded.data", - (address.to_string(), B256::from(key).to_string(), data.to_string()), - )?; - } - } - - if reverts.storage.len() > 1 { - eyre::bail!("too many blocks in storage reverts"); - } - if let Some(storage_reverts) = reverts.storage.into_iter().next() { - for PlainStorageRevert { address, wiped, storage_revert } in storage_reverts { - let storage = storage_revert - .into_iter() - .map(|(k, v)| (B256::new(k.to_be_bytes()), v)) - .collect::>(); - let wiped_storage = if wiped { get_storages(&tx, address)? } else { Vec::new() }; - for (key, data) in StorageRevertsIter::new(storage, wiped_storage) { - tx.execute( - "INSERT INTO storage_revert (block_number, address, key, data) VALUES (?, ?, ?, ?) ON CONFLICT(block_number, address, key) DO UPDATE SET data = excluded.data", - (block.header.number.to_string(), address.to_string(), key.to_string(), data.to_string()), - )?; - } - } - } - - for (hash, bytecode) in changeset.contracts { - tx.execute( - "INSERT INTO bytecode (hash, data) VALUES (?, ?) ON CONFLICT(hash) DO NOTHING", - (hash.to_string(), bytecode.bytecode().to_string()), - )?; - } - - tx.commit()?; - - Ok(()) - } - - /// Reverts the tip block from the database, checking it against the provided block number. - /// - /// The code is adapted from - pub fn revert_tip_block(&self, block_number: U256) -> eyre::Result<()> { - let mut connection = self.connection(); - let tx = connection.transaction()?; - - let tip_block_number = tx - .query_row::( - "SELECT number FROM block ORDER BY number DESC LIMIT 1", - [], - |row| row.get(0), - ) - .map(|data| U256::from_str(&data))??; - if block_number != tip_block_number { - eyre::bail!("Reverts can only be done from the tip. Attempted to revert block {} with tip block {}", block_number, tip_block_number); - } - - tx.execute("DELETE FROM block WHERE number = ?", (block_number.to_string(),))?; - - let mut state = BundleStateInit::new(); - let mut reverts = RevertsInit::new(); - - let account_reverts = tx - .prepare("SELECT address, data FROM account_revert WHERE block_number = ?")? - .query((block_number.to_string(),))? - .mapped(|row| { - Ok(( - Address::from_str(row.get_ref(0)?.as_str()?), - serde_json::from_str::>(row.get_ref(1)?.as_str()?), - )) - }) - .map(|result| { - let (address, data) = result?; - Ok((address?, data?)) - }) - .collect::>>()?; - - for (address, old_info) in account_reverts { - // insert old info into reverts - reverts.entry(address).or_default().0 = Some(old_info.clone()); - - match state.entry(address) { - Entry::Vacant(entry) => { - let new_info = get_account(&tx, address)?; - entry.insert((old_info, new_info, HashMap::new())); - } - Entry::Occupied(mut entry) => { - // overwrite old account state - entry.get_mut().0 = old_info; - } - } - } - - let storage_reverts = tx - .prepare("SELECT address, key, data FROM storage_revert WHERE block_number = ?")? - .query((block_number.to_string(),))? - .mapped(|row| { - Ok(( - Address::from_str(row.get_ref(0)?.as_str()?), - B256::from_str(row.get_ref(1)?.as_str()?), - U256::from_str(row.get_ref(2)?.as_str()?), - )) - }) - .map(|result| { - let (address, key, data) = result?; - Ok((address?, key?, data?)) - }) - .collect::>>()?; - - for (address, key, old_data) in storage_reverts.into_iter().rev() { - let old_storage = StorageEntry { key, value: old_data }; - - // insert old info into reverts - reverts.entry(address).or_default().1.push(old_storage); - - // get account state or insert from plain state - let account_state = match state.entry(address) { - Entry::Vacant(entry) => { - let present_info = get_account(&tx, address)?; - entry.insert((present_info.clone(), present_info, HashMap::new())) - } - Entry::Occupied(entry) => entry.into_mut(), - }; - - // match storage - match account_state.2.entry(old_storage.key) { - Entry::Vacant(entry) => { - let new_value = get_storage(&tx, address, old_storage.key)?.unwrap_or_default(); - entry.insert((old_storage.value, new_value)); - } - Entry::Occupied(mut entry) => { - entry.get_mut().0 = old_storage.value; - } - }; - } - - // iterate over local plain state remove all account and all storages - for (address, (old_account, new_account, storage)) in state { - // revert account if needed - if old_account != new_account { - if let Some(account) = old_account { - upsert_account(&tx, address, |_| Ok(account))?; - } else { - delete_account(&tx, address)?; - } - } - - // revert storages - for (storage_key, (old_storage_value, _new_storage_value)) in storage { - // delete previous value - delete_storage(&tx, address, storage_key)?; - - // insert value if needed - if !old_storage_value.is_zero() { - upsert_storage(&tx, address, storage_key, old_storage_value)?; - } - } - } - - tx.commit()?; - - Ok(()) - } - - /// Get block by number. - pub fn get_block(&self, number: U256) -> eyre::Result> { - let block = self.connection().query_row::( - "SELECT data FROM block WHERE number = ?", - (number.to_string(),), - |row| row.get(0), - ); - match block { - Ok(data) => Ok(Some(serde_json::from_str(&data)?)), - Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(e.into()), - } - } - - /// Insert new account if it does not exist, update otherwise. The provided closure is called - /// with the current account, if it exists. - pub fn upsert_account( - &self, - address: Address, - f: impl FnOnce(Option) -> eyre::Result, - ) -> eyre::Result<()> { - upsert_account(&self.connection(), address, f) - } - - /// Get account by address. - pub fn get_account(&self, address: Address) -> eyre::Result> { - get_account(&self.connection(), address) - } -} - -/// Insert new account if it does not exist, update otherwise. The provided closure is called -/// with the current account, if it exists. Connection can be either -/// [rusqlite::Transaction] or [rusqlite::Connection]. -fn upsert_account( - connection: &Connection, - address: Address, - f: impl FnOnce(Option) -> eyre::Result, -) -> eyre::Result<()> { - let account = get_account(connection, address)?; - let account = f(account)?; - connection.execute( - "INSERT INTO account (address, data) VALUES (?, ?) ON CONFLICT(address) DO UPDATE SET data = excluded.data", - (address.to_string(), serde_json::to_string(&account)?), - )?; - - Ok(()) -} - -/// Delete account by address. Connection can be either [rusqlite::Transaction] or -/// [rusqlite::Connection]. -fn delete_account(connection: &Connection, address: Address) -> eyre::Result<()> { - connection.execute("DELETE FROM account WHERE address = ?", (address.to_string(),))?; - Ok(()) -} - -/// Get account by address using the database connection. Connection can be either -/// [rusqlite::Transaction] or [rusqlite::Connection]. -fn get_account(connection: &Connection, address: Address) -> eyre::Result> { - match connection.query_row::( - "SELECT data FROM account WHERE address = ?", - (address.to_string(),), - |row| row.get(0), - ) { - Ok(account_info) => Ok(Some(serde_json::from_str(&account_info)?)), - Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(e.into()), - } -} - -/// Insert new storage if it does not exist, update otherwise. Connection can be either -/// [rusqlite::Transaction] or [rusqlite::Connection]. -fn upsert_storage( - connection: &Connection, - address: Address, - key: B256, - data: U256, -) -> eyre::Result<()> { - connection.execute( - "INSERT INTO storage (address, key, data) VALUES (?, ?, ?) ON CONFLICT(address, key) DO UPDATE SET data = excluded.data", - (address.to_string(), key.to_string(), data.to_string()), - )?; - Ok(()) -} - -/// Delete storage by address and key. Connection can be either [rusqlite::Transaction] or -/// [rusqlite::Connection]. -fn delete_storage(connection: &Connection, address: Address, key: B256) -> eyre::Result<()> { - connection.execute( - "DELETE FROM storage WHERE address = ? AND key = ?", - (address.to_string(), key.to_string()), - )?; - Ok(()) -} - -/// Get all storages for the provided address using the database connection. Connection can be -/// either [rusqlite::Transaction] or [rusqlite::Connection]. -fn get_storages(connection: &Connection, address: Address) -> eyre::Result> { - connection - .prepare("SELECT key, data FROM storage WHERE address = ?")? - .query((address.to_string(),))? - .mapped(|row| { - Ok(( - B256::from_str(row.get_ref(0)?.as_str()?), - U256::from_str(row.get_ref(1)?.as_str()?), - )) - }) - .map(|result| { - let (key, data) = result?; - Ok((key?, data?)) - }) - .collect() -} - -/// Get storage for the provided address by key using the database connection. Connection can be -/// either [rusqlite::Transaction] or [rusqlite::Connection]. -fn get_storage(connection: &Connection, address: Address, key: B256) -> eyre::Result> { - match connection.query_row::( - "SELECT data FROM storage WHERE address = ? AND key = ?", - (address.to_string(), key.to_string()), - |row| row.get(0), - ) { - Ok(data) => Ok(Some(U256::from_str(&data)?)), - Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(e.into()), - } -} - -impl reth_revm::Database for Database { - type Error = eyre::Report; - - fn basic(&mut self, address: Address) -> Result, Self::Error> { - self.get_account(address) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - let bytecode = self.connection().query_row::( - "SELECT data FROM bytecode WHERE hash = ?", - (code_hash.to_string(),), - |row| row.get(0), - ); - match bytecode { - Ok(data) => Ok(Bytecode::new_raw(Bytes::from_str(&data).unwrap())), - Err(rusqlite::Error::QueryReturnedNoRows) => Ok(Bytecode::default()), - Err(err) => Err(err.into()), - } - } - - fn storage(&mut self, address: Address, index: U256) -> Result { - get_storage(&self.connection(), address, index.into()).map(|data| data.unwrap_or_default()) - } - - fn block_hash(&mut self, number: u64) -> Result { - let block_hash = self.connection().query_row::( - "SELECT hash FROM block WHERE number = ?", - (number.to_string(),), - |row| row.get(0), - ); - match block_hash { - Ok(data) => Ok(B256::from_str(&data).unwrap()), - // No special handling for `QueryReturnedNoRows` is needed, because revm does block - // number bound checks on its own. - // See https://github.com/bluealloy/revm/blob/1ca3d39f6a9e9778f8eb0fcb74fe529345a531b4/crates/interpreter/src/instructions/host.rs#L106-L123. - Err(err) => Err(err.into()), - } - } -} diff --git a/examples/exex/rollup/src/execution.rs b/examples/exex/rollup/src/execution.rs deleted file mode 100644 index 22ec58292..000000000 --- a/examples/exex/rollup/src/execution.rs +++ /dev/null @@ -1,481 +0,0 @@ -use crate::{db::Database, RollupContract, CHAIN_ID, CHAIN_SPEC}; -use alloy_consensus::{Blob, SidecarCoder, SimpleCoder}; -use alloy_rlp::Decodable as _; -use eyre::OptionExt; -use reth::transaction_pool::TransactionPool; -use reth_execution_errors::BlockValidationError; -use reth_node_api::{ConfigureEvm, ConfigureEvmEnv}; -use reth_node_ethereum::EthEvmConfig; -use reth_primitives::{ - constants, - eip4844::kzg_to_versioned_hash, - keccak256, - revm_primitives::{CfgEnvWithHandlerCfg, EVMError, ExecutionResult, ResultAndState}, - Address, Block, BlockWithSenders, Bytes, EthereumHardfork, Header, Receipt, TransactionSigned, - TxType, B256, U256, -}; -use reth_revm::{ - db::{states::bundle_state::BundleRetention, BundleState}, - DBBox, DatabaseCommit, Evm, StateBuilder, StateDBBox, -}; -use reth_tracing::tracing::debug; - -/// Execute a rollup block and return (block with recovered senders)[BlockWithSenders], (bundle -/// state)[BundleState] and list of (receipts)[Receipt]. -pub async fn execute_block( - db: &mut Database, - pool: &Pool, - tx: &TransactionSigned, - header: &RollupContract::BlockHeader, - block_data: Bytes, - block_data_hash: B256, -) -> eyre::Result<(BlockWithSenders, BundleState, Vec, Vec)> { - if header.rollupChainId != U256::from(CHAIN_ID) { - eyre::bail!("Invalid rollup chain ID") - } - - // Construct header - let header = construct_header(db, header)?; - - // Decode transactions - let transactions = decode_transactions(pool, tx, block_data, block_data_hash).await?; - - // Configure EVM - let evm_config = EthEvmConfig::default(); - let mut evm = configure_evm(&evm_config, db, &header); - - // Execute transactions - let (executed_txs, receipts, results) = execute_transactions(&mut evm, &header, transactions)?; - - // Construct block and recover senders - let block = Block { header, body: executed_txs, ..Default::default() } - .with_recovered_senders() - .ok_or_eyre("failed to recover senders")?; - - let bundle = evm.db_mut().take_bundle(); - - Ok((block, bundle, receipts, results)) -} - -/// Construct header from the given rollup header. -fn construct_header(db: &Database, header: &RollupContract::BlockHeader) -> eyre::Result
{ - let parent_block = if !header.sequence.is_zero() { - db.get_block(header.sequence - U256::from(1))? - } else { - None - }; - - let block_number = u64::try_from(header.sequence)?; - - // Calculate base fee per gas for EIP-1559 transactions - let base_fee_per_gas = - if CHAIN_SPEC.fork(EthereumHardfork::London).transitions_at_block(block_number) { - constants::EIP1559_INITIAL_BASE_FEE - } else { - parent_block - .as_ref() - .ok_or(eyre::eyre!("parent block not found"))? - .header - .next_block_base_fee(CHAIN_SPEC.base_fee_params_at_block(block_number)) - .ok_or(eyre::eyre!("failed to calculate base fee"))? - }; - - // Construct header - Ok(Header { - parent_hash: parent_block.map(|block| block.header.hash()).unwrap_or_default(), - number: block_number, - gas_limit: u64::try_from(header.gasLimit)?, - timestamp: u64::try_from(header.confirmBy)?, - base_fee_per_gas: Some(base_fee_per_gas), - ..Default::default() - }) -} - -/// Configure EVM with the given database and header. -fn configure_evm<'a>( - config: &'a EthEvmConfig, - db: &'a mut Database, - header: &Header, -) -> Evm<'a, (), StateDBBox<'a, eyre::Report>> { - let mut evm = config.evm( - StateBuilder::new_with_database(Box::new(db) as DBBox<'_, eyre::Report>) - .with_bundle_update() - .build(), - ); - evm.db_mut().set_state_clear_flag( - CHAIN_SPEC.fork(EthereumHardfork::SpuriousDragon).active_at_block(header.number), - ); - - let mut cfg = CfgEnvWithHandlerCfg::new_with_spec_id(evm.cfg().clone(), evm.spec_id()); - config.fill_cfg_and_block_env(&mut cfg, evm.block_mut(), &CHAIN_SPEC, header, U256::ZERO); - *evm.cfg_mut() = cfg.cfg_env; - - evm -} - -/// Decode transactions from the block data and recover senders. -/// - If the transaction is a blob-carrying one, decode the blobs either using the local transaction -/// pool, or querying Blobscan. -/// - If the transaction is a regular one, decode the block data directly. -async fn decode_transactions( - pool: &Pool, - tx: &TransactionSigned, - block_data: Bytes, - block_data_hash: B256, -) -> eyre::Result> { - // Get raw transactions either from the blobs, or directly from the block data - let raw_transactions = if matches!(tx.tx_type(), TxType::Eip4844) { - let blobs: Vec<_> = if let Some(sidecar) = pool.get_blob(tx.hash)? { - // Try to get blobs from the transaction pool - sidecar.blobs.into_iter().zip(sidecar.commitments).collect() - } else { - // If transaction is not found in the pool, try to get blobs from Blobscan - let blobscan_client = foundry_blob_explorers::Client::holesky(); - let sidecar = blobscan_client.transaction(tx.hash).await?.blob_sidecar(); - sidecar - .blobs - .into_iter() - .map(|blob| (*blob).into()) - .zip(sidecar.commitments.into_iter().map(|commitment| (*commitment).into())) - .collect() - }; - - // Decode blob hashes from block data - let blob_hashes = Vec::::decode(&mut block_data.as_ref())?; - - // Filter blobs that are present in the block data - let blobs = blobs - .into_iter() - // Convert blob KZG commitments to versioned hashes - .map(|(blob, commitment)| (blob, kzg_to_versioned_hash(commitment.as_slice()))) - // Filter only blobs that are present in the block data - .filter(|(_, hash)| blob_hashes.contains(hash)) - .map(|(blob, _)| Blob::from(*blob)) - .collect::>(); - if blobs.len() != blob_hashes.len() { - eyre::bail!("some blobs not found") - } - - // Decode blobs and concatenate them to get the raw transactions - let data = SimpleCoder::default() - .decode_all(&blobs) - .ok_or(eyre::eyre!("failed to decode blobs"))? - .concat(); - - data.into() - } else { - block_data - }; - - let raw_transaction_hash = keccak256(&raw_transactions); - if raw_transaction_hash != block_data_hash { - eyre::bail!("block data hash mismatch") - } - - // Decode block data, filter only transactions with the correct chain ID and recover senders - let transactions = Vec::::decode(&mut raw_transactions.as_ref())? - .into_iter() - .filter(|tx| tx.chain_id() == Some(CHAIN_ID)) - .map(|tx| { - let sender = tx.recover_signer().ok_or(eyre::eyre!("failed to recover signer"))?; - Ok((tx, sender)) - }) - .collect::>()?; - - Ok(transactions) -} - -/// Execute transactions and return the list of executed transactions, receipts and -/// execution results. -fn execute_transactions( - evm: &mut Evm<'_, (), StateDBBox<'_, eyre::Report>>, - header: &Header, - transactions: Vec<(TransactionSigned, Address)>, -) -> eyre::Result<(Vec, Vec, Vec)> { - let mut receipts = Vec::with_capacity(transactions.len()); - let mut executed_txs = Vec::with_capacity(transactions.len()); - let mut results = Vec::with_capacity(transactions.len()); - if !transactions.is_empty() { - let mut cumulative_gas_used = 0; - for (transaction, sender) in transactions { - // The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior, - // must be no greater than the block’s gasLimit. - let block_available_gas = header.gas_limit - cumulative_gas_used; - if transaction.gas_limit() > block_available_gas { - return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { - transaction_gas_limit: transaction.gas_limit(), - block_available_gas, - } - .into()) - } - // Execute transaction. - // Fill revm structure. - EthEvmConfig::default().fill_tx_env(evm.tx_mut(), &transaction, sender); - - let ResultAndState { result, state } = match evm.transact() { - Ok(result) => result, - Err(err) => { - match err { - EVMError::Transaction(err) => { - // if the transaction is invalid, we can skip it - debug!(%err, ?transaction, "Skipping invalid transaction"); - continue - } - err => { - // this is an error that we should treat as fatal for this attempt - eyre::bail!(err) - } - } - } - }; - - debug!(?transaction, ?result, ?state, "Executed transaction"); - - evm.db_mut().commit(state); - - // append gas used - cumulative_gas_used += result.gas_used(); - - // Push transaction changeset and calculate header bloom filter for receipt. - #[allow(clippy::needless_update)] // side-effect of optimism fields - receipts.push(Receipt { - tx_type: transaction.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.logs().iter().cloned().map(Into::into).collect(), - ..Default::default() - }); - - // append transaction to the list of executed transactions - executed_txs.push(transaction); - results.push(result); - } - - evm.db_mut().merge_transitions(BundleRetention::Reverts); - } - - Ok((executed_txs, receipts, results)) -} - -#[cfg(test)] -mod tests { - use std::time::{SystemTime, UNIX_EPOCH}; - - use alloy_consensus::{SidecarBuilder, SimpleCoder}; - use alloy_sol_types::{sol, SolCall}; - use reth::transaction_pool::{ - test_utils::{testing_pool, MockTransaction}, - TransactionOrigin, TransactionPool, - }; - use reth_primitives::{ - bytes, - constants::ETH_TO_WEI, - keccak256, public_key_to_address, - revm_primitives::{AccountInfo, ExecutionResult, Output, TxEnv}, - BlockNumber, Receipt, SealedBlockWithSenders, Transaction, TxEip2930, TxKind, U256, - }; - use reth_revm::Evm; - use reth_testing_utils::generators::{self, sign_tx_with_key_pair}; - use rusqlite::Connection; - use secp256k1::{Keypair, Secp256k1}; - - use crate::{ - db::Database, execute_block, RollupContract::BlockHeader, CHAIN_ID, - ROLLUP_SUBMITTER_ADDRESS, - }; - - sol!( - WETH, - r#" -[ - { - "constant":true, - "inputs":[ - { - "name":"", - "type":"address" - } - ], - "name":"balanceOf", - "outputs":[ - { - "name":"", - "type":"uint256" - } - ], - "payable":false, - "stateMutability":"view", - "type":"function" - } -] - "# - ); - - #[tokio::test] - async fn test_execute_block() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - - let mut database = Database::new(Connection::open_in_memory()?)?; - - // Create key pair - let secp = Secp256k1::new(); - let key_pair = Keypair::new(&secp, &mut generators::rng()); - let sender_address = public_key_to_address(key_pair.public_key()); - - // Deposit some ETH to the sender and insert it into database - database.upsert_account(sender_address, |_| { - Ok(AccountInfo { balance: U256::from(ETH_TO_WEI), nonce: 1, ..Default::default() }) - })?; - - // WETH deployment transaction sent using calldata - let (_, _, results) = execute_transaction( - &mut database, - key_pair, - 0, - Transaction::Eip2930(TxEip2930 { - chain_id: CHAIN_ID, - nonce: 1, - gas_limit: 1_500_000, - gas_price: 1_500_000_000, - to: TxKind::Create, - // WETH9 bytecode - input: bytes!("60606040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100c8565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100c8565b506012600260006101000a81548160ff021916908360ff16021790555034156100c357600080fd5b61016d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010957805160ff1916838001178555610137565b82800160010185558215610137579182015b8281111561013657825182559160200191906001019061011b565b5b5090506101449190610148565b5090565b61016a91905b8082111561016657600081600090555060010161014e565b5090565b90565b610c348061017c6000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029"), - ..Default::default() - }), - BlockDataSource::Calldata - ).await?; - - let weth_address = match results.first() { - Some(ExecutionResult::Success { output: Output::Create(_, Some(address)), .. }) => { - *address - } - _ => eyre::bail!("WETH contract address not found"), - }; - - // WETH deposit transaction sent using blobs - execute_transaction( - &mut database, - key_pair, - 1, - Transaction::Eip2930(TxEip2930 { - chain_id: CHAIN_ID, - nonce: 2, - gas_limit: 50000, - gas_price: 1_500_000_000, - to: TxKind::Call(weth_address), - value: U256::from(0.5 * ETH_TO_WEI as f64), - input: bytes!("d0e30db0"), - ..Default::default() - }), - BlockDataSource::Blobs, - ) - .await?; - - // Verify WETH balance - let mut evm = Evm::builder() - .with_db(&mut database) - .with_tx_env(TxEnv { - caller: sender_address, - gas_limit: 50_000_000, - transact_to: TxKind::Call(weth_address), - data: WETH::balanceOfCall::new((sender_address,)).abi_encode().into(), - ..Default::default() - }) - .build(); - let result = evm.transact().map_err(|err| eyre::eyre!(err))?.result; - assert_eq!( - result.output(), - Some(&U256::from(0.5 * ETH_TO_WEI as f64).to_be_bytes_vec().into()) - ); - drop(evm); - - // Verify nonce - let account = database.get_account(sender_address)?.unwrap(); - assert_eq!(account.nonce, 3); - - // Revert block with WETH deposit transaction - database.revert_tip_block(U256::from(1))?; - - // Verify WETH balance after revert - let mut evm = Evm::builder() - .with_db(&mut database) - .with_tx_env(TxEnv { - caller: sender_address, - gas_limit: 50_000_000, - transact_to: TxKind::Call(weth_address), - data: WETH::balanceOfCall::new((sender_address,)).abi_encode().into(), - ..Default::default() - }) - .build(); - let result = evm.transact().map_err(|err| eyre::eyre!(err))?.result; - assert_eq!(result.output(), Some(&U256::ZERO.to_be_bytes_vec().into())); - drop(evm); - - // Verify nonce after revert - let account = database.get_account(sender_address)?.unwrap(); - assert_eq!(account.nonce, 2); - - Ok(()) - } - - enum BlockDataSource { - Calldata, - Blobs, - } - - async fn execute_transaction( - database: &mut Database, - key_pair: Keypair, - sequence: BlockNumber, - tx: Transaction, - block_data_source: BlockDataSource, - ) -> eyre::Result<(SealedBlockWithSenders, Vec, Vec)> { - // Construct block header - let block_header = BlockHeader { - rollupChainId: U256::from(CHAIN_ID), - sequence: U256::from(sequence), - confirmBy: U256::from(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()), - gasLimit: U256::from(30_000_000), - rewardAddress: ROLLUP_SUBMITTER_ADDRESS, - }; - let encoded_transactions = - alloy_rlp::encode(vec![sign_tx_with_key_pair(key_pair, tx).envelope_encoded()]); - let block_data_hash = keccak256(&encoded_transactions); - - let pool = testing_pool(); - - let (block_data, l1_transaction) = match block_data_source { - BlockDataSource::Calldata => ( - encoded_transactions, - sign_tx_with_key_pair(key_pair, Transaction::Eip2930(TxEip2930::default())), - ), - BlockDataSource::Blobs => { - let sidecar = - SidecarBuilder::::from_slice(&encoded_transactions).build()?; - let blob_hashes = alloy_rlp::encode(sidecar.versioned_hashes().collect::>()); - - let mut mock_transaction = MockTransaction::eip4844_with_sidecar(sidecar); - let transaction = - sign_tx_with_key_pair(key_pair, Transaction::from(mock_transaction.clone())); - mock_transaction.set_hash(transaction.hash); - pool.add_transaction(TransactionOrigin::Local, mock_transaction).await?; - (blob_hashes, transaction) - } - }; - - // Execute block and insert into database - let (block, bundle, receipts, results) = execute_block( - database, - &pool, - &l1_transaction, - &block_header, - block_data.into(), - block_data_hash, - ) - .await?; - let block = block.seal_slow(); - database.insert_block_with_bundle(&block, bundle)?; - - Ok((block, receipts, results)) - } -} diff --git a/examples/exex/rollup/src/main.rs b/examples/exex/rollup/src/main.rs deleted file mode 100644 index 0bac98cd7..000000000 --- a/examples/exex/rollup/src/main.rs +++ /dev/null @@ -1,276 +0,0 @@ -//! Example of a simple rollup that derives its state from the L1 chain by executing transactions, -//! processing deposits and storing all related data in an SQLite database. -//! -//! The rollup contract accepts blocks of transactions and deposits of ETH and is deployed on -//! Holesky at [ROLLUP_CONTRACT_ADDRESS], see . - -use alloy_genesis::Genesis; -use alloy_sol_types::{sol, SolEventInterface, SolInterface}; -use db::Database; -use execution::execute_block; -use once_cell::sync::Lazy; -use reth_chainspec::{ChainSpec, ChainSpecBuilder}; -use reth_execution_types::Chain; -use reth_exex::{ExExContext, ExExEvent}; -use reth_node_api::FullNodeComponents; -use reth_node_ethereum::EthereumNode; -use reth_primitives::{address, Address, SealedBlockWithSenders, TransactionSigned, U256}; -use reth_tracing::tracing::{error, info}; -use rusqlite::Connection; -use std::sync::Arc; - -mod db; -mod execution; - -sol!(RollupContract, "rollup_abi.json"); -use RollupContract::{RollupContractCalls, RollupContractEvents}; - -const DATABASE_PATH: &str = "rollup.db"; -const ROLLUP_CONTRACT_ADDRESS: Address = address!("97C0E40c6B5bb5d4fa3e2AA1C6b8bC7EA5ECAe31"); -const ROLLUP_SUBMITTER_ADDRESS: Address = address!("5b0517Dc94c413a5871536872605522E54C85a03"); -const CHAIN_ID: u64 = 17001; -static CHAIN_SPEC: Lazy> = Lazy::new(|| { - Arc::new( - ChainSpecBuilder::default() - .chain(CHAIN_ID.into()) - .genesis(Genesis::clique_genesis(CHAIN_ID, ROLLUP_SUBMITTER_ADDRESS)) - .shanghai_activated() - .build(), - ) -}); - -struct Rollup { - ctx: ExExContext, - db: Database, -} - -impl Rollup { - fn new(ctx: ExExContext, connection: Connection) -> eyre::Result { - let db = Database::new(connection)?; - Ok(Self { ctx, db }) - } - - 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?; - 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. - async fn commit(&mut self, chain: &Chain) -> eyre::Result<()> { - let events = decode_chain_into_rollup_events(chain); - - for (_, tx, event) in events { - match event { - // A new block is submitted to the rollup contract. - // The block is executed on top of existing rollup state and committed into the - // database. - RollupContractEvents::BlockSubmitted(RollupContract::BlockSubmitted { - blockDataHash, - .. - }) => { - let call = RollupContractCalls::abi_decode(tx.input(), true)?; - - if let RollupContractCalls::submitBlock(RollupContract::submitBlockCall { - header, - blockData, - .. - }) = call - { - match execute_block( - &mut self.db, - self.ctx.pool(), - tx, - &header, - blockData, - blockDataHash, - ) - .await - { - Ok((block, bundle, _, _)) => { - let block = block.seal_slow(); - self.db.insert_block_with_bundle(&block, bundle)?; - info!( - tx_hash = %tx.recalculate_hash(), - chain_id = %header.rollupChainId, - sequence = %header.sequence, - transactions = block.body.len(), - "Block submitted, executed and inserted into database" - ); - } - Err(err) => { - error!( - %err, - tx_hash = %tx.recalculate_hash(), - chain_id = %header.rollupChainId, - sequence = %header.sequence, - "Failed to execute block" - ); - } - } - } - } - // A deposit of ETH to the rollup contract. The deposit is added to the recipient's - // balance and committed into the database. - RollupContractEvents::Enter(RollupContract::Enter { - rollupChainId, - token, - rollupRecipient, - amount, - }) => { - if rollupChainId != U256::from(CHAIN_ID) { - error!(tx_hash = %tx.recalculate_hash(), "Invalid rollup chain ID"); - continue - } - if token != Address::ZERO { - error!(tx_hash = %tx.recalculate_hash(), "Only ETH deposits are supported"); - continue - } - - self.db.upsert_account(rollupRecipient, |account| { - let mut account = account.unwrap_or_default(); - account.balance += amount; - Ok(account) - })?; - - info!( - tx_hash = %tx.recalculate_hash(), - %amount, - recipient = %rollupRecipient, - "Deposit", - ); - } - _ => (), - } - } - - Ok(()) - } - - /// Process a chain revert. - /// - /// This function decodes all transactions to the rollup contract into events, reverts the - /// corresponding actions and updates the database. - fn revert(&mut self, chain: &Chain) -> eyre::Result<()> { - let mut events = decode_chain_into_rollup_events(chain); - // Reverse the order of events to start reverting from the tip - events.reverse(); - - for (_, tx, event) in events { - match event { - // The block is reverted from the database. - RollupContractEvents::BlockSubmitted(_) => { - let call = RollupContractCalls::abi_decode(tx.input(), true)?; - - if let RollupContractCalls::submitBlock(RollupContract::submitBlockCall { - header, - .. - }) = call - { - self.db.revert_tip_block(header.sequence)?; - info!( - tx_hash = %tx.recalculate_hash(), - chain_id = %header.rollupChainId, - sequence = %header.sequence, - "Block reverted" - ); - } - } - // The deposit is subtracted from the recipient's balance. - RollupContractEvents::Enter(RollupContract::Enter { - rollupChainId, - token, - rollupRecipient, - amount, - }) => { - if rollupChainId != U256::from(CHAIN_ID) { - error!(tx_hash = %tx.recalculate_hash(), "Invalid rollup chain ID"); - continue - } - if token != Address::ZERO { - error!(tx_hash = %tx.recalculate_hash(), "Only ETH deposits are supported"); - continue - } - - self.db.upsert_account(rollupRecipient, |account| { - let mut account = account.ok_or(eyre::eyre!("account not found"))?; - account.balance -= amount; - Ok(account) - })?; - - info!( - tx_hash = %tx.recalculate_hash(), - %amount, - recipient = %rollupRecipient, - "Deposit reverted", - ); - } - _ => (), - } - } - - Ok(()) - } -} - -/// Decode chain of blocks into a flattened list of receipt logs, filter only transactions to the -/// Rollup contract [ROLLUP_CONTRACT_ADDRESS] and extract [RollupContractEvents]. -fn decode_chain_into_rollup_events( - chain: &Chain, -) -> Vec<(&SealedBlockWithSenders, &TransactionSigned, RollupContractEvents)> { - chain - // Get all blocks and receipts - .blocks_and_receipts() - // Get all receipts - .flat_map(|(block, receipts)| { - block - .body - .iter() - .zip(receipts.iter().flatten()) - .map(move |(tx, receipt)| (block, tx, receipt)) - }) - // Get all logs from rollup contract - .flat_map(|(block, tx, receipt)| { - receipt - .logs - .iter() - .filter(|log| log.address == ROLLUP_CONTRACT_ADDRESS) - .map(move |log| (block, tx, log)) - }) - // Decode and filter rollup events - .filter_map(|(block, tx, log)| { - RollupContractEvents::decode_raw_log(log.topics(), &log.data.data, true) - .ok() - .map(|event| (block, tx, event)) - }) - .collect() -} - -fn main() -> eyre::Result<()> { - reth::cli::Cli::parse_args().run(|builder, _| async move { - let handle = builder - .node(EthereumNode::default()) - .install_exex("Rollup", move |ctx| async { - let connection = Connection::open(DATABASE_PATH)?; - - Ok(Rollup::new(ctx, connection)?.start()) - }) - .launch() - .await?; - - handle.wait_for_node_exit().await - }) -}