Skip to content

Commit 6ee03c2

Browse files
authored
feat: update block data hint database models (#180)
1 parent d427361 commit 6ee03c2

File tree

24 files changed

+2395
-2387
lines changed

24 files changed

+2395
-2387
lines changed

Cargo.lock

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

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ test:
7070
--all-features \
7171
--no-fail-fast
7272

73+
# Used to update the mainnet-sample.sql data. Provide the path to the sqlite database that should be read from
74+
# using `DB_PATH`.
75+
.PHONY: test-data
76+
export-sample-test-data:
77+
sqlite3 "$(DB_PATH)" <<EOF > mainnet-sample.sql
78+
.headers on
79+
.mode insert block_data
80+
SELECT * FROM block_data LIMIT 2000;
81+
EOF
82+
7383
.PHONY: docs
7484
docs:
7585
cargo docs --document-private-items

crates/codec/src/decoding/blob.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl<'a> Iterator for BlobSliceIter<'a> {
2020
type Item = &'a u8;
2121

2222
fn next(&mut self) -> Option<Self::Item> {
23-
if self.count % 32 == 0 {
23+
if self.count.is_multiple_of(32) {
2424
let _ = self.iterator.next();
2525
self.count += 1;
2626
}

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use alloy_primitives::U256;
1+
use alloy_primitives::{Address, B256, U256};
22
use scroll_alloy_rpc_types_engine::BlockDataHint;
33
use sea_orm::entity::prelude::*;
44

@@ -8,9 +8,11 @@ use sea_orm::entity::prelude::*;
88
pub struct Model {
99
#[sea_orm(primary_key)]
1010
number: i64,
11-
extra_data: Vec<u8>,
12-
state_root: Vec<u8>,
13-
difficulty: Vec<u8>,
11+
extra_data: Option<Vec<u8>>,
12+
state_root: Option<Vec<u8>>,
13+
coinbase: Option<Vec<u8>>,
14+
nonce: Option<String>,
15+
difficulty: Option<i8>,
1416
}
1517

1618
/// The relation for the extra data model.
@@ -23,8 +25,13 @@ impl ActiveModelBehavior for ActiveModel {}
2325
impl From<Model> for BlockDataHint {
2426
fn from(value: Model) -> Self {
2527
Self {
26-
extra_data: value.extra_data.into(),
27-
difficulty: U256::from_be_slice(value.difficulty.as_ref()),
28+
extra_data: value.extra_data.map(Into::into),
29+
state_root: value.state_root.map(|s| B256::from_slice(&s)),
30+
coinbase: value.coinbase.as_deref().map(Address::from_slice),
31+
nonce: value.nonce.map(|n| {
32+
u64::from_str_radix(&n, 16).expect("nonce stored as hex string in database")
33+
}),
34+
difficulty: value.difficulty.map(U256::from),
2835
}
2936
}
3037
}

crates/database/migration/src/m20250408_132123_add_header_metadata.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ impl MigrationTrait for Migration {
1313
.table(BlockData::Table)
1414
.if_not_exists()
1515
.col(big_unsigned(BlockData::Number).primary_key())
16-
.col(binary(BlockData::ExtraData))
17-
.col(binary_len(BlockData::StateRoot, 32))
18-
.col(binary_len(BlockData::Difficulty, 32))
16+
.col(binary_null(BlockData::ExtraData))
17+
.col(binary_len_null(BlockData::StateRoot, 32))
18+
.col(binary_len_null(BlockData::Coinbase, 20))
19+
.col(text_null(BlockData::Nonce))
20+
.col(tiny_integer_null(BlockData::Difficulty))
1921
.to_owned(),
2022
)
2123
.await
@@ -32,5 +34,7 @@ enum BlockData {
3234
Number,
3335
ExtraData,
3436
StateRoot,
37+
Coinbase,
38+
Nonce,
3539
Difficulty,
3640
}

crates/database/migration/src/m20250408_150338_load_header_metadata.rs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{migration_info::DataSource, MigrationInfo};
22
use std::{collections::HashMap, time::Duration};
33

4-
use alloy_primitives::{bytes::Buf, B256};
4+
use alloy_primitives::{bytes::Buf, Address, B256};
55
use eyre::{bail, eyre};
66
use futures::{stream::FuturesUnordered, StreamExt};
77
use indicatif::{ProgressBar, ProgressFinish, ProgressState, ProgressStyle};
@@ -70,19 +70,23 @@ impl<MI: MigrationInfo + Send + Sync> MigrationTrait for Migration<MI> {
7070
pub struct Model {
7171
#[sea_orm(primary_key)]
7272
number: i64,
73-
extra_data: Vec<u8>,
74-
state_root: Vec<u8>,
75-
difficulty: i8,
73+
extra_data: Option<Vec<u8>>,
74+
state_root: Option<Vec<u8>>,
75+
coinbase: Option<Vec<u8>>,
76+
nonce: Option<String>,
77+
difficulty: Option<i8>,
7678
}
7779
impl ActiveModelBehavior for ActiveModel {}
7880

7981
impl From<(i64, HeaderMetadata)> for ActiveModel {
8082
fn from((bn, header): (i64, HeaderMetadata)) -> Self {
8183
Self {
8284
number: ActiveValue::Set(bn),
83-
extra_data: ActiveValue::Set(header.extra_data),
84-
state_root: ActiveValue::Set(header.state_root.to_vec()),
85-
difficulty: ActiveValue::Set(header.difficulty as i8),
85+
extra_data: ActiveValue::Set(Some(header.extra_data)),
86+
state_root: ActiveValue::Set(Some(header.state_root.to_vec())),
87+
coinbase: ActiveValue::Set(header.coinbase.map(|c| c.to_vec())),
88+
nonce: ActiveValue::Set(header.nonce.map(|x| format!("{x:x}"))),
89+
difficulty: ActiveValue::Set(Some(header.difficulty as i8)),
8690
}
8791
}
8892
}
@@ -107,7 +111,8 @@ async fn download(url: &str) -> eyre::Result<Vec<u8>> {
107111
if total_size == 0 {
108112
bail!("empty file");
109113
}
110-
let iterations = total_size / CHUNK_SIZE + if total_size % CHUNK_SIZE != 0 { 1 } else { 0 };
114+
let iterations =
115+
total_size / CHUNK_SIZE + if !total_size.is_multiple_of(CHUNK_SIZE) { 1 } else { 0 };
111116

112117
// create a progress bar.
113118
let pb = ProgressBar::new(total_size).
@@ -213,8 +218,8 @@ fn decode_to_headers(data: Vec<u8>) -> eyre::Result<Vec<HeaderMetadata>> {
213218

214219
// decode all available data.
215220
let mut headers = Vec::with_capacity(data_buf.len() / HEADER_LOWER_SIZE_LIMIT);
216-
while let Some(data) = decoder.next(data_buf) {
217-
headers.push(data);
221+
while !data_buf.is_empty() {
222+
headers.push(decoder.next(data_buf).map_err(|err| DbErr::Custom(err.to_string()))?);
218223
}
219224

220225
Ok(headers)
@@ -225,6 +230,8 @@ fn decode_to_headers(data: Vec<u8>) -> eyre::Result<Vec<HeaderMetadata>> {
225230
struct HeaderMetadata {
226231
extra_data: Vec<u8>,
227232
state_root: Vec<u8>,
233+
coinbase: Option<Vec<u8>>,
234+
nonce: Option<u64>,
228235
difficulty: u8,
229236
}
230237

@@ -260,33 +267,56 @@ impl MetadataDecoder {
260267
}
261268

262269
/// Decodes the next header metadata from the buffer, advancing it.
263-
fn next(&self, buf: &mut &[u8]) -> Option<HeaderMetadata> {
270+
fn next(&self, buf: &mut &[u8]) -> eyre::Result<HeaderMetadata> {
264271
// sanity check.
265272
if buf.len() < 2 {
266-
return None
273+
bail!("header buffer too small to read seal flag and vanity index");
267274
}
268275

269276
// get flag and vanity index.
270277
let flag = buf[0];
271278
let vanity_index = buf[1];
272279

280+
let has_coinbase = (flag & 0b00010000) != 0;
281+
let has_nonce = (flag & 0b00100000) != 0;
273282
let difficulty = if flag & 0b01000000 == 0 { 2 } else { 1 };
274283
let seal_length = if flag & 0b10000000 == 0 { 65 } else { 85 };
275-
let vanity = self.vanity.get(&vanity_index)?;
284+
let vanity = self.vanity.get(&vanity_index).ok_or(eyre!("vanity not found"))?;
276285

277-
if buf.len() < B256::len_bytes() + seal_length + 2 {
278-
return None
286+
// flag + vanity index + state root + coinbase + nonce + seal
287+
let total_expected_size = 2 * size_of::<u8>() +
288+
B256::len_bytes() +
289+
Address::len_bytes() * has_coinbase as usize +
290+
size_of::<u64>() * has_nonce as usize +
291+
seal_length;
292+
293+
if buf.len() < total_expected_size {
294+
bail!("header buffer too small: got {}, expected {}", buf.len(), total_expected_size);
279295
}
280296
buf.advance(2);
281297

282298
let state_root = buf[..B256::len_bytes()].to_vec();
283299
buf.advance(B256::len_bytes());
284300

301+
let mut coinbase = None;
302+
if has_coinbase {
303+
coinbase = Some(buf[..Address::len_bytes()].to_vec());
304+
buf.advance(Address::len_bytes());
305+
}
306+
307+
let mut nonce = None;
308+
if has_nonce {
309+
nonce = Some(u64::from_be_bytes(
310+
buf[..size_of::<u64>()].try_into().expect("32 bytes slice"),
311+
));
312+
buf.advance(size_of::<u64>());
313+
}
314+
285315
let seal = &buf[..seal_length];
286316
let extra_data = [vanity, seal].concat();
287317
buf.advance(seal_length);
288318

289-
Some(HeaderMetadata { extra_data, state_root, difficulty })
319+
Ok(HeaderMetadata { extra_data, state_root, coinbase, nonce, difficulty })
290320
}
291321
}
292322

@@ -316,7 +346,13 @@ mod tests {
316346
let header_data = bytes!("c00020695989e9038823e35f0e88fbc44659ffdbfa1fe89fbeb2689b43f15fa64cb548c3f81f3d998b6652900e1c3183736c238fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
317347
let header = decoder.next(&mut header_data.as_ref()).unwrap();
318348

319-
let expected_header = HeaderMetadata{ extra_data: bytes!("0x000000000000000000000000000000000000000000000000000000000000000048c3f81f3d998b6652900e1c3183736c238fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(), state_root: b256!("20695989e9038823e35f0e88fbc44659ffdbfa1fe89fbeb2689b43f15fa64cb5").to_vec(), difficulty: 1 };
349+
let expected_header = HeaderMetadata {
350+
extra_data: bytes!("0x000000000000000000000000000000000000000000000000000000000000000048c3f81f3d998b6652900e1c3183736c238fe4290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").to_vec(),
351+
state_root: b256!("20695989e9038823e35f0e88fbc44659ffdbfa1fe89fbeb2689b43f15fa64cb5").to_vec(),
352+
coinbase: None,
353+
nonce: None,
354+
difficulty: 1,
355+
};
320356
assert_eq!(header, expected_header)
321357
}
322358
}

crates/database/migration/src/migration_info.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl MigrationInfo for ScrollMainnetMigrationInfo {
3131
}
3232

3333
fn data_hash() -> Option<B256> {
34-
Some(b256!("9062e2fa1200dca63bee1d18d429572f134f5f0c98cb4852f62fc394e33cf6e6"))
34+
Some(b256!("fa2746026ec9590e37e495cb20046e20a38fd0e7099abd2012640dddf6c88b25"))
3535
}
3636
}
3737

@@ -58,6 +58,6 @@ impl MigrationInfo for ScrollSepoliaMigrationInfo {
5858
}
5959

6060
fn data_hash() -> Option<B256> {
61-
Some(b256!("3629f5e53250a526ffc46806c4d74b9c52c9209a6d45ecdfebdef5d596bb3f40"))
61+
Some(b256!("a02354c12ca0f918bf4768255af9ed13c137db7e56252348f304b17bb4088924"))
6262
}
6363
}

0 commit comments

Comments
 (0)