Skip to content

feat: chain orchestrator #185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[codespell]
skip = .git,target,Cargo.toml,Cargo.lock
skip = .git,target,Cargo.toml,Cargo.lock,docker-compose
ignore-words-list = crate
4 changes: 2 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ jobs:
- type: wasm
target: wasm32-unknown-unknown
exclude: |
scroll-engine,scroll-wire,rollup-node,scroll-network,rollup-node-manager,rollup-node-watcher,scroll-db,scroll-migration,rollup-node-indexer,scroll-codec,scroll-derivation-pipeline,rollup-node-providers,rollup-node-sequencer,rollup-node-signer
scroll-engine,scroll-wire,rollup-node,scroll-network,rollup-node-manager,rollup-node-watcher,scroll-db,scroll-migration,rollup-node-chain-orchestrator,scroll-codec,scroll-derivation-pipeline,rollup-node-providers,rollup-node-sequencer,rollup-node-signer
- type: riscv
target: riscv32imac-unknown-none-elf
exclude: |
scroll-engine,scroll-wire,rollup-node,scroll-network,rollup-node-manager,rollup-node-watcher,scroll-db,scroll-migration,rollup-node-indexer,scroll-codec,scroll-derivation-pipeline,rollup-node-providers,rollup-node-sequencer,rollup-node-signer
scroll-engine,scroll-wire,rollup-node,scroll-network,rollup-node-manager,rollup-node-watcher,scroll-db,scroll-migration,rollup-node-chain-orchestrator,scroll-codec,scroll-derivation-pipeline,rollup-node-providers,rollup-node-sequencer,rollup-node-signer
steps:
- uses: actions/checkout@v4
- uses: rui314/setup-mold@v1
Expand Down
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ members = [
"crates/database/migration",
"crates/derivation-pipeline",
"crates/engine",
"crates/indexer",
"crates/chain-orchestrator",
"crates/l1",
"crates/manager",
"crates/network",
Expand Down Expand Up @@ -167,7 +167,7 @@ reth-scroll-primitives = { git = "https://github.com/scroll-tech/reth.git", defa

# rollup node
rollup-node = { path = "crates/node" }
rollup-node-indexer = { path = "crates/indexer" }
rollup-node-chain-orchestrator = { path = "crates/chain-orchestrator" }
rollup-node-manager = { path = "crates/manager" }
rollup-node-primitives = { path = "crates/primitives" }
rollup-node-providers = { path = "crates/providers" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "rollup-node-indexer"
name = "rollup-node-chain-orchestrator"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
Expand All @@ -11,30 +11,45 @@ workspace = true

[dependencies]
# alloy
alloy-consensus = { workspace = true }
alloy-eips = { workspace = true }
alloy-json-rpc.workspace = true
alloy-primitives.workspace = true
alloy-provider.workspace = true
alloy-transport.workspace = true

# rollup-node
scroll-db.workspace = true
rollup-node-primitives.workspace = true
rollup-node-watcher.workspace = true

# scroll
reth-scroll-primitives.workspace = true
scroll-alloy-consensus.workspace = true
scroll-alloy-hardforks.workspace = true
scroll-alloy-network.workspace = true
scroll-network.workspace = true

# reth
reth-chainspec.workspace = true
reth-network-p2p = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
reth-network-peers.workspace = true
reth-primitives-traits.workspace = true

# misc
futures.workspace = true
metrics.workspace = true
metrics-derive.workspace = true
strum = "0.27.1"
thiserror.workspace = true
tracing.workspace = true
tokio.workspace = true

[dev-dependencies]
alloy-consensus = { workspace = true, features = ["arbitrary"] }
alloy-primitives = { workspace = true, features = ["arbitrary"] }
alloy-rpc-client.workspace = true
alloy-transport.workspace = true

# rollup-node
scroll-db = { workspace = true, features = ["test-utils"] }
Expand All @@ -44,8 +59,15 @@ rollup-node-primitives = { workspace = true, features = ["arbitrary"] }
reth-scroll-chainspec.workspace = true
reth-scroll-forks.workspace = true

# reth
reth-eth-wire-types.workspace = true
reth-network-peers.workspace = true

# misc
arbitrary.workspace = true
futures.workspace = true
parking_lot.workspace = true
rand.workspace = true
reqwest.workspace = true
serde_json = { version = "1.0" }
tokio.workspace = true
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
use super::{IndexerError, IndexerEvent};
use super::{ChainOrchestratorError, ChainOrchestratorEvent};
use std::{
fmt,
future::Future,
pin::Pin,
task::{Context, Poll},
};

/// A future that resolves to a `Result<IndexerEvent, IndexerError>`.
/// A future that resolves to a `Result<ChainOrchestratorEvent, ChainOrchestratorError>`.
pub(super) type PendingIndexerFuture =
Pin<Box<dyn Future<Output = Result<IndexerEvent, IndexerError>> + Send>>;
Pin<Box<dyn Future<Output = Result<ChainOrchestratorEvent, ChainOrchestratorError>> + Send>>;

/// A type that represents a future that is being executed by the indexer.
pub(super) enum IndexerFuture {
/// A type that represents a future that is being executed by the chain orchestrator.
pub(super) enum ChainOrchestratorFuture {
HandleReorg(PendingIndexerFuture),
HandleFinalized(PendingIndexerFuture),
HandleBatchCommit(PendingIndexerFuture),
HandleBatchFinalization(PendingIndexerFuture),
HandleL1Message(PendingIndexerFuture),
HandleDerivedBlock(PendingIndexerFuture),
HandleL2Block(PendingIndexerFuture),
}

impl IndexerFuture {
impl ChainOrchestratorFuture {
/// Polls the future to completion.
pub(super) fn poll(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Result<IndexerEvent, IndexerError>> {
) -> Poll<Result<ChainOrchestratorEvent, ChainOrchestratorError>> {
match self {
Self::HandleReorg(fut) |
Self::HandleFinalized(fut) |
Self::HandleBatchCommit(fut) |
Self::HandleBatchFinalization(fut) |
Self::HandleL1Message(fut) |
Self::HandleDerivedBlock(fut) => fut.as_mut().poll(cx),
Self::HandleDerivedBlock(fut) |
Self::HandleL2Block(fut) => fut.as_mut().poll(cx),
}
}
}

// We implement the Debug trait for IndexerFuture to provide a human-readable representation of the
// enum variants.
impl fmt::Debug for IndexerFuture {
// We implement the Debug trait for ChainOrchestratorFuture to provide a human-readable
// representation of the enum variants.
impl fmt::Debug for ChainOrchestratorFuture {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::HandleReorg(_) => write!(f, "HandleReorg"),
Expand All @@ -48,6 +50,7 @@ impl fmt::Debug for IndexerFuture {
Self::HandleBatchFinalization(_) => write!(f, "HandleBatchFinalization"),
Self::HandleL1Message(_) => write!(f, "HandleL1Message"),
Self::HandleDerivedBlock(_) => write!(f, "HandleDerivedBlock"),
Self::HandleL2Block(_) => write!(f, "HandleL2Block"),
}
}
}
44 changes: 44 additions & 0 deletions crates/chain-orchestrator/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use alloy_json_rpc::RpcError;
use alloy_primitives::B256;
use alloy_transport::TransportErrorKind;
use scroll_db::{DatabaseError, L1MessageStart};

/// A type that represents an error that occurred in the chain orchestrator.
#[derive(Debug, thiserror::Error)]
pub enum ChainOrchestratorError {
/// An error occurred while interacting with the database.
#[error("indexing failed due to database error: {0}")]
DatabaseError(#[from] DatabaseError),
/// An error occurred while trying to fetch the L2 block from the database.
#[error("L2 block not found - block number: {0}")]
L2BlockNotFound(u64),
/// A fork was received from the peer that is associated with a reorg of the safe chain.
#[error("L2 safe block reorg detected")]
L2SafeBlockReorgDetected,
/// A block contains invalid L1 messages.
#[error("Block contains invalid L1 message. Expected: {expected:?}, Actual: {actual:?}")]
L1MessageMismatch {
/// The expected L1 messages hash.
expected: B256,
/// The actual L1 messages hash.
actual: B256,
},
/// An L1 message was not found in the database.
#[error("L1 message not found at {0}")]
L1MessageNotFound(L1MessageStart),
/// An inconsistency was detected when trying to consolidate the chain.
#[error("Chain inconsistency detected")]
ChainInconsistency,
/// The peer did not provide the requested block header.
#[error("A peer did not provide the requested block header")]
MissingBlockHeader {
/// The hash of the block header that was requested.
hash: B256,
},
/// An error occurred while making a network request.
#[error("Network request error: {0}")]
NetworkRequestError(#[from] reth_network_p2p::error::RequestError),
/// An error occurred while making a JSON-RPC request to the Execution Node (EN).
#[error("An error occurred while making a JSON-RPC request to the EN: {0}")]
RpcError(#[from] RpcError<TransportErrorKind>),
}
62 changes: 62 additions & 0 deletions crates/chain-orchestrator/src/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use alloy_consensus::Header;
use alloy_primitives::{Signature, B256};
use reth_network_peers::PeerId;
use reth_scroll_primitives::ScrollBlock;
use rollup_node_primitives::{BatchInfo, BlockInfo, ChainImport, L2BlockInfoWithL1Messages};

/// An event emitted by the `ChainOrchestrator`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChainOrchestratorEvent {
/// A new block has been received from the network but we have insufficient data to process it
/// due to being in optimistic mode.
InsufficientDataForReceivedBlock(B256),
/// The block that we have received is already known.
BlockAlreadyKnown(B256, PeerId),
/// A fork of the chain that is older than the current chain has been received.
OldForkReceived {
/// The headers of the old fork.
headers: Vec<Header>,
/// The peer that provided the old fork.
peer_id: PeerId,
/// The signature of the old fork.
signature: Signature,
},
/// The chain should be optimistically synced to the provided block.
OptimisticSync(ScrollBlock),
/// The chain has been extended, returning the new blocks.
ChainExtended(ChainImport),
/// The chain has reorged, returning the new chain and the peer that provided them.
ChainReorged(ChainImport),
/// A `BatchCommit` event has been indexed returning the batch info and the L2 block info to
/// revert to due to a batch revert.
BatchCommitIndexed {
/// The batch info.
batch_info: BatchInfo,
/// The safe L2 block info.
safe_head: Option<BlockInfo>,
},
/// A batch has been finalized returning the batch hash and new an optional finalized
/// L2 block.
BatchFinalized(B256, Option<BlockInfo>),
/// An L1 block has been finalized returning the L1 block number and an optional
/// finalized L2 block.
L1BlockFinalized(u64, Option<BlockInfo>),
/// A `L1Message` event has been committed returning the message queue index.
L1MessageCommitted(u64),
/// The chain has been unwound, returning the L1 block number of the new L1 head,
/// the L1 message queue index of the new L1 head, and optionally the L2 head and safe block
/// info if the unwind resulted in a new L2 head or safe block.
ChainUnwound {
/// The L1 block number of the new L1 head.
l1_block_number: u64,
/// The L1 message queue index of the new L1 head.
queue_index: Option<u64>,
/// The L2 head block info.
l2_head_block_info: Option<BlockInfo>,
/// The L2 safe block info.
l2_safe_block_info: Option<BlockInfo>,
},
/// An L2 block has been committed returning the [`L2BlockInfoWithL1Messages`] and an
/// optional [`BatchInfo`] if the block is associated with a committed batch.
L2ChainCommitted(L2BlockInfoWithL1Messages, Option<BatchInfo>, bool),
}
Loading
Loading