celestia-types 1.0.0

Core types, traits and constants for working with the Celestia ecosystem
Documentation
//! Types related to namespace data
//!
//! Namespace data in Celestia is understood as all the [`Share`]s in a particular
//! namespace inside the [`ExtendedDataSquare`]. It may span over multiple rows.
//!
//! [`Share`]: crate::Share
//! [`ExtendedDataSquare`]: crate::eds::ExtendedDataSquare

use bytes::{BufMut, BytesMut};
use celestia_proto::shwap::RowNamespaceData as RawRowNamespaceData;
use serde::{Deserialize, Serialize};

use crate::eds::{EDS_ID_SIZE, EdsId};
use crate::nmt::{NS_SIZE, Namespace};
use crate::row_namespace_data::{RowNamespaceData, RowNamespaceDataId};
use crate::{DataAvailabilityHeader, Error, Result, bail_verification};

/// Number of bytes needed to represent [`RowNamespaceDataId`] in `multihash`.
pub const NAMESPACE_DATA_ID_SIZE: usize = EDS_ID_SIZE + NS_SIZE;

/// Identifies [`Share`]s within a [`Namespace`] located on block's [`ExtendedDataSquare`].
///
/// [`Share`]: crate::Share
/// [`ExtendedDataSquare`]: crate::eds::ExtendedDataSquare
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct NamespaceDataId {
    eds_id: EdsId,
    namespace: Namespace,
}

/// A collection of rows of [`Share`]s from a particular [`Namespace`].
///
/// [`Share`]: crate::Share
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NamespaceData {
    rows: Vec<RowNamespaceData>,
}

impl NamespaceData {
    /// Create namespace data from one or many rows.
    pub fn new(rows: Vec<RowNamespaceData>) -> Self {
        NamespaceData { rows }
    }

    /// All rows containing shares within some namespace.
    pub fn rows(&self) -> &[RowNamespaceData] {
        &self.rows[..]
    }

    /// Extract inner rows namespace data
    pub fn into_inner(self) -> Vec<RowNamespaceData> {
        self.rows
    }

    /// Verify the namespace data against roots from DAH
    pub fn verify(&self, id: NamespaceDataId, dah: &DataAvailabilityHeader) -> Result<()> {
        if self.rows.len() > u16::MAX as usize {
            return Err(Error::NamespaceDataTooLarge);
        }

        let row_idxs = (0..dah.square_width())
            .filter(|&row| dah.row_contains(row, id.namespace).unwrap_or(false))
            .collect::<Vec<_>>();

        if row_idxs.len() != self.rows.len() {
            bail_verification!(
                "expected {} rows, found {} rows",
                row_idxs.len(),
                self.rows.len()
            );
        }

        for (row, row_index) in self.rows.iter().zip(row_idxs.into_iter()) {
            let ns_row_id = RowNamespaceDataId::new(id.namespace, row_index, id.block_height())?;
            row.verify(ns_row_id, dah)?;
        }

        Ok(())
    }

    /// Recover namespace data from it's raw representation.
    pub fn from_raw(id: NamespaceDataId, namespace_data: Vec<RawRowNamespaceData>) -> Result<Self> {
        if namespace_data.len() > u16::MAX as usize {
            return Err(Error::NamespaceDataTooLarge);
        }

        let mut rows = Vec::with_capacity(namespace_data.len());

        for (row_index, raw_ns_data) in namespace_data.into_iter().enumerate() {
            let ns_row_id =
                RowNamespaceDataId::new(id.namespace, row_index as u16, id.block_height())?;
            let ns_data = RowNamespaceData::from_raw(ns_row_id, raw_ns_data)?;
            rows.push(ns_data);
        }

        Ok(NamespaceData::new(rows))
    }
}

impl NamespaceDataId {
    /// Create a new [`NamespaceDataId`] for given block and the [`Namespace`].
    ///
    /// # Errors
    ///
    /// This function will return an error if the block height is invalid.
    pub fn new(namespace: Namespace, block_height: u64) -> Result<Self> {
        Ok(Self {
            eds_id: EdsId::new(block_height)?,
            namespace,
        })
    }

    /// A height of the block which contains the shares.
    pub fn block_height(&self) -> u64 {
        self.eds_id.block_height()
    }

    /// A namespace of the [`Share`]s.
    ///
    /// [`Share`]: crate::Share
    pub fn namespace(&self) -> Namespace {
        self.namespace
    }

    /// Encode row namespace data id into the byte representation.
    pub fn encode(&self, bytes: &mut BytesMut) {
        bytes.reserve(NAMESPACE_DATA_ID_SIZE);
        self.eds_id.encode(bytes);
        bytes.put(self.namespace.as_bytes());
    }

    /// Decode namespace data id from the byte representation.
    pub fn decode(buffer: &[u8]) -> Result<Self> {
        if buffer.len() != NAMESPACE_DATA_ID_SIZE {
            return Err(Error::InvalidLength(buffer.len(), NAMESPACE_DATA_ID_SIZE));
        }

        let (eds_bytes, ns_bytes) = buffer.split_at(EDS_ID_SIZE);
        let eds_id = EdsId::decode(eds_bytes)?;
        let namespace = Namespace::from_raw(ns_bytes)?;

        Ok(Self { eds_id, namespace })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn decode_namespaced_shares() {
        let get_shares_by_namespace_response = r#"[
          {
            "shares": [
              "AAAAAAAAAAAAAAAAAAAAAAAAAAAADCBNOWAP3dMBAAAAG/HyDKgAfpEKO/iy5h2g8mvKB+94cXpupUFl9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
            ],
            "proof": {
              "start": 1,
              "end": 2,
              "nodes": [
                "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFmTiyJVvgoyHdw7JGii/wyMfMbSdN3Nbi6Uj0Lcprk+",
                "/////////////////////////////////////////////////////////////////////////////0WE8jz9lbFjpXWj9v7/QgdAxYEqy4ew9TMdqil/UFZm"
              ],
              "leaf_hash": null,
              "is_max_namespace_ignored": true
            }
          }
        ]"#;

        let ns_shares: NamespaceData =
            serde_json::from_str(get_shares_by_namespace_response).unwrap();

        assert_eq!(ns_shares.rows()[0].shares.len(), 1);
        assert!(!ns_shares.rows()[0].proof.is_of_absence());
    }
}