Skip to content

Commit 7d1ae6e

Browse files
committed
Add blob database verifier
1 parent 4559e2a commit 7d1ae6e

File tree

6 files changed

+196
-56
lines changed

6 files changed

+196
-56
lines changed

beacon_node/http_api/src/lib.rs

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ use std::path::PathBuf;
7979
use std::pin::Pin;
8080
use std::str::FromStr;
8181
use std::sync::Arc;
82+
use store::BlobSidecarListFromRoot;
8283
use sysinfo::{System, SystemExt};
8384
use system_health::{observe_nat, observe_system_health_bn};
8485
use task_spawner::{Priority, TaskSpawner};
@@ -4747,7 +4748,6 @@ pub fn serve<T: BeaconChainTypes>(
47474748
task_spawner: TaskSpawner<T::EthSpec>,
47484749
chain: Arc<BeaconChain<T>>| {
47494750
task_spawner.spawn_async_with_rejection(Priority::P1, async move {
4750-
let mut results = Vec::new();
47514751
let deneb_start_slot =
47524752
if let Some(deneb_fork_epoch) = chain.spec.deneb_fork_epoch {
47534753
deneb_fork_epoch.start_slot(T::EthSpec::slots_per_epoch())
@@ -4756,51 +4756,77 @@ pub fn serve<T: BeaconChainTypes>(
47564756
"No Deneb fork scheduled".to_string(),
47574757
));
47584758
};
4759-
let start_slot = query.start_slot.unwrap_or(deneb_start_slot);
4759+
let start_slot = query.start_slot;
4760+
47604761
if start_slot < deneb_start_slot {
47614762
return Err(warp_utils::reject::custom_bad_request(format!(
47624763
"start_slot ({}) must be >= deneb fork slot ({})",
47634764
start_slot, deneb_start_slot
47644765
)));
47654766
}
4766-
// Maybe use chain.canonical_head.cached_head().head_slot()??
4767-
let Ok(current_head_slot) = chain.slot() else {
4768-
return Err(warp_utils::reject::custom_bad_request(
4769-
"Failed to get current head slot".to_string(),
4770-
));
4771-
};
47724767

4773-
let end_slot = query.end_slot.unwrap_or(current_head_slot);
4768+
let end_slot = query.end_slot;
47744769
if end_slot < start_slot {
47754770
return Err(warp_utils::reject::custom_bad_request(format!(
47764771
"end_slot ({}) must be >= start_slot ({})",
47774772
end_slot, start_slot
47784773
)));
47794774
}
47804775

4776+
let verify = query.verify.unwrap_or(true);
4777+
4778+
let mut blob_count = 0;
4779+
let mut blobs_missing: Vec<u64> = Vec::new();
4780+
let mut blobs_invalid: Vec<u64> = Vec::new();
4781+
47814782
for slot in start_slot.as_u64()..=end_slot.as_u64() {
47824783
if let Ok((root, _, _)) = BlockId::from_slot(Slot::from(slot)).root(&chain)
47834784
{
4784-
if let Ok(blob_list_res) = chain.store.get_blobs(&root) {
4785-
if let Some(blob_list) = blob_list_res.blobs() {
4786-
if let Err(_e) =
4787-
verify_kzg_for_blob_list(blob_list.iter(), &chain.kzg)
4788-
{
4789-
results.push(BlobsVerificationData {
4790-
block_root: root,
4791-
slot: slot.into(),
4792-
blobs_exist: true,
4793-
blobs_stored: false,
4794-
blobs_verified: true,
4795-
});
4785+
match chain.store.get_blobs(&root) {
4786+
Ok(blob_list) => {
4787+
match blob_list {
4788+
BlobSidecarListFromRoot::NoBlobs => {
4789+
// This means that no blobs exist for this slot.
4790+
continue;
4791+
}
4792+
BlobSidecarListFromRoot::NoRoot => {
4793+
// This means that there are blobs missing for this slot.
4794+
blobs_missing.push(slot);
4795+
}
4796+
BlobSidecarListFromRoot::Blobs(blob_list) => {
4797+
blob_count += blob_list.len();
4798+
// Optionally verify each blob_list.
4799+
if verify
4800+
&& verify_kzg_for_blob_list(
4801+
blob_list.iter(),
4802+
&chain.kzg,
4803+
)
4804+
.is_err()
4805+
{
4806+
blobs_invalid.push(slot);
4807+
}
4808+
}
47964809
}
47974810
}
4811+
Err(_) => {
4812+
// An error here means that we could not decode the blob list.
4813+
// This likely means a corrupted database.
4814+
blobs_invalid.push(slot);
4815+
}
47984816
}
47994817
}
4818+
// An Err here means the block does not exist. This is fine assuming the node is synced.
48004819
}
4820+
48014821
Ok::<_, warp::reject::Rejection>(
4802-
warp::reply::json(&api_types::GenericResponse::from(results))
4803-
.into_response(),
4822+
warp::reply::json(&api_types::GenericResponse::from(
4823+
BlobsVerificationData {
4824+
blob_count,
4825+
blobs_missing,
4826+
blobs_invalid,
4827+
},
4828+
))
4829+
.into_response(),
48044830
)
48054831
})
48064832
},

common/eth2/src/lighthouse.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,13 @@ impl BeaconNodeHttpClient {
395395
self.post_generic_with_ssz_body(path, blobs, None).await
396396
}
397397

398-
/// `POST lighthouse/database/verify_blobs`
398+
/// `GET lighthouse/database/verify_blobs`
399399
pub async fn get_lighthouse_database_verify_blobs(
400400
&self,
401-
start_slot: Option<Slot>,
402-
end_slot: Option<Slot>,
403-
) -> Result<Vec<BlobsVerificationData>, Error> {
401+
start_slot: Slot,
402+
end_slot: Slot,
403+
verify: Option<bool>,
404+
) -> Result<BlobsVerificationData, Error> {
404405
let mut path = self.server.full.clone();
405406

406407
path.path_segments_mut()
@@ -409,14 +410,15 @@ impl BeaconNodeHttpClient {
409410
.push("database")
410411
.push("verify_blobs");
411412

412-
if let Some(start_slot) = start_slot {
413-
path.query_pairs_mut()
414-
.append_pair("start_slot", &start_slot.to_string());
415-
}
413+
path.query_pairs_mut()
414+
.append_pair("start_slot", &start_slot.to_string());
416415

417-
if let Some(end_slot) = end_slot {
416+
path.query_pairs_mut()
417+
.append_pair("end_slot", &end_slot.to_string());
418+
419+
if let Some(verify) = verify {
418420
path.query_pairs_mut()
419-
.append_pair("end_slot", &end_slot.to_string());
421+
.append_pair("verify", &verify.to_string());
420422
}
421423

422424
self.get(path).await
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
use serde::{Deserialize, Serialize};
2-
use types::{Hash256, Slot};
32

43
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
54
pub struct BlobsVerificationData {
6-
pub block_root: Hash256,
7-
pub slot: Slot,
8-
pub blobs_exist: bool,
9-
pub blobs_stored: bool,
10-
pub blobs_verified: bool,
5+
pub blob_count: usize,
6+
pub blobs_missing: Vec<u64>,
7+
pub blobs_invalid: Vec<u64>,
118
}

common/eth2/src/types.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -821,8 +821,9 @@ pub struct ImportBlobsQuery {
821821

822822
#[derive(Debug, Deserialize)]
823823
pub struct VerifyBlobsQuery {
824-
pub start_slot: Option<Slot>,
825-
pub end_slot: Option<Slot>,
824+
pub start_slot: Slot,
825+
pub end_slot: Slot,
826+
pub verify: Option<bool>,
826827
}
827828

828829
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]

database_manager/src/blobs_manager/cli.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,38 @@ pub struct VerifyBlobs {
2020
display_order = 0
2121
)]
2222
pub beacon_node: Option<String>,
23+
2324
#[clap(
2425
long,
2526
value_name = "SLOT",
2627
help = "The slot at which to begin blob verification. Defaults to the Deneb start slot",
2728
display_order = 0
2829
)]
2930
pub start_slot: Option<u64>,
31+
3032
#[clap(
3133
long,
3234
value_name = "SLOT",
3335
help = "The slot at which to stop blob verification. Defaults to the latest slot",
3436
display_order = 0
3537
)]
3638
pub end_slot: Option<u64>,
39+
3740
#[clap(
3841
long,
39-
help = "Perform verification even if the beacon node is not synced",
42+
help = "Perform checks even if the beacon node is not synced",
4043
display_order = 0,
4144
default_value = "false"
4245
)]
4346
pub allow_unsynced: bool,
47+
48+
#[clap(
49+
long,
50+
help = "Skip KZG verification and only perform blob availability checks",
51+
display_order = 0,
52+
default_value = "false"
53+
)]
54+
pub skip_verification: bool,
4455
}
4556

4657
#[derive(Parser, Clone, Deserialize, Serialize, Debug)]
@@ -68,6 +79,7 @@ pub struct ImportBlobs {
6879
default_value = "false"
6980
)]
7081
pub skip_verification: bool,
82+
7183
#[clap(
7284
long,
7385
help = "Attempt import even if the beacon node is not synced",
@@ -95,6 +107,7 @@ pub struct ExportBlobs {
95107
display_order = 0
96108
)]
97109
pub output_dir: PathBuf,
110+
98111
#[clap(
99112
long,
100113
value_name = "SLOT",

0 commit comments

Comments
 (0)