Skip to content

Commit 05e9ce8

Browse files
authored
feat: set initial fcs from db (#335)
* feat: set initial fcs from db * feat: answer comments * feat: save and restart with latest sequenced block for fcs * test: test correct restart at last sequenced block * test: fix graceful shutfown test * fix: lint * feat: store head for followers and sequencer * fix: lint * feat: answer comments
1 parent 625686c commit 05e9ce8

File tree

18 files changed

+269
-60
lines changed

18 files changed

+269
-60
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ parking_lot = "0.12"
220220
rand = { version = "0.9" }
221221
reqwest = "0.12"
222222
serde = { version = "1.0" }
223+
serde_json = { version = "1.0" }
223224
sea-orm = { version = "1.1.0" }
224225
thiserror = "2.0"
225226
tokio = { version = "1.39", default-features = false }

crates/chain-orchestrator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,5 @@ futures.workspace = true
7171
parking_lot.workspace = true
7272
rand.workspace = true
7373
reqwest.workspace = true
74-
serde_json = { version = "1.0" }
74+
serde_json.workspace = true
7575
tokio.workspace = true

crates/chain-orchestrator/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,10 @@ impl<
215215
let block_info: L2BlockInfoWithL1Messages = (&block_with_peer.block).into();
216216
Self::do_handle_block_from_peer(ctx, block_with_peer).await?;
217217
Retry::default()
218-
.retry("update_l1_messages_with_l2_block", || async {
218+
.retry("handle_sequenced_block", || async {
219219
let tx = database.tx_mut().await?;
220220
tx.update_l1_messages_with_l2_block(block_info.clone()).await?;
221+
tx.set_l2_head_block_info(block_info.block_info).await?;
221222
tx.commit().await?;
222223
Ok::<_, ChainOrchestratorError>(())
223224
})
@@ -483,9 +484,7 @@ impl<
483484
Retry::default()
484485
.retry("insert_block", || async {
485486
let tx = database.tx_mut().await?;
486-
for block in block_infos.clone() {
487-
tx.insert_block(block, batch_info).await?;
488-
}
487+
tx.insert_blocks(block_infos.clone(), batch_info).await?;
489488
tx.commit().await?;
490489
Ok::<_, ChainOrchestratorError>(())
491490
})
@@ -537,6 +536,7 @@ impl<
537536
.retry("update_l1_messages_from_l2_blocks", || async {
538537
let tx = database.tx_mut().await?;
539538
tx.update_l1_messages_from_l2_blocks(block_info.clone()).await?;
539+
tx.set_l2_head_block_info(head.block_info).await?;
540540
tx.commit().await?;
541541
Ok::<_, ChainOrchestratorError>(())
542542
})

crates/database/db/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ futures.workspace = true
2929
metrics.workspace = true
3030
metrics-derive.workspace = true
3131
sea-orm = { workspace = true, features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros"] }
32+
serde_json.workspace = true
3233
tempfile = { version = "3.20.0", optional = true }
3334
thiserror.workspace = true
3435
tokio = { workspace = true, features = ["macros", "sync"] }

crates/database/db/src/db.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ impl Database {
9898
db.tmp_dir = Some(dir);
9999
Ok(db)
100100
}
101+
102+
/// Returns a reference to the database tmp dir.
103+
#[cfg(feature = "test-utils")]
104+
pub const fn tmp_dir(&self) -> Option<&tempfile::TempDir> {
105+
self.tmp_dir.as_ref()
106+
}
101107
}
102108

103109
#[async_trait::async_trait]
@@ -961,4 +967,27 @@ mod test {
961967
assert!(retried_block_3.is_none());
962968
assert!(retried_block_4.is_none());
963969
}
970+
971+
#[tokio::test]
972+
async fn test_l2_block_head_roundtrip() {
973+
// Set up the test database.
974+
let db = setup_test_db().await;
975+
let tx = db.tx_mut().await.unwrap();
976+
977+
// Generate unstructured bytes.
978+
let mut bytes = [0u8; 40];
979+
rand::rng().fill(bytes.as_mut_slice());
980+
let mut u = Unstructured::new(&bytes);
981+
982+
// Generate and insert a block info as the head.
983+
let block_info = BlockInfo::arbitrary(&mut u).unwrap();
984+
tx.set_l2_head_block_info(block_info).await.unwrap();
985+
tx.commit().await.unwrap();
986+
987+
// Retrieve and verify the head block info.
988+
let tx = db.tx().await.unwrap();
989+
let head_block_info = tx.get_l2_head_block_info().await.unwrap().unwrap();
990+
991+
assert_eq!(head_block_info, block_info);
992+
}
964993
}

crates/database/db/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub enum DatabaseError {
1313
/// A generic error occurred.
1414
#[error("parse signature error: {0}")]
1515
ParseSignatureError(String),
16+
/// Failed to serde the metadata value.
17+
#[error("failed to serde metadata value: {0}")]
18+
MetadataSerdeError(#[from] serde_json::Error),
1619
/// The L1 message was not found in database.
1720
#[error("L1 message at index [{0}] not found in database")]
1821
L1MessageNotFound(L1MessageStart),

crates/database/db/src/models/metadata.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,6 @@ impl ActiveModelBehavior for ActiveModel {}
2121

2222
impl From<Metadata> for ActiveModel {
2323
fn from(metadata: Metadata) -> Self {
24-
Self {
25-
key: ActiveValue::Set("l1_finalized_block".to_owned()),
26-
value: ActiveValue::Set(metadata.l1_finalized_block.to_string()),
27-
}
28-
}
29-
}
30-
31-
impl From<Model> for Metadata {
32-
fn from(value: Model) -> Self {
33-
debug_assert!(value.key == "l1_finalized_block");
34-
Self { l1_finalized_block: value.value.parse().expect("invalid value") }
24+
Self { key: ActiveValue::Set(metadata.key), value: ActiveValue::Set(metadata.value) }
3525
}
3626
}

crates/database/db/src/operations.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,27 @@ pub trait DatabaseWriteOperations: WriteConnectionProvider + DatabaseReadOperati
7171
) -> Result<(), DatabaseError> {
7272
tracing::trace!(target: "scroll::db", block_number, "Updating the latest finalized L1 block number in the database.");
7373
let metadata: models::metadata::ActiveModel =
74-
Metadata { l1_finalized_block: block_number }.into();
74+
Metadata { key: "l1_finalized_block".to_string(), value: block_number.to_string() }
75+
.into();
76+
Ok(models::metadata::Entity::insert(metadata)
77+
.on_conflict(
78+
OnConflict::column(models::metadata::Column::Key)
79+
.update_column(models::metadata::Column::Value)
80+
.to_owned(),
81+
)
82+
.exec(self.get_connection())
83+
.await
84+
.map(|_| ())?)
85+
}
86+
87+
/// Set the L2 head block info.
88+
async fn set_l2_head_block_info(&self, block_info: BlockInfo) -> Result<(), DatabaseError> {
89+
tracing::trace!(target: "scroll::db", ?block_info, "Updating the L2 head block info in the database.");
90+
let metadata: models::metadata::ActiveModel = Metadata {
91+
key: "l2_head_block".to_string(),
92+
value: serde_json::to_string(&block_info)?,
93+
}
94+
.into();
7595
Ok(models::metadata::Entity::insert(metadata)
7696
.on_conflict(
7797
OnConflict::column(models::metadata::Column::Key)
@@ -444,6 +464,18 @@ pub trait DatabaseReadOperations: ReadConnectionProvider + Sync {
444464
.map(|x| x.and_then(|x| x.parse::<u64>().ok()))?)
445465
}
446466

467+
/// Get the latest L2 head block info.
468+
async fn get_l2_head_block_info(&self) -> Result<Option<BlockInfo>, DatabaseError> {
469+
Ok(models::metadata::Entity::find()
470+
.filter(models::metadata::Column::Key.eq("l2_head_block"))
471+
.select_only()
472+
.column(models::metadata::Column::Value)
473+
.into_tuple::<String>()
474+
.one(self.get_connection())
475+
.await
476+
.map(|x| x.and_then(|x| serde_json::from_str(&x).ok()))?)
477+
}
478+
447479
/// Get an iterator over all [`BatchCommitData`]s in the database.
448480
async fn get_batches<'a>(
449481
&'a self,
@@ -571,7 +603,9 @@ pub trait DatabaseReadOperations: ReadConnectionProvider + Sync {
571603
})?)
572604
}
573605

574-
/// Get the latest safe L2 ([`BlockInfo`], [`BatchInfo`]) from the database.
606+
/// Get the latest safe/finalized L2 ([`BlockInfo`], [`BatchInfo`]) from the database. Until we
607+
/// update the batch handling logic with issue #273, we don't differentiate between safe and
608+
/// finalized l2 blocks.
575609
async fn get_latest_safe_l2_info(
576610
&self,
577611
) -> Result<Option<(BlockInfo, BatchInfo)>, DatabaseError> {

crates/engine/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ rollup-node-providers.workspace = true
4040
rollup-node-signer.workspace = true
4141

4242
# scroll
43+
scroll-db.workspace = true
4344
scroll-network.workspace = true
4445

4546
# misc
@@ -70,6 +71,7 @@ test-utils = [
7071
"rollup-node-providers/test-utils",
7172
"reth-chainspec/test-utils",
7273
"reth-primitives-traits/test-utils",
74+
"scroll-db/test-utils",
7375
]
7476
serde = [
7577
"alloy-eips/serde",

0 commit comments

Comments
 (0)