Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(derive): Inline Blob Validation #175

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 162 additions & 125 deletions crates/derive/src/online/blob_provider.rs

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions crates/derive/src/online/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,3 @@ pub use alloy_providers::{AlloyChainProvider, AlloyL2ChainProvider};

mod blob_provider;
pub use blob_provider::{OnlineBlobProvider, SimpleSlotDerivation};

mod utils;
pub(crate) use utils::blobs_from_sidecars;
140 changes: 0 additions & 140 deletions crates/derive/src/online/utils.rs

This file was deleted.

6 changes: 5 additions & 1 deletion crates/derive/src/sources/blobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ where
return Ok(());
}

let blobs = self.blob_fetcher.get_blobs(&self.block_ref, blob_hashes).await?;
let blobs =
self.blob_fetcher.get_blobs(&self.block_ref, &blob_hashes).await.map_err(|e| {
warn!("Failed to fetch blobs: {e}");
anyhow::anyhow!("Failed to fetch blobs: {e}")
})?;

// Fill the blob pointers.
let mut blob_index = 0;
Expand Down
6 changes: 3 additions & 3 deletions crates/derive/src/traits/data_sources.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Contains traits that describe the functionality of various data sources used in the derivation
//! pipeline's stages.

use crate::types::{Blob, BlockInfo, IndexedBlobHash, StageResult};
use crate::types::{Blob, BlobProviderError, BlockInfo, IndexedBlobHash, StageResult};
use alloc::{boxed::Box, fmt::Debug, vec::Vec};
use alloy_primitives::{Address, Bytes};
use anyhow::Result;
Expand All @@ -14,8 +14,8 @@ pub trait BlobProvider {
async fn get_blobs(
&mut self,
block_ref: &BlockInfo,
blob_hashes: Vec<IndexedBlobHash>,
) -> Result<Vec<Blob>>;
blob_hashes: &[IndexedBlobHash],
) -> Result<Vec<Blob>, BlobProviderError>;
}

/// Describes the functionality of a data source that can provide data availability information.
Expand Down
41 changes: 41 additions & 0 deletions crates/derive/src/types/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,47 @@ impl Display for StageError {
}
}

/// An error returned by the [BlobProviderError].
#[derive(Debug)]
pub enum BlobProviderError {
/// The number of specified blob hashes did not match the number of returned sidecars.
SidecarLengthMismatch(usize, usize),
/// Slot derivation error.
Slot(anyhow::Error),
/// A custom [anyhow::Error] occurred.
Custom(anyhow::Error),
}

impl PartialEq for BlobProviderError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::SidecarLengthMismatch(a, b), Self::SidecarLengthMismatch(c, d)) => {
a == c && b == d
}
(Self::Slot(_), Self::Slot(_)) | (Self::Custom(_), Self::Custom(_)) => true,
_ => false,
}
}
}

impl Display for BlobProviderError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::SidecarLengthMismatch(a, b) => write!(f, "expected {} sidecars but got {}", a, b),
Self::Slot(e) => {
write!(f, "Slot Derivation Error: {}", e)
}
Self::Custom(err) => write!(f, "{}", err),
}
}
}

impl From<anyhow::Error> for BlobProviderError {
fn from(err: anyhow::Error) -> Self {
Self::Custom(err)
}
}

/// A reset error
#[derive(Debug)]
pub enum ResetError {
Expand Down
37 changes: 37 additions & 0 deletions crates/derive/src/types/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
use crate::types::Blob;
use alloc::{string::String, vec::Vec};
use alloy_primitives::FixedBytes;

#[cfg(feature = "online")]
use crate::types::IndexedBlobHash;
#[cfg(feature = "online")]
use alloy_primitives::B256;
#[cfg(feature = "online")]
use c_kzg::{Bytes48, KzgProof, KzgSettings};
#[cfg(feature = "online")]
Expand Down Expand Up @@ -37,6 +42,7 @@ where
{
String::deserialize(de)?.parse().map_err(serde::de::Error::custom)
}

/// A blob sidecar.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -55,6 +61,37 @@ pub struct BlobSidecar {
}

impl BlobSidecar {
/// Verify the blob sidecar against it's [IndexedBlobHash].
#[cfg(feature = "online")]
pub fn verify_blob(&self, hash: &IndexedBlobHash) -> anyhow::Result<()> {
if self.index as usize != hash.index {
return Err(anyhow::anyhow!(
"invalid sidecar ordering, blob hash index {} does not match sidecar index {}",
hash.index,
self.index
));
}

// Ensure the blob's kzg commitment hashes to the expected value.
if self.to_kzg_versioned_hash() != hash.hash {
return Err(anyhow::anyhow!(
"expected hash {} for blob at index {} but got {}",
hash.hash,
hash.index,
B256::from(self.to_kzg_versioned_hash())
));
}

// Confirm blob data is valid by verifying its proof against the commitment
match self.verify_blob_kzg_proof() {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow::anyhow!("blob at index {} failed verification", self.index)),
Err(e) => {
Err(anyhow::anyhow!("blob at index {} failed verification: {}", self.index, e))
}
}
}

/// Verifies the blob kzg proof.
#[cfg(feature = "online")]
pub fn verify_blob_kzg_proof(&self) -> anyhow::Result<bool> {
Expand Down
Loading