Skip to content

Commit b0e2d20

Browse files
authored
feat: message queue hash (#59)
* feat: add message queue hash to db schema. * feat: update L1MessageWithBlockNumber to L1MessageEnvelope * feat: fetch block timestamp block for l1 message in watcher * feat: add chainspec to rollup node * feat: compute rolling hash in indexer * fix: rename batch to block to derived block * fix: reorder args * fix: lints * fix: missing ts * fix: set queue hash column as null * feat: implement hash cursor for all `L1MessageProvider` instances * fix: merge main
1 parent c7af05c commit b0e2d20

File tree

25 files changed

+428
-183
lines changed

25 files changed

+428
-183
lines changed

Cargo.lock

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ alloy-transport = { version = "0.14.0", default-features = false }
131131

132132
# scroll-alloy
133133
scroll-alloy-consensus = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
134+
scroll-alloy-hardforks = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
134135
scroll-alloy-network = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
135136
scroll-alloy-provider = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
136137
scroll-alloy-rpc-types-engine = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
@@ -149,6 +150,7 @@ reth-tokio-util = { git = "https://github.com/scroll-tech/reth.git", default-fea
149150
# reth-scroll
150151
reth-scroll-chainspec = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
151152
reth-scroll-engine-primitives = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
153+
reth-scroll-forks = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
152154
reth-scroll-node = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
153155
reth-scroll-primitives = { git = "https://github.com/scroll-tech/reth.git", default-features = false }
154156

bin/rollup/src/network.rs

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ where
133133
// Wrap the database in an Arc
134134
let db = Arc::new(db);
135135

136+
// Get the chain specification
137+
let chain_spec = ctx.chain_spec();
138+
136139
// Spawn the L1Watcher
137140
let l1_provider_args = self.config.l1_provider_args;
138141
let l1_notification_rx = if let Some(l1_rpc_url) = l1_provider_args.l1_rpc_url {
@@ -183,6 +186,7 @@ where
183186
db,
184187
l1_notification_rx,
185188
consensus,
189+
chain_spec,
186190
block_rx,
187191
sequencer,
188192
block_time,

crates/database/db/src/db.rs

+26-17
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ mod test {
5555
use arbitrary::{Arbitrary, Unstructured};
5656
use futures::StreamExt;
5757
use rand::Rng;
58-
use rollup_node_primitives::{BatchCommitData, BatchInfo, BlockInfo, L1MessageWithBlockNumber};
58+
use rollup_node_primitives::{BatchCommitData, BatchInfo, BlockInfo, L1MessageEnvelope};
5959
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
6060

6161
#[tokio::test]
@@ -119,13 +119,16 @@ mod test {
119119
let mut u = Unstructured::new(&bytes);
120120

121121
// Generate a random L1Message.
122-
let l1_message = L1MessageWithBlockNumber::arbitrary(&mut u).unwrap();
122+
let l1_message = L1MessageEnvelope::arbitrary(&mut u).unwrap();
123123

124124
// Round trip the L1Message through the database.
125125
db.insert_l1_message(l1_message.clone()).await.unwrap();
126-
let l1_message_from_db =
127-
db.get_l1_message(l1_message.transaction.queue_index).await.unwrap().unwrap();
128-
assert_eq!(l1_message, l1_message_from_db);
126+
let l1_message_from_db_index =
127+
db.get_l1_message_by_index(l1_message.transaction.queue_index).await.unwrap().unwrap();
128+
let l1_message_from_db_hash =
129+
db.get_l1_message_by_hash(l1_message.queue_hash.unwrap()).await.unwrap().unwrap();
130+
assert_eq!(l1_message, l1_message_from_db_index);
131+
assert_eq!(l1_message, l1_message_from_db_hash);
129132
}
130133

131134
#[tokio::test]
@@ -139,7 +142,7 @@ mod test {
139142
}
140143

141144
#[tokio::test]
142-
async fn test_database_batch_to_block_exists() {
145+
async fn test_derived_block_exists() {
143146
// Set up the test database.
144147
let db = setup_test_db().await;
145148

@@ -157,7 +160,7 @@ mod test {
157160
for _ in 0..10 {
158161
let block_info =
159162
BlockInfo { number: block_number, hash: B256::arbitrary(&mut u).unwrap() };
160-
db.insert_batch_to_block(batch_info, block_info).await.unwrap();
163+
db.insert_derived_block(block_info, batch_info).await.unwrap();
161164
block_number += 1;
162165
}
163166

@@ -168,7 +171,7 @@ mod test {
168171
}
169172

170173
#[tokio::test]
171-
async fn test_database_batch_to_block_missing() {
174+
async fn test_derived_block_missing() {
172175
// Set up the test database.
173176
let db = setup_test_db().await;
174177

@@ -191,7 +194,7 @@ mod test {
191194
for _ in 0..10 {
192195
let block_info =
193196
BlockInfo { number: block_number, hash: B256::arbitrary(&mut u).unwrap() };
194-
db.insert_batch_to_block(first_batch_info, block_info).await.unwrap();
197+
db.insert_derived_block(block_info, first_batch_info).await.unwrap();
195198
block_number += 1;
196199
}
197200

@@ -256,8 +259,8 @@ mod test {
256259
let mut u = Unstructured::new(&bytes);
257260

258261
// Generate 2 random L1Messages.
259-
let l1_message_1 = L1MessageWithBlockNumber::arbitrary(&mut u).unwrap();
260-
let l1_message_2 = L1MessageWithBlockNumber::arbitrary(&mut u).unwrap();
262+
let l1_message_1 = L1MessageEnvelope::arbitrary(&mut u).unwrap();
263+
let l1_message_2 = L1MessageEnvelope::arbitrary(&mut u).unwrap();
261264

262265
// Insert the L1Messages into the database in a transaction.
263266
let tx = db.tx().await.unwrap();
@@ -266,11 +269,17 @@ mod test {
266269
tx.commit().await.unwrap();
267270

268271
// Check that the L1Messages are in the database.
269-
let l1_message_1_from_db =
270-
db.get_l1_message(l1_message_1.transaction.queue_index).await.unwrap().unwrap();
272+
let l1_message_1_from_db = db
273+
.get_l1_message_by_index(l1_message_1.transaction.queue_index)
274+
.await
275+
.unwrap()
276+
.unwrap();
271277
assert_eq!(l1_message_1, l1_message_1_from_db);
272-
let l1_message_2_from_db =
273-
db.get_l1_message(l1_message_2.transaction.queue_index).await.unwrap().unwrap();
278+
let l1_message_2_from_db = db
279+
.get_l1_message_by_index(l1_message_2.transaction.queue_index)
280+
.await
281+
.unwrap()
282+
.unwrap();
274283
assert_eq!(l1_message_2, l1_message_2_from_db);
275284
}
276285

@@ -285,8 +294,8 @@ mod test {
285294
let mut u = Unstructured::new(&bytes);
286295

287296
// Generate 2 random L1Messages.
288-
let l1_message_1 = L1MessageWithBlockNumber::arbitrary(&mut u).unwrap();
289-
let l1_message_2 = L1MessageWithBlockNumber::arbitrary(&mut u).unwrap();
297+
let l1_message_1 = L1MessageEnvelope::arbitrary(&mut u).unwrap();
298+
let l1_message_2 = L1MessageEnvelope::arbitrary(&mut u).unwrap();
290299

291300
// Insert the L1Messages into the database.
292301
db.insert_l1_message(l1_message_1.clone()).await.unwrap();

crates/database/db/src/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ pub enum DatabaseError {
1313
/// The block was not found in database.
1414
#[error("no block for id {0}")]
1515
BlockNotFound(BlockId),
16+
/// The L1 message was not found in database.
17+
#[error("L1 message at index [{0}] not found in database")]
18+
L1MessageNotFound(u64),
1619
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ pub struct Model {
2222
pub enum Relation {
2323
/// A one-to-many relation with the derived block table.
2424
#[sea_orm(has_many = "super::derived_block::Entity")]
25-
BatchToBlock,
25+
DerivedBlock,
2626
}
2727

2828
impl Related<super::derived_block::Entity> for Entity {
2929
fn to() -> RelationDef {
30-
Relation::BatchToBlock.def()
30+
Relation::DerivedBlock.def()
3131
}
3232
}
3333

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@ impl Related<super::batch_commit::Entity> for Entity {
4242
/// The active model behavior for the batch input model.
4343
impl ActiveModelBehavior for ActiveModel {}
4444

45-
impl From<(BatchInfo, BlockInfo)> for ActiveModel {
46-
fn from((batch_info, block_info): (BatchInfo, BlockInfo)) -> Self {
45+
impl From<(BlockInfo, BatchInfo)> for ActiveModel {
46+
fn from((block_info, batch_info): (BlockInfo, BatchInfo)) -> Self {
4747
Self {
48-
batch_index: ActiveValue::Set(
49-
batch_info.index.try_into().expect("index should fit in i64"),
50-
),
51-
batch_hash: ActiveValue::Set(batch_info.hash.to_vec()),
5248
block_number: ActiveValue::Set(
5349
block_info.number.try_into().expect("block number should fit in i64"),
5450
),
5551
block_hash: ActiveValue::Set(block_info.hash.to_vec()),
52+
batch_index: ActiveValue::Set(
53+
batch_info.index.try_into().expect("index should fit in i64"),
54+
),
55+
batch_hash: ActiveValue::Set(batch_info.hash.to_vec()),
5656
}
5757
}
5858
}

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use alloy_primitives::{Address, U256};
2-
use rollup_node_primitives::L1MessageWithBlockNumber;
1+
use alloy_primitives::{Address, B256, U256};
2+
use rollup_node_primitives::L1MessageEnvelope;
33
use scroll_alloy_consensus::TxL1Message;
44
use sea_orm::{entity::prelude::*, ActiveValue};
55

@@ -9,6 +9,7 @@ use sea_orm::{entity::prelude::*, ActiveValue};
99
pub struct Model {
1010
#[sea_orm(primary_key)]
1111
queue_index: i64,
12+
queue_hash: Option<Vec<u8>>,
1213
block_number: i64,
1314
gas_limit: String,
1415
to: Vec<u8>,
@@ -24,10 +25,11 @@ pub enum Relation {}
2425
/// The active model behavior for the L1 message model.
2526
impl ActiveModelBehavior for ActiveModel {}
2627

27-
impl From<L1MessageWithBlockNumber> for ActiveModel {
28-
fn from(value: L1MessageWithBlockNumber) -> Self {
28+
impl From<L1MessageEnvelope> for ActiveModel {
29+
fn from(value: L1MessageEnvelope) -> Self {
2930
Self {
3031
queue_index: ActiveValue::Set(value.transaction.queue_index as i64),
32+
queue_hash: ActiveValue::Set(value.queue_hash.map(|q| q.to_vec())),
3133
block_number: ActiveValue::Set(value.block_number as i64),
3234
gas_limit: ActiveValue::Set(value.transaction.gas_limit.to_string()),
3335
to: ActiveValue::Set(value.transaction.to.to_vec()),
@@ -38,10 +40,11 @@ impl From<L1MessageWithBlockNumber> for ActiveModel {
3840
}
3941
}
4042

41-
impl From<Model> for L1MessageWithBlockNumber {
43+
impl From<Model> for L1MessageEnvelope {
4244
fn from(value: Model) -> Self {
4345
Self {
4446
block_number: value.block_number as u64,
47+
queue_hash: value.queue_hash.map(|q| B256::from_slice(&q)),
4548
transaction: TxL1Message {
4649
queue_index: value.queue_index as u64,
4750
gas_limit: value.gas_limit.parse().expect("gas limit is valid"),

crates/database/db/src/operations.rs

+30-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::DatabaseConnectionProvider;
44
use alloy_eips::{BlockId, BlockNumberOrTag};
55
use alloy_primitives::B256;
66
use futures::{Stream, StreamExt};
7-
use rollup_node_primitives::{BatchCommitData, BatchInfo, BlockInfo, L1MessageWithBlockNumber};
7+
use rollup_node_primitives::{BatchCommitData, BatchInfo, BlockInfo, L1MessageEnvelope};
88
use scroll_alloy_rpc_types_engine::BlockDataHint;
99
use sea_orm::{
1010
ActiveModelTrait, ColumnTrait, Condition, DbErr, EntityTrait, QueryFilter, QueryOrder,
@@ -107,18 +107,15 @@ pub trait DatabaseOperations: DatabaseConnectionProvider {
107107
.map(|res| res.map(Into::into)))
108108
}
109109

110-
/// Insert an [`L1MessageWithBlockNumber`] into the database.
111-
async fn insert_l1_message(
112-
&self,
113-
l1_message: L1MessageWithBlockNumber,
114-
) -> Result<(), DatabaseError> {
110+
/// Insert an [`L1MessageEnvelope`] into the database.
111+
async fn insert_l1_message(&self, l1_message: L1MessageEnvelope) -> Result<(), DatabaseError> {
115112
tracing::trace!(target: "scroll::db", queue_index = l1_message.transaction.queue_index, "Inserting L1 message into database.");
116113
let l1_message: models::l1_message::ActiveModel = l1_message.into();
117114
l1_message.insert(self.get_connection()).await?;
118115
Ok(())
119116
}
120117

121-
/// Delete all [`L1MessageWithBlockNumber`]s with a block number greater than the provided block
118+
/// Delete all [`L1MessageEnvelope`]s with a block number greater than the provided block
122119
/// number.
123120
async fn delete_l1_messages_gt(&self, block_number: u64) -> Result<(), DatabaseError> {
124121
tracing::trace!(target: "scroll::db", block_number, "Deleting L1 messages greater than block number.");
@@ -129,22 +126,37 @@ pub trait DatabaseOperations: DatabaseConnectionProvider {
129126
.map(|_| ())?)
130127
}
131128

132-
/// Get a [`L1MessageWithBlockNumber`] from the database by its message queue index.
133-
async fn get_l1_message(
129+
/// Get a [`L1MessageEnvelope`] from the database by its message queue index.
130+
async fn get_l1_message_by_index(
134131
&self,
135132
queue_index: u64,
136-
) -> Result<Option<L1MessageWithBlockNumber>, DatabaseError> {
133+
) -> Result<Option<L1MessageEnvelope>, DatabaseError> {
137134
Ok(models::l1_message::Entity::find_by_id(queue_index as i64)
138135
.one(self.get_connection())
139136
.await
140137
.map(|x| x.map(Into::into))?)
141138
}
142139

143-
/// Gets an iterator over all [`L1MessageWithBlockNumber`]s in the database.
140+
/// Get a [`L1MessageEnvelope`] from the database by its message queue hash.
141+
async fn get_l1_message_by_hash(
142+
&self,
143+
queue_hash: B256,
144+
) -> Result<Option<L1MessageEnvelope>, DatabaseError> {
145+
Ok(models::l1_message::Entity::find()
146+
.filter(
147+
Condition::all()
148+
.add(models::l1_message::Column::QueueHash.is_not_null())
149+
.add(models::l1_message::Column::QueueHash.eq(queue_hash.to_vec())),
150+
)
151+
.one(self.get_connection())
152+
.await
153+
.map(|x| x.map(Into::into))?)
154+
}
155+
156+
/// Gets an iterator over all [`L1MessageEnvelope`]s in the database.
144157
async fn get_l1_messages<'a>(
145158
&'a self,
146-
) -> Result<impl Stream<Item = Result<L1MessageWithBlockNumber, DbErr>> + 'a, DatabaseError>
147-
{
159+
) -> Result<impl Stream<Item = Result<L1MessageEnvelope, DbErr>> + 'a, DatabaseError> {
148160
Ok(models::l1_message::Entity::find()
149161
.stream(self.get_connection())
150162
.await?
@@ -170,21 +182,21 @@ pub trait DatabaseOperations: DatabaseConnectionProvider {
170182
.map(|x| x.map(Into::into))?)
171183
}
172184

173-
/// Insert a new batch to block line in the database.
174-
async fn insert_batch_to_block(
185+
/// Insert a new derived block line in the database.
186+
async fn insert_derived_block(
175187
&self,
176-
batch_info: BatchInfo,
177188
block_info: BlockInfo,
189+
batch_info: BatchInfo,
178190
) -> Result<(), DatabaseError> {
179191
tracing::trace!(
180192
target: "scroll::db",
181193
batch_hash = ?batch_info.hash,
182194
batch_index = batch_info.index,
183195
block_number = block_info.number,
184196
block_hash = ?block_info.hash,
185-
"Inserting batch to block into database."
197+
"Inserting derived block into database."
186198
);
187-
let derived_block: models::derived_block::ActiveModel = (batch_info, block_info).into();
199+
let derived_block: models::derived_block::ActiveModel = (block_info, batch_info).into();
188200
derived_block.insert(self.get_connection()).await?;
189201

190202
Ok(())

crates/database/migration/src/m20250304_125946_add_l1_msg_table.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ impl MigrationTrait for Migration {
1212
.table(L1Message::Table)
1313
.if_not_exists()
1414
.col(pk_auto(L1Message::QueueIndex))
15+
.col(binary_len_null(L1Message::QueueHash, 32))
1516
.col(unsigned(L1Message::BlockNumber))
1617
.col(text(L1Message::GasLimit))
1718
.col(binary_len(L1Message::To, 20))
@@ -32,6 +33,7 @@ impl MigrationTrait for Migration {
3233
enum L1Message {
3334
Table,
3435
QueueIndex,
36+
QueueHash,
3537
BlockNumber,
3638
GasLimit,
3739
To,

0 commit comments

Comments
 (0)