diff --git a/Cargo.lock b/Cargo.lock index 6cea3ec5..d0df7eb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10772,6 +10772,7 @@ dependencies = [ "reth-scroll-node", "reth-scroll-primitives", "reth-scroll-rpc", + "reth-storage-api", "reth-tasks", "reth-tokio-util", "reth-tracing", @@ -10907,6 +10908,7 @@ dependencies = [ "reth-scroll-primitives", "scroll-alloy-consensus", "scroll-alloy-rpc-types-engine", + "serde", ] [[package]] @@ -11547,6 +11549,7 @@ dependencies = [ "scroll-alloy-rpc-types-engine", "scroll-migration", "sea-orm", + "serde_json", "tempfile", "thiserror 2.0.16", "tokio", @@ -11611,6 +11614,7 @@ dependencies = [ "scroll-alloy-network", "scroll-alloy-provider", "scroll-alloy-rpc-types-engine", + "scroll-db", "scroll-engine", "scroll-network", "thiserror 2.0.16", diff --git a/Cargo.toml b/Cargo.toml index 45332320..32b0c3b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -220,6 +220,7 @@ parking_lot = "0.12" rand = { version = "0.9" } reqwest = "0.12" serde = { version = "1.0" } +serde_json = { version = "1.0" } sea-orm = { version = "1.1.0" } thiserror = "2.0" tokio = { version = "1.39", default-features = false } diff --git a/crates/chain-orchestrator/Cargo.toml b/crates/chain-orchestrator/Cargo.toml index 3462acaf..5c4697dd 100644 --- a/crates/chain-orchestrator/Cargo.toml +++ b/crates/chain-orchestrator/Cargo.toml @@ -71,5 +71,5 @@ futures.workspace = true parking_lot.workspace = true rand.workspace = true reqwest.workspace = true -serde_json = { version = "1.0" } +serde_json.workspace = true tokio.workspace = true diff --git a/crates/chain-orchestrator/src/lib.rs b/crates/chain-orchestrator/src/lib.rs index 99d59d3c..aa5b2389 100644 --- a/crates/chain-orchestrator/src/lib.rs +++ b/crates/chain-orchestrator/src/lib.rs @@ -215,9 +215,10 @@ impl< let block_info: L2BlockInfoWithL1Messages = (&block_with_peer.block).into(); Self::do_handle_block_from_peer(ctx, block_with_peer).await?; Retry::default() - .retry("update_l1_messages_with_l2_block", || async { + .retry("handle_sequenced_block", || async { let tx = database.tx_mut().await?; tx.update_l1_messages_with_l2_block(block_info.clone()).await?; + tx.set_l2_head_block_info(block_info.block_info).await?; tx.commit().await?; Ok::<_, ChainOrchestratorError>(()) }) @@ -483,9 +484,7 @@ impl< Retry::default() .retry("insert_block", || async { let tx = database.tx_mut().await?; - for block in block_infos.clone() { - tx.insert_block(block, batch_info).await?; - } + tx.insert_blocks(block_infos.clone(), batch_info).await?; tx.commit().await?; Ok::<_, ChainOrchestratorError>(()) }) @@ -537,6 +536,7 @@ impl< .retry("update_l1_messages_from_l2_blocks", || async { let tx = database.tx_mut().await?; tx.update_l1_messages_from_l2_blocks(block_info.clone()).await?; + tx.set_l2_head_block_info(head.block_info).await?; tx.commit().await?; Ok::<_, ChainOrchestratorError>(()) }) diff --git a/crates/database/db/Cargo.toml b/crates/database/db/Cargo.toml index e88e8859..c75f0458 100644 --- a/crates/database/db/Cargo.toml +++ b/crates/database/db/Cargo.toml @@ -29,6 +29,7 @@ futures.workspace = true metrics.workspace = true metrics-derive.workspace = true sea-orm = { workspace = true, features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros"] } +serde_json.workspace = true tempfile = { version = "3.20.0", optional = true } thiserror.workspace = true tokio = { workspace = true, features = ["macros", "sync"] } diff --git a/crates/database/db/src/db.rs b/crates/database/db/src/db.rs index 174aac90..2e982054 100644 --- a/crates/database/db/src/db.rs +++ b/crates/database/db/src/db.rs @@ -98,6 +98,12 @@ impl Database { db.tmp_dir = Some(dir); Ok(db) } + + /// Returns a reference to the database tmp dir. + #[cfg(feature = "test-utils")] + pub const fn tmp_dir(&self) -> Option<&tempfile::TempDir> { + self.tmp_dir.as_ref() + } } #[async_trait::async_trait] @@ -961,4 +967,27 @@ mod test { assert!(retried_block_3.is_none()); assert!(retried_block_4.is_none()); } + + #[tokio::test] + async fn test_l2_block_head_roundtrip() { + // Set up the test database. + let db = setup_test_db().await; + let tx = db.tx_mut().await.unwrap(); + + // Generate unstructured bytes. + let mut bytes = [0u8; 40]; + rand::rng().fill(bytes.as_mut_slice()); + let mut u = Unstructured::new(&bytes); + + // Generate and insert a block info as the head. + let block_info = BlockInfo::arbitrary(&mut u).unwrap(); + tx.set_l2_head_block_info(block_info).await.unwrap(); + tx.commit().await.unwrap(); + + // Retrieve and verify the head block info. + let tx = db.tx().await.unwrap(); + let head_block_info = tx.get_l2_head_block_info().await.unwrap().unwrap(); + + assert_eq!(head_block_info, block_info); + } } diff --git a/crates/database/db/src/error.rs b/crates/database/db/src/error.rs index 0320a03f..7702cb2f 100644 --- a/crates/database/db/src/error.rs +++ b/crates/database/db/src/error.rs @@ -13,6 +13,9 @@ pub enum DatabaseError { /// A generic error occurred. #[error("parse signature error: {0}")] ParseSignatureError(String), + /// Failed to serde the metadata value. + #[error("failed to serde metadata value: {0}")] + MetadataSerdeError(#[from] serde_json::Error), /// The L1 message was not found in database. #[error("L1 message at index [{0}] not found in database")] L1MessageNotFound(L1MessageStart), diff --git a/crates/database/db/src/models/metadata.rs b/crates/database/db/src/models/metadata.rs index 3afe2351..5d292e67 100644 --- a/crates/database/db/src/models/metadata.rs +++ b/crates/database/db/src/models/metadata.rs @@ -21,16 +21,6 @@ impl ActiveModelBehavior for ActiveModel {} impl From for ActiveModel { fn from(metadata: Metadata) -> Self { - Self { - key: ActiveValue::Set("l1_finalized_block".to_owned()), - value: ActiveValue::Set(metadata.l1_finalized_block.to_string()), - } - } -} - -impl From for Metadata { - fn from(value: Model) -> Self { - debug_assert!(value.key == "l1_finalized_block"); - Self { l1_finalized_block: value.value.parse().expect("invalid value") } + Self { key: ActiveValue::Set(metadata.key), value: ActiveValue::Set(metadata.value) } } } diff --git a/crates/database/db/src/operations.rs b/crates/database/db/src/operations.rs index 05a79d82..5eee0609 100644 --- a/crates/database/db/src/operations.rs +++ b/crates/database/db/src/operations.rs @@ -71,7 +71,27 @@ pub trait DatabaseWriteOperations: WriteConnectionProvider + DatabaseReadOperati ) -> Result<(), DatabaseError> { tracing::trace!(target: "scroll::db", block_number, "Updating the latest finalized L1 block number in the database."); let metadata: models::metadata::ActiveModel = - Metadata { l1_finalized_block: block_number }.into(); + Metadata { key: "l1_finalized_block".to_string(), value: block_number.to_string() } + .into(); + Ok(models::metadata::Entity::insert(metadata) + .on_conflict( + OnConflict::column(models::metadata::Column::Key) + .update_column(models::metadata::Column::Value) + .to_owned(), + ) + .exec(self.get_connection()) + .await + .map(|_| ())?) + } + + /// Set the L2 head block info. + async fn set_l2_head_block_info(&self, block_info: BlockInfo) -> Result<(), DatabaseError> { + tracing::trace!(target: "scroll::db", ?block_info, "Updating the L2 head block info in the database."); + let metadata: models::metadata::ActiveModel = Metadata { + key: "l2_head_block".to_string(), + value: serde_json::to_string(&block_info)?, + } + .into(); Ok(models::metadata::Entity::insert(metadata) .on_conflict( OnConflict::column(models::metadata::Column::Key) @@ -444,6 +464,18 @@ pub trait DatabaseReadOperations: ReadConnectionProvider + Sync { .map(|x| x.and_then(|x| x.parse::().ok()))?) } + /// Get the latest L2 head block info. + async fn get_l2_head_block_info(&self) -> Result, DatabaseError> { + Ok(models::metadata::Entity::find() + .filter(models::metadata::Column::Key.eq("l2_head_block")) + .select_only() + .column(models::metadata::Column::Value) + .into_tuple::() + .one(self.get_connection()) + .await + .map(|x| x.and_then(|x| serde_json::from_str(&x).ok()))?) + } + /// Get an iterator over all [`BatchCommitData`]s in the database. async fn get_batches<'a>( &'a self, @@ -571,7 +603,9 @@ pub trait DatabaseReadOperations: ReadConnectionProvider + Sync { })?) } - /// Get the latest safe L2 ([`BlockInfo`], [`BatchInfo`]) from the database. + /// Get the latest safe/finalized L2 ([`BlockInfo`], [`BatchInfo`]) from the database. Until we + /// update the batch handling logic with issue #273, we don't differentiate between safe and + /// finalized l2 blocks. async fn get_latest_safe_l2_info( &self, ) -> Result, DatabaseError> { diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 0d822e9f..551b7148 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -40,6 +40,7 @@ rollup-node-providers.workspace = true rollup-node-signer.workspace = true # scroll +scroll-db.workspace = true scroll-network.workspace = true # misc @@ -70,6 +71,7 @@ test-utils = [ "rollup-node-providers/test-utils", "reth-chainspec/test-utils", "reth-primitives-traits/test-utils", + "scroll-db/test-utils", ] serde = [ "alloy-eips/serde", diff --git a/crates/engine/src/fcs.rs b/crates/engine/src/fcs.rs index 784d57d6..1dbae201 100644 --- a/crates/engine/src/fcs.rs +++ b/crates/engine/src/fcs.rs @@ -33,7 +33,7 @@ impl ForkchoiceState { /// Creates a new [`ForkchoiceState`] instance setting the `head`, `safe` and `finalized` block /// info to the provided `genesis` hash. - pub const fn head_from_genesis(genesis: B256) -> Self { + pub const fn from_genesis(genesis: B256) -> Self { Self::new( BlockInfo { hash: genesis, number: 0 }, BlockInfo { hash: genesis, number: 0 }, @@ -43,7 +43,7 @@ impl ForkchoiceState { /// Creates a [`ForkchoiceState`] instance setting the `head`, `safe` and `finalized` hash to /// the appropriate genesis values by reading from the provider. - pub async fn head_from_provider>(provider: P) -> Option { + pub async fn from_provider>(provider: &P) -> Option { let latest_block = provider.get_block(BlockId::Number(BlockNumberOrTag::Latest)).await.ok()??; let safe_block = @@ -65,7 +65,7 @@ impl ForkchoiceState { pub fn head_from_chain_spec>( chain_spec: CS, ) -> Option { - Some(Self::head_from_genesis(genesis_hash_from_chain_spec(chain_spec)?)) + Some(Self::from_genesis(genesis_hash_from_chain_spec(chain_spec)?)) } /// Updates the `head` block info. diff --git a/crates/manager/src/manager/mod.rs b/crates/manager/src/manager/mod.rs index 6e033ea9..1896b59d 100644 --- a/crates/manager/src/manager/mod.rs +++ b/crates/manager/src/manager/mod.rs @@ -124,7 +124,7 @@ pub struct RollupNodeManager< database: Arc, /// The original block time configuration for restoring automatic sequencing. block_time_config: Option, - // metrics for the rollup node manager. + /// Metrics for the rollup node manager. metrics: RollupNodeManagerMetrics, } @@ -381,26 +381,17 @@ where event_sender.notify(RollupManagerEvent::Reorg(l1_block_number)); } } - ChainOrchestratorEvent::ChainExtended(chain_import) => { - self.metrics - .handle_chain_import_block_number - .set(chain_import.chain.last().unwrap().number as f64); - trace!(target: "scroll::node::manager", head = ?chain_import.chain.last().unwrap().header.clone(), peer_id = ?chain_import.peer_id.clone(), "Received chain extension from peer"); - // Issue the new chain to the engine driver for processing. - self.engine.handle_chain_import(chain_import) - } + ChainOrchestratorEvent::ChainExtended(chain_import) | ChainOrchestratorEvent::ChainReorged(chain_import) => { self.metrics .handle_chain_import_block_number .set(chain_import.chain.last().unwrap().number as f64); - trace!(target: "scroll::node::manager", head = ?chain_import.chain.last().unwrap().header, ?chain_import.peer_id, "Received chain reorg from peer"); // Issue the new chain to the engine driver for processing. self.engine.handle_chain_import(chain_import) } ChainOrchestratorEvent::OptimisticSync(block) => { let block_info: BlockInfo = (&block).into(); - trace!(target: "scroll::node::manager", ?block_info, "Received optimistic sync from peer"); self.metrics.handle_optimistic_syncing_block_number.set(block_info.number as f64); diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index fa4adf37..4fca7863 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -108,12 +108,13 @@ reth-provider.workspace = true reth-primitives-traits.workspace = true reth-rpc-server-types.workspace = true reth-scroll-node = { workspace = true, features = ["test-utils"] } +reth-storage-api.workspace = true reth-tasks.workspace = true reth-tokio-util.workspace = true reth-tracing.workspace = true rollup-node = { workspace = true, features = ["test-utils"] } scroll-alloy-rpc-types-engine.workspace = true -serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] } +serde_json = { workspace = true, features = ["alloc"] } color-eyre = "0.6" alloy-rpc-types-eth = { workspace = true } diff --git a/crates/node/src/args.rs b/crates/node/src/args.rs index ab386b92..5f605203 100644 --- a/crates/node/src/args.rs +++ b/crates/node/src/args.rs @@ -253,9 +253,31 @@ impl ScrollRollupNodeConfig { ForkchoiceState::head_from_chain_spec(chain_spec.clone()) .expect("failed to derive forkchoice state from chain spec") }; - let mut fcs = ForkchoiceState::head_from_provider(l2_provider.clone()) - .await - .unwrap_or_else(chain_spec_fcs); + let mut fcs = + ForkchoiceState::from_provider(&l2_provider).await.unwrap_or_else(chain_spec_fcs); + + // On startup we replay the latest batch of blocks from the database as such we set the safe + // block hash to the latest block hash associated with the previous consolidated + // batch in the database. + let tx = db.tx_mut().await?; + let (startup_safe_block, l1_start_block_number) = + tx.prepare_on_startup(chain_spec.genesis_hash()).await?; + tx.commit().await?; + if let Some(block_info) = startup_safe_block { + fcs.update_safe_block_info(block_info); + } else { + fcs.update_safe_block_info(BlockInfo { + hash: genesis_hash_from_chain_spec(chain_spec.clone()).unwrap(), + number: 0, + }); + } + + // Update the head block info if available and ahead of finalized. + if let Some(latest_block) = db.tx().await?.get_l2_head_block_info().await? { + if latest_block > *fcs.finalized_block_info() { + fcs.update_head_block_info(latest_block); + } + } let chain_spec = Arc::new(chain_spec.clone()); @@ -277,22 +299,6 @@ impl ScrollRollupNodeConfig { authorized_signer, ); - // On startup we replay the latest batch of blocks from the database as such we set the safe - // block hash to the latest block hash associated with the previous consolidated - // batch in the database. - let tx = db.tx_mut().await?; - let (startup_safe_block, l1_start_block_number) = - tx.prepare_on_startup(chain_spec.genesis_hash()).await?; - tx.commit().await?; - if let Some(block_info) = startup_safe_block { - fcs.update_safe_block_info(block_info); - } else { - fcs.update_safe_block_info(BlockInfo { - hash: genesis_hash_from_chain_spec(chain_spec.clone()).unwrap(), - number: 0, - }); - } - tracing::info!(target: "scroll::node::args", fcs = ?fcs, payload_building_duration = ?self.sequencer_args.payload_building_duration, "Starting engine driver"); let engine = EngineDriver::new( Arc::new(engine_api), diff --git a/crates/node/tests/e2e.rs b/crates/node/tests/e2e.rs index e0651894..44322970 100644 --- a/crates/node/tests/e2e.rs +++ b/crates/node/tests/e2e.rs @@ -6,7 +6,7 @@ use alloy_rpc_types_eth::Block; use alloy_signer::Signer; use alloy_signer_local::PrivateKeySigner; use eyre::Ok; -use futures::StreamExt; +use futures::{task::noop_waker_ref, FutureExt, StreamExt}; use reth_chainspec::EthChainSpec; use reth_e2e_test_utils::{NodeHelperType, TmpDB}; use reth_network::{NetworkConfigBuilder, NetworkEventListenerProvider, Peers, PeersInfo}; @@ -17,6 +17,7 @@ use reth_rpc_api::EthApiServer; use reth_scroll_chainspec::{ScrollChainSpec, SCROLL_DEV, SCROLL_MAINNET, SCROLL_SEPOLIA}; use reth_scroll_node::ScrollNetworkPrimitives; use reth_scroll_primitives::ScrollBlock; +use reth_storage_api::BlockReader; use reth_tokio_util::EventStream; use rollup_node::{ constants::SCROLL_GAS_LIMIT, @@ -31,15 +32,22 @@ use rollup_node::{ }; use rollup_node_chain_orchestrator::ChainOrchestratorEvent; use rollup_node_manager::{RollupManagerCommand, RollupManagerEvent}; -use rollup_node_primitives::{sig_encode_hash, BatchCommitData, ConsensusUpdate}; +use rollup_node_primitives::{sig_encode_hash, BatchCommitData, BlockInfo, ConsensusUpdate}; use rollup_node_sequencer::L1MessageInclusionMode; use rollup_node_watcher::L1Notification; use scroll_alloy_consensus::TxL1Message; use scroll_alloy_rpc_types::Transaction as ScrollAlloyTransaction; -use scroll_db::L1MessageStart; +use scroll_db::{test_utils::setup_test_db, L1MessageStart}; use scroll_network::NewBlockWithPeer; use scroll_wire::{ScrollWireConfig, ScrollWireProtocolHandler}; -use std::{path::PathBuf, sync::Arc, time::Duration}; +use std::{ + future::Future, + path::PathBuf, + pin::pin, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; use tokio::{ sync::{oneshot, Mutex}, time, @@ -1053,6 +1061,133 @@ async fn graceful_shutdown_consolidates_most_recent_batch_on_startup() -> eyre:: Ok(()) } +/// Test that when the rollup node manager is shutdown, it restarts with the head set to the latest +/// sequenced block stored in database. +#[tokio::test] +async fn graceful_shutdown_sets_fcs_to_latest_sequenced_block_in_db_on_start_up() -> eyre::Result<()> +{ + reth_tracing::init_test_tracing(); + let chain_spec = (*SCROLL_DEV).clone(); + + // Create a config with a random signer. + let mut config = default_sequencer_test_scroll_rollup_node_config(); + config.signer_args.private_key = Some(PrivateKeySigner::random()); + + // Launch a node + let (mut nodes, _tasks, _) = + setup_engine(config.clone(), 1, chain_spec.clone(), false, false).await?; + let node = nodes.pop().unwrap(); + + // Instantiate the rollup node manager. + let test_db = setup_test_db().await; + let path = test_db.tmp_dir().expect("Database started with temp dir").path().join("test.db"); + config.blob_provider_args.beacon_node_urls = Some(vec!["http://dummy:8545" + .parse() + .expect("valid url that will not be used as test batches use calldata")]); + config.hydrate(node.inner.config.clone()).await?; + + let (_, events) = ScrollWireProtocolHandler::new(ScrollWireConfig::new(true)); + let (mut rnm, handle, _) = config + .clone() + .build( + RollupNodeContext::new( + node.inner.network.clone(), + chain_spec.clone(), + path.clone(), + SCROLL_GAS_LIMIT, + ), + events, + node.inner.add_ons_handle.rpc_handle.rpc_server_handles.clone(), + ) + .await?; + + // Poll the rnm until we get an event stream listener. + let mut rnm_events_fut = pin!(handle.get_event_listener()); + let mut rnm_events = loop { + let _ = rnm.poll_unpin(&mut Context::from_waker(noop_waker_ref())); + if let Poll::Ready(Result::Ok(events)) = + rnm_events_fut.as_mut().poll(&mut Context::from_waker(noop_waker_ref())) + { + break events; + } + tokio::time::sleep(Duration::from_millis(10)).await; + }; + + // Wait for the EN to be synced to block 10. + let execution_node_provider = node.inner.provider; + loop { + handle.build_block().await; + let block_number = loop { + let _ = rnm.poll_unpin(&mut Context::from_waker(noop_waker_ref())); + if let Poll::Ready(Some(RollupManagerEvent::BlockSequenced(block))) = + rnm_events.poll_next_unpin(&mut Context::from_waker(noop_waker_ref())) + { + break block.header.number + } + tokio::time::sleep(Duration::from_millis(10)).await; + }; + if block_number == 10 { + break + } + } + + // Get the block info for block 10. + let db_head_block_info = execution_node_provider + .block(10u64.into())? + .map(|b| BlockInfo { number: b.number, hash: b.hash_slow() }) + .expect("block exists"); + + // Build one block, and only wait for the block sequenced event. + handle.build_block().await; + loop { + let _ = rnm.poll_unpin(&mut Context::from_waker(noop_waker_ref())); + if let Poll::Ready(Some(RollupManagerEvent::BlockSequenced(_))) = + rnm_events.poll_next_unpin(&mut Context::from_waker(noop_waker_ref())) + { + break + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + + // At this point, we have the EN synced to a block > 10 and the RNM has sequenced one additional + // block, validating it with the EN, but not updating the last sequenced block in the DB. + + // Simulate a shutdown of the rollup node manager by dropping it. + drop(rnm_events); + drop(rnm); + + // Start the RNM again. + let (_, events) = ScrollWireProtocolHandler::new(ScrollWireConfig::new(true)); + let (_rnm, handle, _) = config + .clone() + .build( + RollupNodeContext::new( + node.inner.network.clone(), + chain_spec, + path.clone(), + SCROLL_GAS_LIMIT, + ), + events, + node.inner.add_ons_handle.rpc_handle.rpc_server_handles.clone(), + ) + .await?; + + // Launch the rnm in a task. + tokio::spawn(async move { + let _ = _rnm.await; + }); + + // Check the fcs. + let (tx, rx) = oneshot::channel(); + handle.send_command(RollupManagerCommand::Status(tx)).await; + let status = rx.await?; + + // The fcs should be set to the database head. + assert_eq!(status.forkchoice_state.head_block_info(), &db_head_block_info); + + Ok(()) +} + #[tokio::test] #[ignore = "Enable once we implement issue #273"] async fn can_handle_batch_revert() -> eyre::Result<()> { @@ -1355,7 +1490,7 @@ async fn test_custom_genesis_block_production_and_propagation() -> eyre::Result< } } }, - "number": "0x0", + "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" }"#; diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 214183a6..948c1d48 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -33,6 +33,7 @@ reth-node-core.workspace = true arbitrary = { workspace = true, optional = true } derive_more = { workspace = true, features = ["from"] } eyre.workspace = true +serde.workspace = true [features] default = ["std"] diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 80254060..ab7d8c08 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -3,6 +3,7 @@ use alloy_eips::{BlockNumHash, Decodable2718}; use alloy_primitives::{B256, U256}; use alloy_rpc_types_engine::ExecutionPayload; use core::{ + cmp::Ordering, future::Future, pin::Pin, task::{ready, Context, Poll}, @@ -16,7 +17,7 @@ use std::vec::Vec; pub const DEFAULT_BLOCK_DIFFICULTY: U256 = U256::from_limbs([1, 0, 0, 0]); /// Information about a block. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct BlockInfo { /// The block number. pub number: u64, @@ -24,6 +25,12 @@ pub struct BlockInfo { pub hash: B256, } +impl PartialOrd for BlockInfo { + fn partial_cmp(&self, other: &Self) -> Option { + self.number.partial_cmp(&other.number) + } +} + impl BlockInfo { /// Returns a new instance of [`BlockInfo`]. pub const fn new(number: u64, hash: B256) -> Self { diff --git a/crates/primitives/src/metadata.rs b/crates/primitives/src/metadata.rs index 22e2c911..0c8063f8 100644 --- a/crates/primitives/src/metadata.rs +++ b/crates/primitives/src/metadata.rs @@ -1,6 +1,10 @@ +use std::string::String; + /// Contains metadata relevant to the rollup node. #[derive(Debug)] pub struct Metadata { - /// The latest finalized L1 block. - pub l1_finalized_block: u64, + /// The metadata key. + pub key: String, + /// The metadata value. + pub value: String, }