use crate::errors::BlobDecodingError;
use alloc::{boxed::Box, vec};
use alloy_eips::eip4844::{Blob, BYTES_PER_BLOB, VERSIONED_HASH_VERSION_KZG};
use alloy_primitives::Bytes;
pub(crate) const BLOB_ENCODING_VERSION: u8 = 0;
pub(crate) const BLOB_MAX_DATA_SIZE: usize = (4 * 31 + 3) * 1024 - 4; pub(crate) const BLOB_ENCODING_ROUNDS: usize = 1024;
#[derive(Default, Clone, Debug)]
pub struct BlobData {
pub(crate) data: Option<Bytes>,
pub(crate) calldata: Option<Bytes>,
}
impl BlobData {
pub(crate) fn decode(&self) -> Result<Bytes, BlobDecodingError> {
let data = self.data.as_ref().ok_or(BlobDecodingError::MissingData)?;
if data[VERSIONED_HASH_VERSION_KZG as usize] != BLOB_ENCODING_VERSION {
return Err(BlobDecodingError::InvalidEncodingVersion);
}
let length = u32::from_be_bytes([0, data[2], data[3], data[4]]) as usize;
if length > BLOB_MAX_DATA_SIZE {
return Err(BlobDecodingError::InvalidLength);
}
let mut output = vec![0u8; BLOB_MAX_DATA_SIZE];
output[0..27].copy_from_slice(&data[5..32]);
let mut output_pos = 28;
let mut input_pos = 32;
let mut encoded_byte = [0u8; 4];
encoded_byte[0] = data[0];
for b in encoded_byte.iter_mut().skip(1) {
let (enc, opos, ipos) =
self.decode_field_element(output_pos, input_pos, &mut output)?;
*b = enc;
output_pos = opos;
input_pos = ipos;
}
output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output);
for _ in 1..BLOB_ENCODING_ROUNDS {
if output_pos >= length {
break;
}
for d in &mut encoded_byte {
let (enc, opos, ipos) =
self.decode_field_element(output_pos, input_pos, &mut output)?;
*d = enc;
output_pos = opos;
input_pos = ipos;
}
output_pos = self.reassemble_bytes(output_pos, &encoded_byte, &mut output);
}
for o in output.iter().skip(length) {
if *o != 0u8 {
return Err(BlobDecodingError::InvalidFieldElement);
}
}
output.truncate(length);
for i in input_pos..BYTES_PER_BLOB {
if data[i] != 0 {
return Err(BlobDecodingError::InvalidFieldElement);
}
}
Ok(Bytes::from(output))
}
pub(crate) fn decode_field_element(
&self,
output_pos: usize,
input_pos: usize,
output: &mut [u8],
) -> Result<(u8, usize, usize), BlobDecodingError> {
let Some(data) = self.data.as_ref() else {
return Err(BlobDecodingError::MissingData);
};
if data[input_pos] & 0b1100_0000 != 0 {
return Err(BlobDecodingError::InvalidFieldElement);
}
output[output_pos..output_pos + 31].copy_from_slice(&data[input_pos + 1..input_pos + 32]);
Ok((data[input_pos], output_pos + 32, input_pos + 32))
}
pub(crate) fn reassemble_bytes(
&self,
mut output_pos: usize,
encoded_byte: &[u8],
output: &mut [u8],
) -> usize {
output_pos -= 1;
let x = (encoded_byte[0] & 0b0011_1111) | ((encoded_byte[1] & 0b0011_0000) << 2);
let y = (encoded_byte[1] & 0b0000_1111) | ((encoded_byte[3] & 0b0000_1111) << 4);
let z = (encoded_byte[2] & 0b0011_1111) | ((encoded_byte[3] & 0b0011_0000) << 2);
output[output_pos - 32] = z;
output[output_pos - (32 * 2)] = y;
output[output_pos - (32 * 3)] = x;
output_pos
}
pub(crate) fn fill(
&mut self,
blobs: &[Box<Blob>],
index: usize,
) -> Result<bool, BlobDecodingError> {
if self.calldata.is_some() {
return Ok(false);
}
if index >= blobs.len() {
return Err(BlobDecodingError::InvalidLength);
}
if blobs[index].is_empty() || blobs[index].is_zero() {
return Err(BlobDecodingError::MissingData);
}
self.data = Some(Bytes::from(*blobs[index]));
Ok(true)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reassemble_bytes() {
let blob_data = BlobData::default();
let mut output = vec![0u8; 128];
let encoded_byte = [0x00, 0x00, 0x00, 0x00];
let output_pos = blob_data.reassemble_bytes(127, &encoded_byte, &mut output);
assert_eq!(output_pos, 126);
assert_eq!(output, vec![0u8; 128]);
}
#[test]
fn test_cannot_fill_empty_calldata() {
let mut blob_data = BlobData { calldata: Some(Bytes::new()), ..Default::default() };
let blobs = vec![Box::new(Blob::with_last_byte(1u8))];
assert_eq!(blob_data.fill(&blobs, 0), Ok(false));
}
#[test]
fn test_fill_oob_index() {
let mut blob_data = BlobData::default();
let blobs = vec![Box::new(Blob::with_last_byte(1u8))];
assert_eq!(blob_data.fill(&blobs, 1), Err(BlobDecodingError::InvalidLength));
}
#[test]
fn test_fill_zero_blob() {
let mut blob_data = BlobData::default();
let blobs = vec![Box::new(Blob::ZERO)];
assert_eq!(blob_data.fill(&blobs, 0), Err(BlobDecodingError::MissingData));
}
#[test]
fn test_fill_blob() {
let mut blob_data = BlobData::default();
let blobs = vec![Box::new(Blob::with_last_byte(1u8))];
assert_eq!(blob_data.fill(&blobs, 0), Ok(true));
let expected = Bytes::from([&[0u8; 131071][..], &[1u8]].concat());
assert_eq!(blob_data.data, Some(expected));
}
#[test]
fn test_blob_data_decode_missing_data() {
let blob_data = BlobData::default();
assert_eq!(blob_data.decode(), Err(BlobDecodingError::MissingData));
}
#[test]
fn test_blob_data_decode_invalid_encoding_version() {
let blob_data = BlobData { data: Some(Bytes::from(vec![1u8; 32])), ..Default::default() };
assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidEncodingVersion));
}
#[test]
fn test_blob_data_decode_invalid_length() {
let mut data = vec![0u8; 32];
data[VERSIONED_HASH_VERSION_KZG as usize] = BLOB_ENCODING_VERSION;
data[2] = 0xFF;
data[3] = 0xFF;
data[4] = 0xFF;
let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() };
assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidLength));
}
#[test]
fn test_blob_data_decode() {
let mut data = vec![0u8; alloy_eips::eip4844::BYTES_PER_BLOB];
data[VERSIONED_HASH_VERSION_KZG as usize] = BLOB_ENCODING_VERSION;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x01;
let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() };
assert_eq!(blob_data.decode(), Ok(Bytes::from(vec![0u8; 1])));
}
#[test]
fn test_blob_data_decode_invalid_field_element() {
let mut data = vec![0u8; alloy_eips::eip4844::BYTES_PER_BLOB + 10];
data[VERSIONED_HASH_VERSION_KZG as usize] = BLOB_ENCODING_VERSION;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x01;
data[33] = 0x01;
let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() };
assert_eq!(blob_data.decode(), Err(BlobDecodingError::InvalidFieldElement));
}
#[test]
fn test_decode_field_element_missing_data() {
let blob_data = BlobData::default();
assert_eq!(
blob_data.decode_field_element(0, 0, &mut []),
Err(BlobDecodingError::MissingData)
);
}
#[test]
fn test_decode_field_element_invalid_field_element() {
let mut data = vec![0u8; 32];
data[0] = 0b1100_0000;
let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() };
assert_eq!(
blob_data.decode_field_element(0, 0, &mut []),
Err(BlobDecodingError::InvalidFieldElement)
);
}
#[test]
fn test_decode_field_element() {
let mut data = vec![0u8; 32];
data[1..32].copy_from_slice(&[1u8; 31]);
let blob_data = BlobData { data: Some(Bytes::from(data)), ..Default::default() };
let mut output = vec![0u8; 31];
assert_eq!(blob_data.decode_field_element(0, 0, &mut output), Ok((0, 32, 32)));
assert_eq!(output, vec![1u8; 31]);
}
}